kcode_k1_chat_thread_web_code_open_targets/
lib.rs1#![forbid(unsafe_code)]
2
3use kcode_k1_chat_boxes::{BoxId, ChatBox, ToolCallId};
4use kcode_k1_chat_web_code_output::{DocumentPart, WebCodeOutput, create, open};
5use kcode_k1_web_code_document::CodeDocument;
6use std::fmt::{Display, Formatter};
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct SourceLayout {
10 originating_call: BoxId,
11 tool_call_id: ToolCallId,
12 code_message_count: u64,
13}
14
15impl SourceLayout {
16 pub const fn originating_call(self) -> BoxId {
17 self.originating_call
18 }
19 pub const fn tool_call_id(self) -> ToolCallId {
20 self.tool_call_id
21 }
22 pub const fn code_message_count(self) -> u64 {
23 self.code_message_count
24 }
25}
26
27pub type OpenLayout = SourceLayout;
28
29#[derive(Clone, Debug, Eq, PartialEq)]
30pub struct SourcePresentation {
31 pub output: WebCodeOutput,
32 pub layout: SourceLayout,
33}
34
35pub type OpenPresentation = SourcePresentation;
36
37pub fn present_create(
38 originating_call: BoxId,
39 tool_call_id: ToolCallId,
40 document: &CodeDocument,
41 version: &str,
42) -> SourcePresentation {
43 present(originating_call, tool_call_id, document, version, true)
44}
45
46pub fn present_open(
47 originating_call: BoxId,
48 tool_call_id: ToolCallId,
49 document: &CodeDocument,
50 version: &str,
51) -> SourcePresentation {
52 present(originating_call, tool_call_id, document, version, false)
53}
54
55fn present(
56 originating_call: BoxId,
57 tool_call_id: ToolCallId,
58 document: &CodeDocument,
59 version: &str,
60 creating: bool,
61) -> SourcePresentation {
62 let ranges = document.chunk_ranges();
63 let code_message_count = u64::try_from(ranges.len()).expect("code chunk count fits u64");
64 let mut messages = Vec::with_capacity(ranges.len() + 1);
65 messages.push(text(document.documentation()));
66 messages.extend(
67 ranges
68 .iter()
69 .map(|range| text(&document.code()[range.clone()])),
70 );
71 let name = document.family().logical_name();
72 let result = if creating {
73 Ok(format!("Created {version}."))
74 } else {
75 Ok(format!("Opened {version}."))
76 };
77 let output = if creating {
78 create(
79 tool_call_id,
80 messages,
81 result,
82 name,
83 version,
84 document.language(),
85 code_message_count,
86 )
87 } else {
88 open(
89 tool_call_id,
90 messages,
91 result,
92 name,
93 version,
94 document.language(),
95 code_message_count,
96 )
97 };
98 SourcePresentation {
99 output,
100 layout: SourceLayout {
101 originating_call,
102 tool_call_id,
103 code_message_count,
104 },
105 }
106}
107
108fn text(bytes: &[u8]) -> String {
109 std::str::from_utf8(bytes)
110 .expect("CodeDocument retains UTF-8")
111 .to_owned()
112}
113
114#[derive(Clone, Copy, Debug, Eq, PartialEq)]
115pub enum TargetRejection {
116 HistoryAbsent,
117 HistoryDuplicate,
118 MalformedPayload,
119 DifferentOpen,
120 MessageOutOfLayout,
121 NotSourceMessage,
122}
123
124impl TargetRejection {
125 pub const fn code(self) -> &'static str {
126 match self {
127 Self::HistoryAbsent => "history_absent",
128 Self::HistoryDuplicate => "history_duplicate",
129 Self::MalformedPayload => "malformed_payload",
130 Self::DifferentOpen => "different_open",
131 Self::MessageOutOfLayout => "message_out_of_layout",
132 Self::NotSourceMessage => "not_source_message",
133 }
134 }
135}
136
137impl Display for TargetRejection {
138 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
139 formatter.write_str(match self {
140 Self::HistoryAbsent => "target box is absent from history",
141 Self::HistoryDuplicate => "target box ID occurs more than once in history",
142 Self::MalformedPayload => "target has malformed tool metadata",
143 Self::DifferentOpen => "target belongs to a different source Open",
144 Self::MessageOutOfLayout => "target message index is outside the source layout",
145 Self::NotSourceMessage => "target is not an editable source message",
146 })
147 }
148}
149impl std::error::Error for TargetRejection {}
150
151pub fn classify_original_target(
152 history: &[ChatBox],
153 target_id: BoxId,
154 layout: SourceLayout,
155) -> Result<DocumentPart, TargetRejection> {
156 let mut matching = history.iter().filter(|value| value.id() == target_id);
157 let target = matching.next().ok_or(TargetRejection::HistoryAbsent)?;
158 if matching.next().is_some() {
159 return Err(TargetRejection::HistoryDuplicate);
160 }
161 let message = target
162 .tool_message_metadata()
163 .map_err(|_| TargetRejection::MalformedPayload)?
164 .ok_or(TargetRejection::NotSourceMessage)?;
165 if message.tool_call_id != layout.tool_call_id
166 || message.originating_call != layout.originating_call
167 {
168 return Err(TargetRejection::DifferentOpen);
169 }
170 match message.message_index {
171 1 => Ok(DocumentPart::Documentation),
172 index if (2..=layout.code_message_count + 1).contains(&index) => Ok(DocumentPart::Code),
173 _ => Err(TargetRejection::MessageOutOfLayout),
174 }
175}