/** * LangChain Tool Definitions with Zod Schemas * * Single source of truth for all diagram AI tools. * Uses Zod for type-safe schema definitions. * Can be exported to Claude or OpenAI format. */ import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; // Position schema (reusable) - from camera/user perspective const positionSchema = z.object({ x: z.number().describe("Left (-) / Right (+) position from camera view"), y: z.number().describe("Down (-) / Up (+) position (0 = floor, 1.5 = eye level)"), z: z.number().describe("Backward (-) / Forward (+) position from camera view") }).optional().describe("3D position from camera perspective. Example: (0, 1.5, 2) = directly in front at eye level"); // Scale schema - can be number or object, values are in METERS const scaleSchema = z.union([ z.number().describe("Uniform size in meters (e.g., 1 = 1 meter cube, 0.5 = 50cm cube)"), z.object({ x: z.number().describe("Width in meters"), y: z.number().describe("Height in meters"), z: z.number().describe("Depth in meters") }).describe("Size as {x: width, y: height, z: depth} in meters. Example: {x: 1, y: 0.1, z: 1} = 1m wide, 10cm tall, 1m deep") ]).optional().describe("Size in METERS. Use {x, y, z} for different width/height/depth."); // Rotation schema - can be number or object const rotationSchema = z.union([ z.number().describe("Y-axis rotation in degrees (e.g., 90 = turn right 90°, -90 = turn left 90°)"), z.object({ x: z.number().describe("Pitch in degrees"), y: z.number().describe("Yaw in degrees"), z: z.number().describe("Roll in degrees") }).describe("Full 3D rotation in degrees as {x, y, z}") ]).optional().describe("Rotation in degrees. Use a number for Y-axis rotation or {x, y, z} for full 3D rotation."); // Tool definitions with Zod schemas export const toolSchemas = { create_entity: { name: "create_entity", description: "Create a 3D shape in the diagram. Use this to add new elements like boxes, spheres, cylinders, etc.", schema: z.object({ shape: z.enum(["box", "sphere", "cylinder", "cone", "plane", "person"]) .describe("The type of 3D shape to create"), color: z.string().optional() .describe("Color name (red, blue, green, etc.) or hex code (#ff0000)"), text: z.string().optional() .describe("Text label to display on or near the entity"), position: positionSchema }) }, connect_entities: { name: "connect_entities", description: "Draw a connection line between two entities. Check useDefaultLabels preference first - if not set, ask user if they want default labels on connections.", schema: z.object({ from: z.string().describe("ID or label of the source entity"), to: z.string().describe("ID or label of the target entity"), label: z.string().optional().describe("Optional label for the connection. If omitted, default label 'X to Y' is used based on useDefaultLabels preference."), color: z.string().optional().describe("Color of the connection line") }) }, list_entities: { name: "list_entities", description: "List all entities currently in the diagram. Use this to see what exists before connecting or modifying.", schema: z.object({}) }, remove_entity: { name: "remove_entity", description: "Remove an entity from the diagram by its ID or label.", schema: z.object({ target: z.string().describe("ID or label of the entity to remove") }) }, modify_entity: { name: "modify_entity", description: "CALL THIS TOOL to modify an entity. You MUST call this - describing changes does nothing. Use for: resize, move, rename, recolor, rotate, change shape.", schema: z.object({ target: z.string().describe("Label or ID of entity to modify (e.g., 'CDN', 'Server')"), color: z.string().optional().describe("New color hex code from toolbox palette"), text: z.string().optional() .describe("New label text. Use empty string \"\" to remove the label."), shape: z.enum(["box", "sphere", "cylinder", "cone", "plane", "person"]).optional() .describe("New shape for the entity"), position: positionSchema, scale: scaleSchema, rotation: rotationSchema }) }, modify_connection: { name: "modify_connection", description: "Modify a connection's label or color. Connections can be identified by their label or by specifying the from/to entities.", schema: z.object({ target: z.string().optional() .describe("Label of the connection to modify, or use from/to to identify it"), from: z.string().optional() .describe("ID or label of the source entity (alternative to target)"), to: z.string().optional() .describe("ID or label of the destination entity (alternative to target)"), text: z.string().optional() .describe("New label text for the connection. Use empty string \"\" to remove the label."), color: z.string().optional() .describe("New color for the connection") }) }, clear_diagram: { name: "clear_diagram", description: "DESTRUCTIVE: Permanently delete ALL entities from the diagram and clear the session. This cannot be undone. IMPORTANT: Before calling this tool, you MUST first ask the user to confirm. Only call this tool with confirmed=true AFTER the user explicitly confirms.", schema: z.object({ confirmed: z.boolean() .describe("Must be true to execute. Only set to true after user has explicitly confirmed the deletion.") }) }, get_camera_position: { name: "get_camera_position", description: "Get the current camera/viewer position and orientation in the 3D scene. Use this to understand where the user is looking and to position new entities relative to their view.", schema: z.object({}) }, list_models: { name: "list_models", description: "List all available AI models that can be used for this conversation.", schema: z.object({}) }, get_current_model: { name: "get_current_model", description: "Get information about the currently active AI model.", schema: z.object({}) }, set_model: { name: "set_model", description: "Change the AI model. Use the model name from list_models.", schema: z.object({ model_id: z.string() .describe("Model name like 'Claude Opus 4', 'Hermes 2 Pro (CF)', 'Mistral Small 3.1 (CF)', etc.") }) }, clear_conversation: { name: "clear_conversation", description: "Clear the conversation history to start fresh. This preserves the diagram entities but clears chat history.", schema: z.object({}) }, search_wikipedia: { name: "search_wikipedia", description: "Search Wikipedia for information about a topic. Use this to research concepts, architectures, technologies, or anything else that would help create more accurate and detailed diagrams.", schema: z.object({ query: z.string() .describe("The topic or concept to search for (e.g., 'microservices architecture', 'neural network', 'kubernetes')") }) }, set_connection_label_preference: { name: "set_connection_label_preference", description: "Set whether connections should have default labels. Call this after asking the user their preference.", schema: z.object({ use_default_labels: z.boolean() .describe("true = create labels like 'Server to Database', false = no labels on connections") }) }, get_connection_label_preference: { name: "get_connection_label_preference", description: "Check if the user has set a preference for connection labels. Returns the preference or null if not set.", schema: z.object({}) } }; /** * Convert tool schema to Claude/Anthropic format * @param {object} toolDef - Tool definition with name, description, and Zod schema * @returns {object} Tool in Claude format */ function toClaudeFormat(toolDef) { return { name: toolDef.name, description: toolDef.description, input_schema: zodToJsonSchema(toolDef.schema, { target: "openApi3" }) }; } /** * Convert tool schema to OpenAI/Ollama/Cloudflare format * @param {object} toolDef - Tool definition with name, description, and Zod schema * @returns {object} Tool in OpenAI function format */ function toOpenAIFormat(toolDef) { return { type: "function", function: { name: toolDef.name, description: toolDef.description, parameters: zodToJsonSchema(toolDef.schema, { target: "openApi3" }) } }; } // Export tools in different formats export const claudeTools = Object.values(toolSchemas).map(toClaudeFormat); export const openAITools = Object.values(toolSchemas).map(toOpenAIFormat); // For backwards compatibility - alias export const ollamaTools = openAITools; export const cloudflareTools = openAITools; export default { toolSchemas, claudeTools, openAITools, ollamaTools, cloudflareTools };