Skip to main content

kcode_k1_chat_codex_codec/
lib.rs

1#![forbid(unsafe_code)]
2
3use kcode_k1_chat_chatend::{ChatBox, ProviderCall};
4use serde_json::Value;
5
6const KTOOL_VERSION: &str = "1.0.0";
7
8#[derive(Clone, Debug, PartialEq)]
9pub enum BoxValue {
10    History(String),
11    Call(Result<ProviderCall, String>),
12}
13
14#[derive(Default)]
15pub struct Codec;
16
17impl kcode_k1_codex_adapter::BoxCodec for Codec {
18    type Box = BoxValue;
19
20    fn tool_call_box(&mut self, call: &kcode_k1_codex_adapter::ToolCall) -> Self::Box {
21        if call.name != "call_ktool" {
22            return BoxValue::Call(Err("tool name must be call_ktool".into()));
23        }
24        let Value::String(wrapper) = &call.arguments else {
25            return BoxValue::Call(Err("tool arguments must be a JSON string".into()));
26        };
27        let wrapper: Value = match serde_json::from_str(wrapper) {
28            Ok(wrapper) => wrapper,
29            Err(_) => return BoxValue::Call(Err("tool arguments must be valid JSON".into())),
30        };
31        let Value::Object(values) = wrapper else {
32            return BoxValue::Call(Err("tool arguments must be an object".into()));
33        };
34        if values.len() != 2 || !values.contains_key("name") || !values.contains_key("arguments") {
35            return BoxValue::Call(Err("tool arguments must have name and arguments".into()));
36        }
37        let Some(Value::String(tool)) = values.get("name") else {
38            return BoxValue::Call(Err("tool arguments name must be a string".into()));
39        };
40        let Some(Value::Object(arguments)) = values.get("arguments") else {
41            return BoxValue::Call(Err("tool arguments must be an object".into()));
42        };
43        if arguments.contains_key("tool") {
44            return BoxValue::Call(Err("tool arguments may not contain reserved tool".into()));
45        }
46        BoxValue::Call(Ok(ProviderCall {
47            tool: tool.clone(),
48            tool_version: KTOOL_VERSION.into(),
49            arguments: Value::Object(arguments.clone()),
50        }))
51    }
52
53    fn box_text<'a>(&self, box_: &'a Self::Box) -> &'a str {
54        match box_ {
55            BoxValue::History(text) => text,
56            BoxValue::Call(_) => "",
57        }
58    }
59}
60
61pub fn project(box_: &ChatBox) -> BoxValue {
62    BoxValue::History(format!(
63        "[Box {} | {}]\n{}",
64        box_.id().get(),
65        box_.box_type(),
66        box_.contents()
67    ))
68}