Skip to main content

kcode_k1_chat_codex_codec/
lib.rs

1use kcode_k1_chat_chatend::{BoxContent, ChatBox};
2
3#[derive(Clone, Debug, Eq, PartialEq)]
4pub struct Call {
5    pub name: String,
6    pub arguments: String,
7}
8
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub enum BoxValue {
11    History(String),
12    Call(Result<Call, String>),
13}
14
15#[derive(Default)]
16pub struct Codec;
17
18impl kcode_k1_codex_adapter::BoxCodec for Codec {
19    type Box = BoxValue;
20
21    fn tool_call_box(&mut self, call: &kcode_k1_codex_adapter::ToolCall) -> Self::Box {
22        if call.name != "call_ktool" {
23            return BoxValue::Call(Err("tool name must be call_ktool".into()));
24        }
25        let serde_json::Value::Object(values) = &call.arguments else {
26            return BoxValue::Call(Err("tool arguments must be an object".into()));
27        };
28        if values.len() != 2 || !values.contains_key("name") || !values.contains_key("arguments") {
29            return BoxValue::Call(Err("tool arguments must have name and arguments".into()));
30        }
31        let Some(serde_json::Value::String(name)) = values.get("name") else {
32            return BoxValue::Call(Err("tool arguments name must be a string".into()));
33        };
34        let Some(arguments) = values.get("arguments") else {
35            return BoxValue::Call(Err("tool arguments require arguments".into()));
36        };
37        let arguments = match serde_json::to_string(arguments) {
38            Ok(arguments) => arguments,
39            Err(_) => return BoxValue::Call(Err("tool arguments cannot be encoded".into())),
40        };
41        BoxValue::Call(Ok(Call {
42            name: name.clone(),
43            arguments,
44        }))
45    }
46
47    fn box_text<'a>(&self, box_: &'a Self::Box) -> &'a str {
48        match box_ {
49            BoxValue::History(text) => text,
50            BoxValue::Call(_) => "",
51        }
52    }
53}
54
55pub fn project(box_: &ChatBox) -> BoxValue {
56    let box_id = box_.id().get();
57    let text = match box_.content() {
58        BoxContent::System(text) => format!(
59            "{{\"box_id\":{box_id},\"kind\":\"system\",\"text\":{}}}",
60            json(text)
61        ),
62        BoxContent::User(text) => format!(
63            "{{\"box_id\":{box_id},\"kind\":\"user\",\"text\":{}}}",
64            json(text)
65        ),
66        BoxContent::Kennedy { text, complete } => format!(
67            "{{\"box_id\":{box_id},\"kind\":\"kennedy\",\"text\":{},\"complete\":{complete}}}",
68            json(text)
69        ),
70        BoxContent::Attachment => format!("{{\"box_id\":{box_id},\"kind\":\"attachment\"}}"),
71        BoxContent::KtoolCall {
72            action_id,
73            name,
74            arguments,
75        } => format!(
76            "{{\"box_id\":{box_id},\"kind\":\"ktool_call\",\"action_id\":{},\"name\":{},\"arguments\":{}}}",
77            action(action_id),
78            json(name),
79            json(arguments)
80        ),
81        BoxContent::KtoolReturn {
82            action_id,
83            originating_call,
84            result,
85        } => format!(
86            "{{\"box_id\":{box_id},\"kind\":\"ktool_return\",\"action_id\":{},\"originating_call_box_id\":{},\"result\":{}}}",
87            action(action_id),
88            originating_call.get(),
89            result_json(result)
90        ),
91    };
92    BoxValue::History(text)
93}
94
95fn json(text: &str) -> String {
96    serde_json::to_string(text).expect("strings serialize")
97}
98
99fn action(action_id: &kcode_k1_chat_chatend::ActionId) -> String {
100    let session = action_id
101        .session()
102        .iter()
103        .map(|byte| format!("{byte:02x}"))
104        .collect::<String>();
105    format!(
106        "{{\"session\":{session:?},\"sequence\":{}}}",
107        action_id.sequence()
108    )
109}
110
111fn result_json(result: &Result<String, String>) -> String {
112    match result {
113        Ok(value) => format!("{{\"status\":\"ok\",\"value\":{}}}", json(value)),
114        Err(value) => format!("{{\"status\":\"error\",\"value\":{}}}", json(value)),
115    }
116}