feat: phase 2 of AI pipeline hardening — tool-based structured outputs + prompt caching #3

Merged
rve merged 1 commits from feat/ai-pipeline-hardening-phase-2 into main 2026-05-20 13:48:09 +00:00
Owner

Every structured-output call now uses an Anthropic tool instead of
parsing JSON out of free-form prose, and stable system prompts are
sent as cacheable blocks. Behaviour-equivalent to phase 1 from the
caller's point of view; the savings show up in token usage and in the
absence of "AI returned non-JSON response" failure modes.

  • src/lib/llmTools.js — single source of truth for tool definitions:
    emit_knowledge_graph, emit_handbook_delta, emit_learning_article /
    _slides / _infographic / _all, emit_custom_topic, emit_quiz_questions,
    emit_graph_actions, plus five article-patch tools (set_intro,
    set_section, add_section, remove_section, replace_takeaways).
  • src/lib/articlePatches.js — pure applyArticlePatches +
    applyAndValidate; rebuilds the article from a sequence of patch tool
    calls and re-validates against learningArticleSchema. set_section
    falls back to appending when no matching heading exists so the
    model's intent is preserved rather than silently dropped.
  • src/lib/llmSchemas.js — Zod schemas for the five patch ops,
    registered in toolSchemaRegistry so callLLM validates them
    automatically.
  • src/lib/llm.js — simulation mode now returns a tool_use stub matching
    toolChoice.name, so the UI keeps working with Simulation Mode on
    after the structured-output migration.
  • src/lib/extractionPipeline.js — processSourceText and
    analyzeHandbookDelta migrated to callLLM + tool use. System prompts
    sent as { cache_control: ephemeral } blocks. Handbook results pass
    through normalizeHandbookResult to collapse legacy "executes"
    relations into executed_by with swapped source/target.
  • src/lib/learningService.js — generateLearningContent picks the right
    tool per selectedType; generateCustomTopic uses emit_custom_topic;
    refineLearningContent now drives the five patch tools with
    toolChoice 'any' and rejects the whole turn if the patched article
    fails validation. Article-only refinement is intentional for phase 2;
    refining a topic without an article surfaces a clear error.
  • src/lib/testService.js — quiz generation via emit_quiz_questions.
  • src/components/admin/KnowledgeGraph.jsx — analyzeGraph routed through
    the reasoning tier (Opus) since graph-wide consolidation benefits
    from a stronger reasoner.
  • src/components/chat/prompts.js — buildSystemPrompt now returns three
    text blocks: stable preamble (cached), KB context (cached, hash-bust
    deferred to phase 5), per-turn user/admin tail (uncached).
  • src/lib/tests/ — 13 new tests covering each patch op, multi-op
    sequencing, post-patch validation failure, and tool/registry shape.

