1use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11use utoipa::ToSchema;
12
13#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, ToSchema, PartialEq, Eq)]
18pub enum CogneeModelDTO {
19 #[default]
20 #[serde(rename = "cognee-v1")]
21 CogneeV1,
22}
23
24#[derive(Debug, Deserialize, ToSchema)]
29#[serde(rename_all = "camelCase")]
30pub struct ResponseRequestDTO {
31 #[serde(default)]
33 pub model: CogneeModelDTO,
34 pub input: String,
36 pub tools: Option<Vec<ToolFunctionDTO>>,
38 #[serde(
42 default = "ResponseRequestDTO::default_tool_choice",
43 alias = "tool_choice"
44 )]
45 pub tool_choice: Value,
46 pub user: Option<String>,
48 #[serde(default = "ResponseRequestDTO::default_temperature")]
50 pub temperature: f32,
51 #[serde(alias = "max_completion_tokens")]
53 pub max_completion_tokens: Option<u32>,
54}
55
56impl ResponseRequestDTO {
57 fn default_tool_choice() -> Value {
58 Value::String("auto".into())
59 }
60 fn default_temperature() -> f32 {
61 1.0
62 }
63}
64
65#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
67pub struct ToolFunctionDTO {
68 #[serde(default = "ToolFunctionDTO::default_kind", rename = "type")]
70 pub kind: String,
71 pub function: FunctionDTO,
72}
73impl ToolFunctionDTO {
74 fn default_kind() -> String {
75 "function".into()
76 }
77}
78
79#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
81pub struct FunctionDTO {
82 pub name: String,
83 pub description: String,
84 pub parameters: FunctionParametersDTO,
85}
86
87#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
89pub struct FunctionParametersDTO {
90 #[serde(default = "FunctionParametersDTO::default_type", rename = "type")]
92 pub kind: String,
93 pub properties: HashMap<String, Value>,
94 pub required: Option<Vec<String>>,
95}
96impl FunctionParametersDTO {
97 fn default_type() -> String {
98 "object".into()
99 }
100}
101
102#[derive(Debug, Serialize, ToSchema)]
109#[serde(rename_all = "camelCase")]
110pub struct ResponseBodyDTO {
111 pub id: String,
113 pub created: i64,
115 pub model: String,
117 pub object: String,
119 pub status: String,
121 pub tool_calls: Vec<ResponseToolCallDTO>,
123 pub usage: Option<ChatUsageDTO>,
125 pub metadata: Option<HashMap<String, Value>>,
127}
128
129#[derive(Debug, Serialize, ToSchema)]
131pub struct ResponseToolCallDTO {
132 pub id: String,
133 #[serde(rename = "type")]
135 pub kind: String,
136 pub function: FunctionCallDTO,
137 pub output: Option<ToolCallOutputDTO>,
138}
139
140#[derive(Debug, Serialize, ToSchema)]
142pub struct FunctionCallDTO {
143 pub name: String,
144 pub arguments: String,
146}
147
148#[derive(Debug, Serialize, ToSchema)]
150pub struct ToolCallOutputDTO {
151 pub status: String,
153 pub data: Option<HashMap<String, Value>>,
154}
155
156#[derive(Debug, Serialize, Deserialize, ToSchema, Default)]
160pub struct ChatUsageDTO {
161 pub prompt_tokens: u32,
162 pub completion_tokens: u32,
163 pub total_tokens: u32,
164}
165
166#[cfg(test)]
169#[allow(
170 clippy::unwrap_used,
171 clippy::expect_used,
172 reason = "test code — panics are acceptable failures"
173)]
174mod tests {
175 use super::*;
176 use serde_json::json;
177
178 #[test]
179 fn round_trip_response_request_dto_snake_case_via_alias() {
180 let input = json!({
181 "model": "cognee-v1",
182 "input": "What is the meaning of life?",
183 "tools": null,
184 "tool_choice": "auto",
185 "temperature": 1.0
186 });
187
188 let dto: ResponseRequestDTO =
189 serde_json::from_value(input).expect("deserialize ResponseRequestDTO");
190 assert_eq!(dto.input, "What is the meaning of life?");
191 assert_eq!(dto.model, CogneeModelDTO::CogneeV1);
192 assert_eq!(dto.temperature, 1.0);
193 }
194
195 #[test]
196 fn round_trip_response_request_dto_camelcase() {
197 let input = json!({
198 "model": "cognee-v1",
199 "input": "x",
200 "toolChoice": "auto",
201 "maxCompletionTokens": 64
202 });
203 let dto: ResponseRequestDTO = serde_json::from_value(input).expect("deserialize camelCase");
204 assert_eq!(dto.input, "x");
205 assert_eq!(dto.max_completion_tokens, Some(64));
206 }
207
208 #[test]
209 fn tool_choice_accepts_object_variant() {
210 let input = json!({
211 "input": "hello",
212 "tool_choice": {"type": "function", "function": {"name": "search"}}
213 });
214 let dto: ResponseRequestDTO =
215 serde_json::from_value(input).expect("deserialize with object tool_choice");
216 assert!(dto.tool_choice.is_object());
217 }
218
219 #[test]
220 fn response_body_dto_serializes_camelcase_only() {
221 let dto = ResponseBodyDTO {
222 id: "resp_1".into(),
223 created: 0,
224 model: "cognee-v1".into(),
225 object: "response".into(),
226 status: "completed".into(),
227 tool_calls: vec![],
228 usage: None,
229 metadata: None,
230 };
231 let s = serde_json::to_string(&dto).expect("serialize");
232 assert!(s.contains("\"toolCalls\""), "missing toolCalls: {s}");
233 assert!(
234 !s.contains("\"tool_calls\""),
235 "snake_case tool_calls leaked: {s}"
236 );
237 }
238}