/** * Anthropic tool definitions used by every structured-output flow. * * Each `tool_use` reply the model emits is validated against the matching * Zod schema in `llmSchemas.js` (see `toolSchemaRegistry`). The two stay * in lock-step on purpose — JSON Schema here drives the model, Zod there * defends the application. */ const TOPIC_TYPES = ['concept', 'role', 'process']; const LEARNING_RELEVANCE = ['core', 'standard', 'peripheral', 'exclude']; const RELATION_TYPES_STRICT = ['related_to', 'depends_on', 'part_of', 'executed_by']; const extractionTopicSchema = { type: 'object', properties: { id: { type: 'string', description: 'kebab-case slug specific to the topic. Reuse existing IDs when the same concept recurs.' }, label: { type: 'string' }, type: { type: 'string', enum: TOPIC_TYPES }, description: { type: 'string', description: 'Max 3 sentences.' }, learning_relevance: { type: 'string', enum: LEARNING_RELEVANCE }, }, required: ['id', 'label', 'type', 'description', 'learning_relevance'], }; const extractionRelationSchema = { type: 'object', properties: { source: { type: 'string', description: 'Topic id.' }, target: { type: 'string', description: 'Topic id.' }, type: { type: 'string', enum: RELATION_TYPES_STRICT }, }, required: ['source', 'target', 'type'], }; export const EMIT_KNOWLEDGE_GRAPH_TOOL = { name: 'emit_knowledge_graph', description: 'Return the complete knowledge graph extracted from the supplied source text — every distinct role, process and concept as a topic, plus the relations between them.', input_schema: { type: 'object', properties: { topics: { type: 'array', items: extractionTopicSchema }, relations: { type: 'array', items: extractionRelationSchema }, }, required: ['topics', 'relations'], }, }; export const EMIT_CURRICULUM_SCHEDULE_TOOL = { name: 'emit_curriculum_schedule', description: 'Emit a 26-week curriculum schedule. One theme per week, with an ordered subset of topics from that theme.', input_schema: { type: 'object', properties: { weeks: { type: 'array', items: { type: 'object', properties: { week_number: { type: 'integer', minimum: 1, maximum: 26 }, theme: { type: 'string' }, topic_ids: { type: 'array', items: { type: 'string' }, minItems: 1 }, estimated_duration: { type: 'integer', minimum: 15, maximum: 45 }, week_rationale: { type: 'string' }, }, required: ['week_number', 'theme', 'topic_ids', 'estimated_duration', 'week_rationale'], }, minItems: 26, maxItems: 26, }, }, required: ['weeks'], }, }; export const EMIT_TOPIC_ENRICHMENT_TOOL = { name: 'emit_topic_enrichment', description: 'Enrich a batch of topics with a theme, complexity_weight, and difficulty.', input_schema: { type: 'object', properties: { topics: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, theme: { type: 'string' }, complexity_weight: { type: 'integer', minimum: 1, maximum: 5 }, difficulty: { type: 'string', enum: ['introductory', 'intermediate', 'advanced'] }, }, required: ['id', 'theme', 'complexity_weight', 'difficulty'], }, minItems: 1, }, }, required: ['topics'], }, }; const articleSectionSchema = { type: 'object', properties: { heading: { type: 'string' }, body: { type: 'string', description: 'At least three sentences.' }, }, required: ['heading', 'body'], }; const articleBodySchema = { type: 'object', properties: { title: { type: 'string' }, intro: { type: 'string', description: 'One or two sentences.' }, sections: { type: 'array', items: articleSectionSchema, minItems: 1 }, keyTakeaways: { type: 'array', items: { type: 'string' }, minItems: 1 }, }, required: ['title', 'intro', 'sections', 'keyTakeaways'], }; const slideSchema = { type: 'object', properties: { title: { type: 'string' }, bullets: { type: 'array', items: { type: 'string' }, minItems: 1 }, speakerNote: { type: 'string' }, }, required: ['title', 'bullets', 'speakerNote'], }; const infographicStatSchema = { type: 'object', properties: { value: { type: 'string' }, label: { type: 'string' }, icon: { type: 'string' }, }, required: ['value', 'label', 'icon'], }; const infographicStepSchema = { type: 'object', properties: { number: { type: 'integer', minimum: 1 }, title: { type: 'string' }, description: { type: 'string' }, icon: { type: 'string' }, }, required: ['number', 'title', 'description', 'icon'], }; const infographicBodySchema = { type: 'object', properties: { headline: { type: 'string', description: 'Punchy, max 8 words.' }, tagline: { type: 'string', description: 'Max 15 words.' }, stats: { type: 'array', items: infographicStatSchema, minItems: 1 }, steps: { type: 'array', items: infographicStepSchema, minItems: 1 }, quote: { type: 'string' }, colorTheme: { type: 'string', description: 'Tailwind colour token (e.g. "teal").' }, }, required: ['headline', 'tagline', 'stats', 'steps', 'quote', 'colorTheme'], }; export const EMIT_LEARNING_ARTICLE_TOOL = { name: 'emit_learning_article', description: 'Return the article body for a learning module. At least three sections.', input_schema: { type: 'object', properties: { article: articleBodySchema }, required: ['article'], }, }; export const EMIT_LEARNING_SLIDES_TOOL = { name: 'emit_learning_slides', description: 'Return the slide deck for a learning module. At least four slides.', input_schema: { type: 'object', properties: { slides: { type: 'array', items: slideSchema, minItems: 1 } }, required: ['slides'], }, }; export const EMIT_LEARNING_INFOGRAPHIC_TOOL = { name: 'emit_learning_infographic', description: 'Return the infographic for a learning module. At least three stats and three steps.', input_schema: { type: 'object', properties: { infographic: infographicBodySchema }, required: ['infographic'], }, }; export const EMIT_LEARNING_ALL_TOOL = { name: 'emit_learning_all', description: 'Return article, slides and infographic for a learning module in one call.', input_schema: { type: 'object', properties: { article: articleBodySchema, slides: { type: 'array', items: slideSchema, minItems: 1 }, infographic: infographicBodySchema, }, required: ['article', 'slides', 'infographic'], }, }; export const EMIT_CUSTOM_TOPIC_TOOL = { name: 'emit_custom_topic', description: 'Return a polished label, type and short description for a user-requested topic.', input_schema: { type: 'object', properties: { label: { type: 'string' }, type: { type: 'string', enum: TOPIC_TYPES }, description: { type: 'string', description: 'Two or three sentences.' }, }, required: ['label', 'type', 'description'], }, }; const QUIZ_DIFFICULTIES = ['easy', 'medium', 'hard']; const quizQuestionSchema = { type: 'object', properties: { id: { type: 'string' }, question: { type: 'string' }, topicLabel: { type: 'string' }, options: { type: 'array', items: { type: 'string' }, minItems: 4, maxItems: 4 }, correctIndex: { type: 'integer', minimum: 0, maximum: 3 }, explanation: { type: 'string', description: 'Why the correct answer is correct (1–2 sentences).' }, difficulty: { type: 'string', enum: QUIZ_DIFFICULTIES, description: 'Per-question difficulty tag.' }, }, required: ['id', 'question', 'topicLabel', 'options', 'correctIndex', 'explanation', 'difficulty'], }; export const EMIT_QUIZ_QUESTIONS_TOOL = { name: 'emit_quiz_questions', description: 'Return a batch of multiple-choice questions for a topic. Exactly four options each; correctIndex is 0-based.', input_schema: { type: 'object', properties: { questions: { type: 'array', items: quizQuestionSchema, minItems: 1 } }, required: ['questions'], }, }; export const EMIT_GRAPH_ACTIONS_TOOL = { name: 'emit_graph_actions', description: 'Return the actions to take on the knowledge graph: merges, deletions, new relations and relevance updates. Do not return the entire graph.', input_schema: { type: 'object', properties: { merges: { type: 'array', items: { type: 'object', properties: { keepId: { type: 'string' }, deleteId: { type: 'string' } }, required: ['keepId', 'deleteId'], }, }, deletions: { type: 'array', items: { type: 'string' } }, newRelations: { type: 'array', items: extractionRelationSchema }, relevanceUpdates: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, learning_relevance: { type: 'string', enum: LEARNING_RELEVANCE } }, required: ['id', 'learning_relevance'], }, }, }, }, }; // ── Patch tools for refineLearningContent (Phase 2.4) ───────────────────────── export const SET_INTRO_TOOL = { name: 'set_intro', description: 'Replace the article intro with a new one or two sentences.', input_schema: { type: 'object', properties: { intro: { type: 'string', description: 'New intro text.' } }, required: ['intro'], }, }; export const SET_SECTION_TOOL = { name: 'set_section', description: 'Replace the body of an existing section, matched by its heading (case-insensitive). Use add_section if no section with that heading exists.', input_schema: { type: 'object', properties: { heading: { type: 'string', description: 'Heading of the section to replace.' }, body: { type: 'string', description: 'New body for that section, at least three sentences.' }, }, required: ['heading', 'body'], }, }; export const ADD_SECTION_TOOL = { name: 'add_section', description: 'Insert a new section into the article at the start or end.', input_schema: { type: 'object', properties: { heading: { type: 'string' }, body: { type: 'string', description: 'At least three sentences.' }, position: { type: 'string', enum: ['start', 'end'] }, }, required: ['heading', 'body', 'position'], }, }; export const REMOVE_SECTION_TOOL = { name: 'remove_section', description: 'Delete a section from the article, matched by its heading (case-insensitive).', input_schema: { type: 'object', properties: { heading: { type: 'string' } }, required: ['heading'], }, }; export const REPLACE_TAKEAWAYS_TOOL = { name: 'replace_takeaways', description: 'Replace the key takeaways list with a new one.', input_schema: { type: 'object', properties: { items: { type: 'array', items: { type: 'string' }, minItems: 1 } }, required: ['items'], }, }; export const ARTICLE_PATCH_TOOLS = [ SET_INTRO_TOOL, SET_SECTION_TOOL, ADD_SECTION_TOOL, REMOVE_SECTION_TOOL, REPLACE_TAKEAWAYS_TOOL, ]; // ── Micro Learning generation tools ─────────────────────────────────────────── export const EMIT_CONCEPT_EXPLAINER_TOOL = { name: 'emit_concept_explainer', description: 'Return a structured concept explanation with multiple sections. Each section moves from definition → importance → practical application. The final section must include a concrete workplace example.', input_schema: { type: 'object', properties: { sections: { type: 'array', items: { type: 'object', properties: { title: { type: 'string', description: 'Section heading.' }, content: { type: 'string', description: 'Section body in HTML. Use

,