Skip to main content

kcode_k1_chat_codex_codec/
lib.rs

1#![forbid(unsafe_code)]
2
3use kcode_k1_chat_chatend::{AGENT_RESPONSE_TYPE, BoxId, ChatBox};
4
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct Call {
7    pub name: String,
8    pub arguments: String,
9}
10
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub enum BoxValue {
13    History(String),
14    Call(Result<Call, String>),
15    AgentMessage(Result<String, String>),
16}
17
18#[derive(Default)]
19pub struct Codec;
20
21impl kcode_k1_codex_adapter::BoxCodec for Codec {
22    type Box = BoxValue;
23
24    fn tool_call_box(&mut self, call: &kcode_k1_codex_adapter::ToolCall) -> Self::Box {
25        match call.name.as_str() {
26            "call_ktool" => {
27                let serde_json::Value::Object(values) = &call.arguments else {
28                    return BoxValue::Call(Err("tool arguments must be an object".into()));
29                };
30                if values.len() != 2
31                    || !values.contains_key("name")
32                    || !values.contains_key("arguments")
33                {
34                    return BoxValue::Call(Err(
35                        "tool arguments must have name and arguments".into()
36                    ));
37                }
38                let Some(serde_json::Value::String(name)) = values.get("name") else {
39                    return BoxValue::Call(Err("tool arguments name must be a string".into()));
40                };
41                let Some(arguments) = values.get("arguments") else {
42                    return BoxValue::Call(Err("tool arguments require arguments".into()));
43                };
44                let arguments = match serde_json::to_string(arguments) {
45                    Ok(arguments) => arguments,
46                    Err(_) => {
47                        return BoxValue::Call(Err("tool arguments cannot be encoded".into()));
48                    }
49                };
50                BoxValue::Call(Ok(Call {
51                    name: name.clone(),
52                    arguments,
53                }))
54            }
55            "agent_message" => {
56                let serde_json::Value::Object(values) = &call.arguments else {
57                    return BoxValue::AgentMessage(Err(
58                        "agent_message arguments must be an object".into(),
59                    ));
60                };
61                if values.len() != 1 || !values.contains_key("message") {
62                    return BoxValue::AgentMessage(Err(
63                        "agent_message arguments must have exactly one message field".into(),
64                    ));
65                }
66                let Some(serde_json::Value::String(message)) = values.get("message") else {
67                    return BoxValue::AgentMessage(Err(
68                        "agent_message message must be a string".into()
69                    ));
70                };
71                if message.is_empty() {
72                    return BoxValue::AgentMessage(Err(
73                        "agent_message message must not be empty".into()
74                    ));
75                }
76                BoxValue::AgentMessage(Ok(message.clone()))
77            }
78            _ => BoxValue::Call(Err("tool name must be call_ktool or agent_message".into())),
79        }
80    }
81
82    fn box_text<'a>(&self, box_: &'a Self::Box) -> &'a str {
83        match box_ {
84            BoxValue::History(text) => text,
85            BoxValue::Call(_) | BoxValue::AgentMessage(_) => "",
86        }
87    }
88}
89
90pub fn project(box_: &ChatBox) -> BoxValue {
91    BoxValue::History(format!(
92        "[Box {} | {}]\n{}",
93        box_.id().get(),
94        box_.box_type(),
95        box_.contents()
96    ))
97}
98
99pub fn open_agent_response(id: BoxId) -> BoxValue {
100    BoxValue::History(format!("[Box {} | {}]", id.get(), AGENT_RESPONSE_TYPE))
101}