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