gproxy_protocol/protocol/claude/
models.rs1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use super::common::{AnthropicBetaHeaders, ClaudeModel, JsonObject, ModelObjectType};
7
8pub type ListModelsRequestHeaders = AnthropicBetaHeaders;
9pub type RetrieveModelRequestHeaders = AnthropicBetaHeaders;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
12#[non_exhaustive]
13pub struct ListModelsQuery {
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub after_id: Option<String>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub before_id: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub limit: Option<u64>,
20 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
21 pub extra: JsonObject,
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
25#[non_exhaustive]
26pub struct RetrieveModelPath {
27 pub model_id: ClaudeModel,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
31#[non_exhaustive]
32pub struct ListModelsResponse {
33 pub data: Vec<ModelInfo>,
34 pub first_id: String,
35 pub has_more: bool,
36 pub last_id: String,
37 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
38 pub extra: JsonObject,
39}
40
41#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
42#[non_exhaustive]
43pub struct ModelInfo {
44 pub id: ClaudeModel,
45 pub allowed_fallback_models: Option<Vec<ClaudeModel>>,
46 #[serde(rename = "type")]
47 pub type_: ModelObjectType,
48 pub created_at: String,
49 pub display_name: String,
50 pub max_input_tokens: Option<u64>,
51 pub max_tokens: Option<u64>,
52 pub capabilities: Option<ModelCapabilities>,
53 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
54 pub extra: JsonObject,
55}
56
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
58#[non_exhaustive]
59pub struct ModelCapabilities {
60 pub batch: CapabilitySupport,
61 pub citations: CapabilitySupport,
62 pub code_execution: CapabilitySupport,
63 pub context_management: ContextManagementCapability,
64 pub effort: EffortCapability,
65 pub image_input: CapabilitySupport,
66 pub pdf_input: CapabilitySupport,
67 pub structured_outputs: CapabilitySupport,
68 pub thinking: ThinkingCapability,
69 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
70 pub extra: JsonObject,
71}
72
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
74#[non_exhaustive]
75pub struct CapabilitySupport {
76 pub supported: bool,
77 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
78 pub extra: JsonObject,
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
82#[non_exhaustive]
83pub struct ContextManagementCapability {
84 pub supported: bool,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub clear_thinking_20251015: Option<CapabilitySupport>,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub clear_tool_uses_20250919: Option<CapabilitySupport>,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub compact_20260112: Option<CapabilitySupport>,
91 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
92 pub extra: JsonObject,
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
96#[non_exhaustive]
97pub struct EffortCapability {
98 pub supported: bool,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub low: Option<CapabilitySupport>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub medium: Option<CapabilitySupport>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub high: Option<CapabilitySupport>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub xhigh: Option<CapabilitySupport>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub max: Option<CapabilitySupport>,
109 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
110 pub extra: JsonObject,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
114#[non_exhaustive]
115pub struct ThinkingCapability {
116 pub supported: bool,
117 pub types: ThinkingTypes,
118 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
119 pub extra: JsonObject,
120}
121
122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
123#[non_exhaustive]
124pub struct ThinkingTypes {
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub adaptive: Option<CapabilitySupport>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub enabled: Option<CapabilitySupport>,
129 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
130 pub extra: JsonObject,
131}
132
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
134#[non_exhaustive]
135pub struct ModelError {
136 #[serde(rename = "type")]
137 pub type_: String,
138 pub message: String,
139 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
140 pub extra: BTreeMap<String, Value>,
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn beta_model_info_decodes_nullable_metadata() {
149 let model: ModelInfo = serde_json::from_str(
150 r#"{
151 "id": "claude-test",
152 "allowed_fallback_models": null,
153 "type": "model",
154 "created_at": "2026-01-01T00:00:00Z",
155 "display_name": "Claude Test",
156 "max_input_tokens": null,
157 "max_tokens": null,
158 "capabilities": null
159 }"#,
160 )
161 .expect("decode nullable beta model metadata");
162
163 assert!(model.allowed_fallback_models.is_none());
164 assert!(model.max_input_tokens.is_none());
165 assert!(model.max_tokens.is_none());
166 assert!(model.capabilities.is_none());
167 }
168}