summaryrefslogtreecommitdiff
path: root/makima/src/llm/tools.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/llm/tools.rs')
-rw-r--r--makima/src/llm/tools.rs206
1 files changed, 203 insertions, 3 deletions
diff --git a/makima/src/llm/tools.rs b/makima/src/llm/tools.rs
index 77fc8c6..649633e 100644
--- a/makima/src/llm/tools.rs
+++ b/makima/src/llm/tools.rs
@@ -73,6 +73,51 @@ pub static AVAILABLE_TOOLS: once_cell::sync::Lazy<Vec<Tool>> =
}),
},
Tool {
+ name: "add_code".to_string(),
+ description: "Add a code block element to the file body".to_string(),
+ parameters: json!({
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "string",
+ "description": "The code content"
+ },
+ "language": {
+ "type": "string",
+ "description": "Optional programming language for syntax highlighting (e.g., 'javascript', 'python', 'rust')"
+ },
+ "position": {
+ "type": "integer",
+ "description": "Optional position to insert at (0-indexed). If not specified, appends to end."
+ }
+ },
+ "required": ["content"]
+ }),
+ },
+ Tool {
+ name: "add_list".to_string(),
+ description: "Add a list element (ordered or unordered) to the file body".to_string(),
+ parameters: json!({
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Array of list item strings"
+ },
+ "ordered": {
+ "type": "boolean",
+ "description": "If true, creates a numbered list; if false (default), creates a bullet list"
+ },
+ "position": {
+ "type": "integer",
+ "description": "Optional position to insert at (0-indexed). If not specified, appends to end."
+ }
+ },
+ "required": ["items"]
+ }),
+ },
+ Tool {
name: "add_chart".to_string(),
description: "Add a chart visualization to the file body. Supports line, bar, pie, and area charts.".to_string(),
parameters: json!({
@@ -122,7 +167,7 @@ pub static AVAILABLE_TOOLS: once_cell::sync::Lazy<Vec<Tool>> =
},
Tool {
name: "update_element".to_string(),
- description: "Update an existing element in the file body. IMPORTANT: You must provide ALL required fields. For heading: type, level (1-6), text. For paragraph: type, text. For chart: type, chartType (line/bar/pie/area), data (array of objects).".to_string(),
+ description: "Update an existing element in the file body. IMPORTANT: You must provide ALL required fields. For heading: type, level (1-6), text. For paragraph: type, text. For code: type, content, language (optional). For list: type, items (array of strings), ordered (boolean). For chart: type, chartType (line/bar/pie/area), data (array of objects).".to_string(),
parameters: json!({
"type": "object",
"properties": {
@@ -132,7 +177,7 @@ pub static AVAILABLE_TOOLS: once_cell::sync::Lazy<Vec<Tool>> =
},
"element_type": {
"type": "string",
- "enum": ["heading", "paragraph", "chart"],
+ "enum": ["heading", "paragraph", "code", "list", "chart"],
"description": "Type of element"
},
"text": {
@@ -143,6 +188,23 @@ pub static AVAILABLE_TOOLS: once_cell::sync::Lazy<Vec<Tool>> =
"type": "integer",
"description": "Heading level 1-6 (required for heading)"
},
+ "content": {
+ "type": "string",
+ "description": "Code content (required for code)"
+ },
+ "language": {
+ "type": "string",
+ "description": "Programming language for syntax highlighting (optional for code)"
+ },
+ "items": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "List items (required for list)"
+ },
+ "ordered": {
+ "type": "boolean",
+ "description": "If true, numbered list; if false, bullet list (for list)"
+ },
"chartType": {
"type": "string",
"enum": ["line", "bar", "pie", "area"],
@@ -418,6 +480,8 @@ pub fn execute_tool_call(
match call.name.as_str() {
"add_heading" => execute_add_heading(call, current_body),
"add_paragraph" => execute_add_paragraph(call, current_body),
+ "add_code" => execute_add_code(call, current_body),
+ "add_list" => execute_add_list(call, current_body),
"add_chart" => execute_add_chart(call, current_body),
"remove_element" => execute_remove_element(call, current_body),
"update_element" => execute_update_element(call, current_body),
@@ -605,6 +669,103 @@ fn execute_add_paragraph(call: &ToolCall, current_body: &[BodyElement]) -> ToolE
}
}
+fn execute_add_code(call: &ToolCall, current_body: &[BodyElement]) -> ToolExecutionResult {
+ let language = call
+ .arguments
+ .get("language")
+ .and_then(|v| v.as_str())
+ .map(|s| s.to_string());
+ let content = call
+ .arguments
+ .get("content")
+ .and_then(|v| v.as_str())
+ .unwrap_or("")
+ .to_string();
+ let position = call.arguments.get("position").and_then(|v| v.as_u64());
+
+ let element = BodyElement::Code {
+ language: language.clone(),
+ content: content.clone(),
+ };
+ let mut new_body = current_body.to_vec();
+
+ if let Some(pos) = position {
+ let pos = pos as usize;
+ if pos <= new_body.len() {
+ new_body.insert(pos, element);
+ } else {
+ new_body.push(element);
+ }
+ } else {
+ new_body.push(element);
+ }
+
+ let lang_str = language.as_deref().unwrap_or("plain");
+ let preview: String = content.chars().take(50).collect();
+
+ ToolExecutionResult {
+ result: ToolResult {
+ success: true,
+ message: format!("Added code block ({}): {}", lang_str, preview),
+ },
+ new_body: Some(new_body),
+ new_summary: None,
+ parsed_data: None,
+ version_request: None,
+ pending_questions: None,
+ }
+}
+
+fn execute_add_list(call: &ToolCall, current_body: &[BodyElement]) -> ToolExecutionResult {
+ let ordered = call
+ .arguments
+ .get("ordered")
+ .and_then(|v| v.as_bool())
+ .unwrap_or(false);
+ let items: Vec<String> = call
+ .arguments
+ .get("items")
+ .and_then(|v| v.as_array())
+ .map(|arr| {
+ arr.iter()
+ .filter_map(|v| v.as_str().map(|s| s.to_string()))
+ .collect()
+ })
+ .unwrap_or_default();
+ let position = call.arguments.get("position").and_then(|v| v.as_u64());
+
+ let element = BodyElement::List {
+ ordered,
+ items: items.clone(),
+ };
+ let mut new_body = current_body.to_vec();
+
+ if let Some(pos) = position {
+ let pos = pos as usize;
+ if pos <= new_body.len() {
+ new_body.insert(pos, element);
+ } else {
+ new_body.push(element);
+ }
+ } else {
+ new_body.push(element);
+ }
+
+ let list_type = if ordered { "ordered" } else { "unordered" };
+
+ ToolExecutionResult {
+ result: ToolResult {
+ success: true,
+ message: format!("Added {} list with {} items", list_type, items.len()),
+ },
+ new_body: Some(new_body),
+ new_summary: None,
+ parsed_data: None,
+ version_request: None,
+ pending_questions: None,
+ }
+}
+
fn execute_add_chart(call: &ToolCall, current_body: &[BodyElement]) -> ToolExecutionResult {
let chart_type_str = call
.arguments
@@ -778,6 +939,19 @@ fn execute_update_element(call: &ToolCall, current_body: &[BodyElement]) -> Tool
let text = call.arguments.get("text").and_then(|v| v.as_str()).unwrap_or("").to_string();
BodyElement::Paragraph { text }
}
+ "code" => {
+ let language = call.arguments.get("language").and_then(|v| v.as_str()).map(|s| s.to_string());
+ let content = call.arguments.get("content").and_then(|v| v.as_str()).unwrap_or("").to_string();
+ BodyElement::Code { language, content }
+ }
+ "list" => {
+ let ordered = call.arguments.get("ordered").and_then(|v| v.as_bool()).unwrap_or(false);
+ let items = call.arguments.get("items")
+ .and_then(|v| v.as_array())
+ .map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())
+ .unwrap_or_default();
+ BodyElement::List { ordered, items }
+ }
"chart" => {
let chart_type_str = call.arguments.get("chartType").and_then(|v| v.as_str()).unwrap_or("bar");
let chart_type = match chart_type_str {
@@ -796,7 +970,7 @@ fn execute_update_element(call: &ToolCall, current_body: &[BodyElement]) -> Tool
return ToolExecutionResult {
result: ToolResult {
success: false,
- message: format!("Unknown element_type: {}. Must be heading, paragraph, or chart.", element_type),
+ message: format!("Unknown element_type: {}. Must be heading, paragraph, code, list, or chart.", element_type),
},
new_body: None,
new_summary: None,
@@ -1149,6 +1323,18 @@ fn execute_view_body(current_body: &[BodyElement]) -> ToolExecutionResult {
"type": "paragraph",
"text": text
}),
+ BodyElement::Code { language, content } => json!({
+ "index": i,
+ "type": "code",
+ "language": language,
+ "content": content
+ }),
+ BodyElement::List { ordered, items } => json!({
+ "index": i,
+ "type": "list",
+ "ordered": ordered,
+ "items": items
+ }),
BodyElement::Chart { chart_type, title, data, config } => json!({
"index": i,
"type": "chart",
@@ -1226,6 +1412,18 @@ fn execute_read_element(call: &ToolCall, current_body: &[BodyElement]) -> ToolEx
"type": "paragraph",
"text": text
}),
+ BodyElement::Code { language, content } => json!({
+ "index": index,
+ "type": "code",
+ "language": language,
+ "content": content
+ }),
+ BodyElement::List { ordered, items } => json!({
+ "index": index,
+ "type": "list",
+ "ordered": ordered,
+ "items": items
+ }),
BodyElement::Chart { chart_type, title, data, config } => json!({
"index": index,
"type": "chart",
@@ -1246,6 +1444,8 @@ fn execute_read_element(call: &ToolCall, current_body: &[BodyElement]) -> ToolEx
let type_str = match element {
BodyElement::Heading { .. } => "heading",
BodyElement::Paragraph { .. } => "paragraph",
+ BodyElement::Code { .. } => "code",
+ BodyElement::List { .. } => "list",
BodyElement::Chart { .. } => "chart",
BodyElement::Image { .. } => "image",
};