kcode_k1_chat_codex_codec/
lib.rs1#![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}