Skip to main content

gproxy_protocol/transform/models/
supplement.rs

1pub use crate::transform::Converted;
2use crate::transform::TransformError;
3use crate::wire::claude::models as claude_models;
4use serde::{Deserialize, Serialize};
5
6/// Facts needed by targets whose model schema requires an owner or creation
7/// timestamp absent from the source. A source fact always wins; a conflicting
8/// supplied fact is rejected.
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub struct OpenAiModelSupplement {
11    pub owned_by: String,
12    pub created: Option<i64>,
13}
14
15/// Target-specific Claude metadata. The nested capability facts are rebuilt
16/// into fresh wire structs so no supplement or source `rest` map is copied.
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct ClaudeModelSupplement {
19    pub display_name: Option<String>,
20    pub allowed_fallback_models: Vec<String>,
21    pub capabilities: ClaudeCapabilities,
22    pub max_input_tokens: Option<i64>,
23    pub max_tokens: Option<i64>,
24    pub created_at: Option<String>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct ClaudeCapabilities {
29    pub batch: bool,
30    pub citations: bool,
31    pub code_execution: bool,
32    pub context_management: ClaudeContextManagement,
33    pub effort: ClaudeEffort,
34    pub image_input: bool,
35    pub pdf_input: bool,
36    pub structured_outputs: bool,
37    pub thinking: ClaudeThinking,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct ClaudeContextManagement {
42    pub clear_thinking_20251015: bool,
43    pub clear_tool_uses_20250919: bool,
44    pub compact_20260112: bool,
45    pub supported: bool,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49pub struct ClaudeEffort {
50    pub high: bool,
51    pub low: bool,
52    pub max: bool,
53    pub medium: bool,
54    pub supported: bool,
55    pub xhigh: bool,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct ClaudeThinking {
60    pub supported: bool,
61    pub adaptive: bool,
62    pub enabled: bool,
63}
64
65impl ClaudeCapabilities {
66    pub(crate) fn into_wire(self) -> claude_models::ModelCapabilities {
67        let support = |supported| claude_models::CapabilitySupport {
68            supported,
69            rest: Default::default(),
70        };
71        claude_models::ModelCapabilities {
72            batch: support(self.batch),
73            citations: support(self.citations),
74            code_execution: support(self.code_execution),
75            context_management: claude_models::ContextManagementCapability {
76                clear_thinking_20251015: support(self.context_management.clear_thinking_20251015),
77                clear_tool_uses_20250919: support(self.context_management.clear_tool_uses_20250919),
78                compact_20260112: support(self.context_management.compact_20260112),
79                supported: self.context_management.supported,
80                rest: Default::default(),
81            },
82            effort: claude_models::EffortCapability {
83                high: support(self.effort.high),
84                low: support(self.effort.low),
85                max: support(self.effort.max),
86                medium: support(self.effort.medium),
87                supported: self.effort.supported,
88                xhigh: support(self.effort.xhigh),
89                rest: Default::default(),
90            },
91            image_input: support(self.image_input),
92            pdf_input: support(self.pdf_input),
93            structured_outputs: support(self.structured_outputs),
94            thinking: claude_models::ThinkingCapability {
95                supported: self.thinking.supported,
96                types: claude_models::ThinkingTypes {
97                    adaptive: support(self.thinking.adaptive),
98                    enabled: support(self.thinking.enabled),
99                    rest: Default::default(),
100                },
101                rest: Default::default(),
102            },
103            rest: Default::default(),
104        }
105    }
106}
107
108/// Facts for Gemini's required `baseModelId` and `version` fields.
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub struct GeminiModelSupplement {
111    pub base_model_id: String,
112    pub version: String,
113}
114
115/// Pagination facts are target-specific. A cursor is never renamed to a page
116/// token implicitly. `complete` means the caller has aggregated the complete
117/// directory from the first request through its terminal page; it does not
118/// mean merely that the current source page has `has_more = false`.
119#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
120pub struct ListPageFacts {
121    pub complete: bool,
122    pub first_id: Option<String>,
123    pub last_id: Option<String>,
124    pub has_more: Option<bool>,
125    pub next_page_token: Option<String>,
126}
127
128impl ListPageFacts {
129    pub const fn complete() -> Self {
130        Self {
131            complete: true,
132            first_id: None,
133            last_id: None,
134            has_more: Some(false),
135            next_page_token: None,
136        }
137    }
138
139    pub fn explicit(
140        first_id: impl Into<String>,
141        last_id: impl Into<String>,
142        has_more: bool,
143    ) -> Self {
144        Self {
145            complete: false,
146            first_id: Some(first_id.into()),
147            last_id: Some(last_id.into()),
148            has_more: Some(has_more),
149            next_page_token: None,
150        }
151    }
152
153    pub fn with_next_page_token(mut self, token: impl Into<String>) -> Self {
154        self.next_page_token = Some(token.into());
155        self
156    }
157}
158
159pub(crate) fn missing_pagination(detail: &str) -> TransformError {
160    TransformError::new(
161        crate::transform::TransformErrorKind::MissingMetadata,
162        "pagination",
163        detail,
164    )
165}