#![forbid(unsafe_code)]
use kcode_k1_chat_boxes::ToolCallId;
pub use kcode_k1_web_code_ktool_protocol::{
Language, WEB_CODE_CHECK_METADATA_TYPE, WEB_CODE_CREATE_METADATA_TYPE,
WEB_CODE_DOCS_METADATA_TYPE, WEB_CODE_OPEN_METADATA_TYPE, WEB_CODE_OVERWRITE_METADATA_TYPE,
WEB_CODE_PUBLISH_METADATA_TYPE,
};
use serde::Serialize;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WebCodeOutput {
pub tool_call_id: ToolCallId,
pub messages: Vec<String>,
pub result: Result<String, String>,
pub metadata_type: String,
pub metadata_contents: String,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PublishStatus {
Published,
Idempotent,
Conflict,
}
impl PublishStatus {
pub const fn as_str(self) -> &'static str {
match self {
Self::Published => "published",
Self::Idempotent => "idempotent",
Self::Conflict => "conflict",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DocumentPart {
Documentation,
Code,
}
impl DocumentPart {
pub const fn as_str(self) -> &'static str {
match self {
Self::Documentation => "documentation",
Self::Code => "code",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Operation {
Create,
Docs,
Open,
Overwrite,
Check,
Publish,
}
impl Operation {
pub const fn metadata_type(self) -> &'static str {
match self {
Self::Create => WEB_CODE_CREATE_METADATA_TYPE,
Self::Docs => WEB_CODE_DOCS_METADATA_TYPE,
Self::Open => WEB_CODE_OPEN_METADATA_TYPE,
Self::Overwrite => WEB_CODE_OVERWRITE_METADATA_TYPE,
Self::Check => WEB_CODE_CHECK_METADATA_TYPE,
Self::Publish => WEB_CODE_PUBLISH_METADATA_TYPE,
}
}
}
#[derive(Serialize)]
struct DocsMetadata<'a> {
status: &'static str,
name: &'a str,
version: &'a str,
}
#[derive(Serialize)]
struct SourceMetadata<'a> {
status: &'static str,
name: &'a str,
version: &'a str,
language: &'static str,
documentation_message_index: u64,
code_message_start: u64,
code_message_count: u64,
}
#[derive(Serialize)]
struct OverwriteMetadata<'a> {
status: &'static str,
box_id: u64,
part: &'static str,
version: &'a str,
}
#[derive(Serialize)]
struct CheckMetadata<'a> {
status: &'static str,
fresh: bool,
diagnostics: Option<&'a str>,
}
#[derive(Serialize)]
struct PublishMetadata<'a> {
status: &'static str,
version: &'a str,
source_object: &'a str,
winning: &'a str,
submitted: Option<&'a str>,
check: &'static str,
}
#[derive(Serialize)]
struct ErrorMetadata<'a> {
status: &'static str,
code: &'a str,
source_object: Option<&'a str>,
}
pub fn docs(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
name: &str,
version: &str,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
WEB_CODE_DOCS_METADATA_TYPE,
&DocsMetadata {
status: "found",
name,
version,
},
)
}
#[allow(clippy::too_many_arguments)]
pub fn create(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
name: &str,
version: &str,
language: Language,
code_message_count: u64,
) -> WebCodeOutput {
source(
tool_call_id,
messages,
result,
WEB_CODE_CREATE_METADATA_TYPE,
"created",
name,
version,
language,
code_message_count,
)
}
#[allow(clippy::too_many_arguments)]
pub fn open(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
name: &str,
version: &str,
language: Language,
code_message_count: u64,
) -> WebCodeOutput {
source(
tool_call_id,
messages,
result,
WEB_CODE_OPEN_METADATA_TYPE,
"opened",
name,
version,
language,
code_message_count,
)
}
#[allow(clippy::too_many_arguments)]
fn source(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
metadata_type: &str,
status: &'static str,
name: &str,
version: &str,
language: Language,
code_message_count: u64,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
metadata_type,
&SourceMetadata {
status,
name,
version,
language: language.as_str(),
documentation_message_index: 1,
code_message_start: 2,
code_message_count,
},
)
}
pub fn overwrite(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
box_id: u64,
part: DocumentPart,
version: &str,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
WEB_CODE_OVERWRITE_METADATA_TYPE,
&OverwriteMetadata {
status: "overwritten",
box_id,
part: part.as_str(),
version,
},
)
}
pub fn check_success(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
WEB_CODE_CHECK_METADATA_TYPE,
&CheckMetadata {
status: "success",
fresh: true,
diagnostics: None,
},
)
}
pub fn check_failed(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
diagnostics: &str,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
WEB_CODE_CHECK_METADATA_TYPE,
&CheckMetadata {
status: "failed",
fresh: true,
diagnostics: Some(diagnostics),
},
)
}
#[allow(clippy::too_many_arguments)]
pub fn publish(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
version: &str,
source_object: &str,
winning: &str,
submitted: Option<&str>,
status: PublishStatus,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
WEB_CODE_PUBLISH_METADATA_TYPE,
&PublishMetadata {
status: status.as_str(),
version,
source_object,
winning,
submitted,
check: "fresh",
},
)
}
pub fn error(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
operation: Operation,
code: &str,
source_object: Option<&str>,
) -> WebCodeOutput {
build(
tool_call_id,
messages,
result,
operation.metadata_type(),
&ErrorMetadata {
status: "error",
code,
source_object,
},
)
}
fn build(
tool_call_id: ToolCallId,
messages: Vec<String>,
result: Result<String, String>,
metadata_type: &str,
metadata: &impl Serialize,
) -> WebCodeOutput {
WebCodeOutput {
tool_call_id,
messages,
result,
metadata_type: metadata_type.to_owned(),
metadata_contents: serde_json::to_string(metadata)
.expect("fixed WebCode metadata serializes"),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn id() -> ToolCallId {
ToolCallId::new([1; 12], 7)
}
#[test]
fn metadata_is_exact_and_authority_free() {
let value = create(
id(),
vec!["docs".into(), "code".into()],
Ok("Created 010101010101010101010101.".into()),
"alpha",
"010101010101010101010101",
Language::JavaScript,
1,
);
assert_eq!(value.metadata_type, "k1.web-code-create/v1");
assert_eq!(
value.metadata_contents,
r#"{"status":"created","name":"alpha","version":"010101010101010101010101","language":"javascript","documentation_message_index":1,"code_message_start":2,"code_message_count":1}"#
);
assert!(!value.metadata_contents.contains("authority"));
let value = overwrite(
id(),
Vec::new(),
Ok("Overwritten.".into()),
42,
DocumentPart::Code,
"020202020202020202020202",
);
assert_eq!(
value.metadata_contents,
r#"{"status":"overwritten","box_id":42,"part":"code","version":"020202020202020202020202"}"#
);
}
#[test]
fn check_and_publish_are_always_fresh() {
let checked = check_failed(id(), Vec::new(), Err("bad".into()), "all diagnostics");
assert_eq!(
checked.metadata_contents,
r#"{"status":"failed","fresh":true,"diagnostics":"all diagnostics"}"#
);
let published = publish(
id(),
Vec::new(),
Ok("Published 1.2.3.".into()),
"1.2.3",
"object",
"winner",
None,
PublishStatus::Published,
);
assert_eq!(
published.metadata_contents,
r#"{"status":"published","version":"1.2.3","source_object":"object","winning":"winner","submitted":null,"check":"fresh"}"#
);
}
}