use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use utoipa::ToSchema;
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, ToSchema, PartialEq, Eq)]
pub enum CogneeModelDTO {
#[default]
#[serde(rename = "cognee-v1")]
CogneeV1,
}
#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ResponseRequestDTO {
#[serde(default)]
pub model: CogneeModelDTO,
pub input: String,
pub tools: Option<Vec<ToolFunctionDTO>>,
#[serde(
default = "ResponseRequestDTO::default_tool_choice",
alias = "tool_choice"
)]
pub tool_choice: Value,
pub user: Option<String>,
#[serde(default = "ResponseRequestDTO::default_temperature")]
pub temperature: f32,
#[serde(alias = "max_completion_tokens")]
pub max_completion_tokens: Option<u32>,
}
impl ResponseRequestDTO {
fn default_tool_choice() -> Value {
Value::String("auto".into())
}
fn default_temperature() -> f32 {
1.0
}
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct ToolFunctionDTO {
#[serde(default = "ToolFunctionDTO::default_kind", rename = "type")]
pub kind: String,
pub function: FunctionDTO,
}
impl ToolFunctionDTO {
fn default_kind() -> String {
"function".into()
}
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct FunctionDTO {
pub name: String,
pub description: String,
pub parameters: FunctionParametersDTO,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct FunctionParametersDTO {
#[serde(default = "FunctionParametersDTO::default_type", rename = "type")]
pub kind: String,
pub properties: HashMap<String, Value>,
pub required: Option<Vec<String>>,
}
impl FunctionParametersDTO {
fn default_type() -> String {
"object".into()
}
}
#[derive(Debug, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ResponseBodyDTO {
pub id: String,
pub created: i64,
pub model: String,
pub object: String,
pub status: String,
pub tool_calls: Vec<ResponseToolCallDTO>,
pub usage: Option<ChatUsageDTO>,
pub metadata: Option<HashMap<String, Value>>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct ResponseToolCallDTO {
pub id: String,
#[serde(rename = "type")]
pub kind: String,
pub function: FunctionCallDTO,
pub output: Option<ToolCallOutputDTO>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct FunctionCallDTO {
pub name: String,
pub arguments: String,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct ToolCallOutputDTO {
pub status: String,
pub data: Option<HashMap<String, Value>>,
}
#[derive(Debug, Serialize, Deserialize, ToSchema, Default)]
pub struct ChatUsageDTO {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code — panics are acceptable failures"
)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn round_trip_response_request_dto_snake_case_via_alias() {
let input = json!({
"model": "cognee-v1",
"input": "What is the meaning of life?",
"tools": null,
"tool_choice": "auto",
"temperature": 1.0
});
let dto: ResponseRequestDTO =
serde_json::from_value(input).expect("deserialize ResponseRequestDTO");
assert_eq!(dto.input, "What is the meaning of life?");
assert_eq!(dto.model, CogneeModelDTO::CogneeV1);
assert_eq!(dto.temperature, 1.0);
}
#[test]
fn round_trip_response_request_dto_camelcase() {
let input = json!({
"model": "cognee-v1",
"input": "x",
"toolChoice": "auto",
"maxCompletionTokens": 64
});
let dto: ResponseRequestDTO = serde_json::from_value(input).expect("deserialize camelCase");
assert_eq!(dto.input, "x");
assert_eq!(dto.max_completion_tokens, Some(64));
}
#[test]
fn tool_choice_accepts_object_variant() {
let input = json!({
"input": "hello",
"tool_choice": {"type": "function", "function": {"name": "search"}}
});
let dto: ResponseRequestDTO =
serde_json::from_value(input).expect("deserialize with object tool_choice");
assert!(dto.tool_choice.is_object());
}
#[test]
fn response_body_dto_serializes_camelcase_only() {
let dto = ResponseBodyDTO {
id: "resp_1".into(),
created: 0,
model: "cognee-v1".into(),
object: "response".into(),
status: "completed".into(),
tool_calls: vec![],
usage: None,
metadata: None,
};
let s = serde_json::to_string(&dto).expect("serialize");
assert!(s.contains("\"toolCalls\""), "missing toolCalls: {s}");
assert!(
!s.contains("\"tool_calls\""),
"snake_case tool_calls leaked: {s}"
);
}
}