Acceptance: lint and 45/45 tests green; build succeeds; no
match(/\{[\s\S]*\}/) JSON extraction left in src/. Live verification
of cache hits on a second extraction within 5 minutes is deferred to
manual smoke testing — needs real /api/anthropic traffic.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Every structured-output call now uses an Anthropic tool instead of parsing JSON out of free-form prose, and stable system prompts are sent as cacheable blocks. Behaviour-equivalent to phase 1 from the caller's point of view; the savings show up in token usage and in the absence of "AI returned non-JSON response" failure modes. * src/lib/llmTools.js — single source of truth for tool definitions: emit_knowledge_graph, emit_handbook_delta, emit_learning_article / _slides / _infographic / _all, emit_custom_topic, emit_quiz_questions, emit_graph_actions, plus five article-patch tools (set_intro, set_section, add_section, remove_section, replace_takeaways). * src/lib/articlePatches.js — pure applyArticlePatches + applyAndValidate; rebuilds the article from a sequence of patch tool calls and re-validates against learningArticleSchema. set_section falls back to appending when no matching heading exists so the model's intent is preserved rather than silently dropped. * src/lib/llmSchemas.js — Zod schemas for the five patch ops, registered in toolSchemaRegistry so callLLM validates them automatically. * src/lib/llm.js — simulation mode now returns a tool_use stub matching toolChoice.name, so the UI keeps working with Simulation Mode on after the structured-output migration. * src/lib/extractionPipeline.js — processSourceText and analyzeHandbookDelta migrated to callLLM + tool use. System prompts sent as { cache_control: ephemeral } blocks. Handbook results pass through normalizeHandbookResult to collapse legacy "executes" relations into executed_by with swapped source/target. * src/lib/learningService.js — generateLearningContent picks the right tool per selectedType; generateCustomTopic uses emit_custom_topic; refineLearningContent now drives the five patch tools with toolChoice 'any' and rejects the whole turn if the patched article fails validation. Article-only refinement is intentional for phase 2; refining a topic without an article surfaces a clear error. * src/lib/testService.js — quiz generation via emit_quiz_questions. * src/components/admin/KnowledgeGraph.jsx — analyzeGraph routed through the reasoning tier (Opus) since graph-wide consolidation benefits from a stronger reasoner. * src/components/chat/prompts.js — buildSystemPrompt now returns three text blocks: stable preamble (cached), KB context (cached, hash-bust deferred to phase 5), per-turn user/admin tail (uncached). * src/lib/__tests__/ — 13 new tests covering each patch op, multi-op sequencing, post-patch validation failure, and tool/registry shape. Acceptance: lint and 45/45 tests green; build succeeds; no `match(/\{[\s\S]*\}/)` JSON extraction left in src/. Live verification of cache hits on a second extraction within 5 minutes is deferred to manual smoke testing — needs real `/api/anthropic` traffic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rve added 1 commit 2026-05-20 13:48:00 +00:00
feat: phase 2 of AI pipeline hardening — tool-based structured outputs + prompt caching
All checks were successful
On Pull Request to Main / test (pull_request) Successful in 31s
On Pull Request to Main / publish (pull_request) Successful in 1m1s
On Pull Request to Main / deploy-dev (pull_request) Successful in 1m31s
f838755991
Every structured-output call now uses an Anthropic tool instead of
parsing JSON out of free-form prose, and stable system prompts are
sent as cacheable blocks. Behaviour-equivalent to phase 1 from the
caller's point of view; the savings show up in token usage and in the
absence of "AI returned non-JSON response" failure modes.

* src/lib/llmTools.js — single source of truth for tool definitions:
  emit_knowledge_graph, emit_handbook_delta, emit_learning_article /
  _slides / _infographic / _all, emit_custom_topic, emit_quiz_questions,
  emit_graph_actions, plus five article-patch tools (set_intro,
  set_section, add_section, remove_section, replace_takeaways).
* src/lib/articlePatches.js — pure applyArticlePatches +
  applyAndValidate; rebuilds the article from a sequence of patch tool
  calls and re-validates against learningArticleSchema. set_section
  falls back to appending when no matching heading exists so the
  model's intent is preserved rather than silently dropped.
* src/lib/llmSchemas.js — Zod schemas for the five patch ops,
  registered in toolSchemaRegistry so callLLM validates them
  automatically.
* src/lib/llm.js — simulation mode now returns a tool_use stub matching
  toolChoice.name, so the UI keeps working with Simulation Mode on
  after the structured-output migration.
* src/lib/extractionPipeline.js — processSourceText and
  analyzeHandbookDelta migrated to callLLM + tool use. System prompts
  sent as { cache_control: ephemeral } blocks. Handbook results pass
  through normalizeHandbookResult to collapse legacy "executes"
  relations into executed_by with swapped source/target.
* src/lib/learningService.js — generateLearningContent picks the right
  tool per selectedType; generateCustomTopic uses emit_custom_topic;
  refineLearningContent now drives the five patch tools with
  toolChoice 'any' and rejects the whole turn if the patched article
  fails validation. Article-only refinement is intentional for phase 2;
  refining a topic without an article surfaces a clear error.
* src/lib/testService.js — quiz generation via emit_quiz_questions.
* src/components/admin/KnowledgeGraph.jsx — analyzeGraph routed through
  the reasoning tier (Opus) since graph-wide consolidation benefits
  from a stronger reasoner.
* src/components/chat/prompts.js — buildSystemPrompt now returns three
  text blocks: stable preamble (cached), KB context (cached, hash-bust
  deferred to phase 5), per-turn user/admin tail (uncached).
* src/lib/__tests__/ — 13 new tests covering each patch op, multi-op
  sequencing, post-patch validation failure, and tool/registry shape.

Acceptance: lint and 45/45 tests green; build succeeds; no
`match(/\{[\s\S]*\}/)` JSON extraction left in src/. Live verification
of cache hits on a second extraction within 5 minutes is deferred to
manual smoke testing — needs real `/api/anthropic` traffic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rve merged commit 33529dfb2b into main 2026-05-20 13:48:09 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rve/learning-platform#3