codex_convert_proxy/providers/
glm.rs1use crate::providers::trait_::Provider;
4use crate::types::chat_api::{ChatRequest, ChatResponse, ChatStreamChunk};
5
6pub struct GLMProvider;
13
14impl Default for GLMProvider {
15 fn default() -> Self {
16 Self
17 }
18}
19
20impl GLMProvider {
21 pub fn new() -> Self {
22 Self
23 }
24}
25
26impl Provider for GLMProvider {
27 fn name(&self) -> &'static str {
28 "glm"
29 }
30
31 fn chat_completions_path(&self) -> String {
32 "/chat/completions".to_string()
34 }
35
36 fn transform_request(&self, request: &mut ChatRequest) {
37 request.tools = None;
39 request.tool_choice = None;
40
41 for message in &mut request.messages {
43 if message.role == crate::types::chat_api::MessageRole::Developer {
45 message.role = crate::types::chat_api::MessageRole::User;
46 }
47 let text = message.content.as_text();
48 message.content = crate::types::chat_api::Content::String(text);
49 }
50 }
51
52 fn transform_response(&self, response: &mut ChatResponse) {
53 for choice in &mut response.choices {
55 let text = choice.message.content.as_text();
56 choice.message.content = crate::types::chat_api::Content::String(text);
57 }
58 }
59
60 fn transform_stream_chunk(&self, chunk: &mut ChatStreamChunk) {
61 for choice in &mut chunk.choices {
63 if let Some(delta) = &mut choice.delta
64 && let Some(content) = &delta.content {
65 let text = content.as_text();
66 if !text.is_empty() {
67 delta.content = Some(crate::types::chat_api::Content::String(text));
68 }
69 }
70 }
71 }
72
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::types::chat_api::{ChatMessage, Content, MessageRole};
79
80 #[test]
81 fn test_glm_removes_tools() {
82 let mut request = ChatRequest {
83 model: "glm-4".to_string(),
84 messages: vec![ChatMessage {
85 role: MessageRole::User,
86 content: Content::String("Hello".to_string()),
87 name: None,
88 annotations: None,
89 tool_calls: None,
90 tool_call_id: None,
91 function_call: None,
92 refusal: None,
93 }],
94 tools: Some(vec![]),
95 tool_choice: None,
96 stream: Some(false),
97 temperature: None,
98 max_tokens: None,
99 top_p: None,
100 user: None,
101 stream_options: None,
102 frequency_penalty: None,
103 presence_penalty: None,
104 logit_bias: None,
105 logprobs: None,
106 top_logprobs: None,
107 n: None,
108 stop: None,
109 response_format: None,
110 reasoning_effort: None,
111 parallel_tool_calls: None,
112 seed: None,
113 service_tier: None,
114 web_search_options: None,
115 modalities: None,
116 prediction: None,
117 audio: None,
118 };
119
120 let provider = GLMProvider;
121 provider.transform_request(&mut request);
122
123 assert!(request.tools.is_none());
124 assert!(request.tool_choice.is_none());
125 }
126
127 #[test]
128 fn test_glm_flattens_content() {
129 let mut request = ChatRequest {
130 model: "glm-4".to_string(),
131 messages: vec![ChatMessage {
132 role: MessageRole::User,
133 content: Content::Array(vec![crate::types::chat_api::ContentBlock {
134 block_type: "text".to_string(),
135 text: Some("Hello".to_string()),
136 image_url: None,
137 input_audio: None,
138 file: None,
139 refusal: None,
140 }]),
141 name: None,
142 annotations: None,
143 tool_calls: None,
144 tool_call_id: None,
145 function_call: None,
146 refusal: None,
147 }],
148 tools: None,
149 tool_choice: None,
150 stream: Some(false),
151 temperature: None,
152 max_tokens: None,
153 top_p: None,
154 user: None,
155 stream_options: None,
156 frequency_penalty: None,
157 presence_penalty: None,
158 logit_bias: None,
159 logprobs: None,
160 top_logprobs: None,
161 n: None,
162 stop: None,
163 response_format: None,
164 reasoning_effort: None,
165 parallel_tool_calls: None,
166 seed: None,
167 service_tier: None,
168 web_search_options: None,
169 modalities: None,
170 prediction: None,
171 audio: None,
172 };
173
174 let provider = GLMProvider;
175 provider.transform_request(&mut request);
176
177 let msg = request.messages.first().unwrap();
178 assert!(matches!(msg.content, Content::String(_)));
179 assert_eq!(msg.content.as_text(), "Hello");
180 }
181}