#![forbid(unsafe_code)]
use kcode_k1_chat_chatend::{ChatBox, ProviderCall};
use serde_json::Value;
const KTOOL_VERSION: &str = "1.0.0";
#[derive(Clone, Debug, PartialEq)]
pub enum BoxValue {
History(String),
Call(Result<ProviderCall, String>),
}
#[derive(Default)]
pub struct Codec;
impl kcode_k1_codex_adapter::BoxCodec for Codec {
type Box = BoxValue;
fn tool_call_box(&mut self, call: &kcode_k1_codex_adapter::ToolCall) -> Self::Box {
if call.name != "call_ktool" {
return BoxValue::Call(Err("tool name must be call_ktool".into()));
}
let Value::String(wrapper) = &call.arguments else {
return BoxValue::Call(Err("tool arguments must be a JSON string".into()));
};
let wrapper: Value = match serde_json::from_str(wrapper) {
Ok(wrapper) => wrapper,
Err(_) => return BoxValue::Call(Err("tool arguments must be valid JSON".into())),
};
let Value::Object(values) = wrapper else {
return BoxValue::Call(Err("tool arguments must be an object".into()));
};
if values.len() != 2 || !values.contains_key("name") || !values.contains_key("arguments") {
return BoxValue::Call(Err("tool arguments must have name and arguments".into()));
}
let Some(Value::String(tool)) = values.get("name") else {
return BoxValue::Call(Err("tool arguments name must be a string".into()));
};
let Some(Value::Object(arguments)) = values.get("arguments") else {
return BoxValue::Call(Err("tool arguments must be an object".into()));
};
if arguments.contains_key("tool") {
return BoxValue::Call(Err("tool arguments may not contain reserved tool".into()));
}
BoxValue::Call(Ok(ProviderCall {
tool: tool.clone(),
tool_version: KTOOL_VERSION.into(),
arguments: Value::Object(arguments.clone()),
}))
}
fn box_text<'a>(&self, box_: &'a Self::Box) -> &'a str {
match box_ {
BoxValue::History(text) => text,
BoxValue::Call(_) => "",
}
}
}
pub fn project(box_: &ChatBox) -> BoxValue {
BoxValue::History(format!(
"[Box {} | {}]\n{}",
box_.id().get(),
box_.box_type(),
box_.contents()
))
}