Implements phase 1 of AI_PIPELINE_HARDENING_PLAN.md. Every Anthropic call now goes through one module that owns retry, timeout, abort, structured- output parsing, schema validation, and best-effort call telemetry. * src/lib/llm.js — single callLLM entry point. Resolves model per tier (fast / standard / reasoning) with admin:model legacy fallback for the standard tier; 60s default timeout via AbortController; balanced-brace JSON extraction; LLMHttpError, LLMTruncatedError, LLMOutputError, and LLMValidationError surface clearly distinct failure modes. * src/lib/llmRetry.js — exponential backoff with full jitter, retries only on transient HTTP statuses, honours Retry-After up to 60s, never retries on AbortError. * src/lib/llmSchemas.js — Zod schemas for every structured task plus normalizeHandbookResult (collapses legacy "executes" relations into the canonical "executed_by" vocabulary). * src/lib/api.js — thin shim over callLLM so existing callers (extraction pipeline, learning, quiz, R42, knowledge graph) keep working unchanged. * src/lib/__tests__/ — 32 Vitest cases covering parse paths, error surfaces, simulation mode, model resolution, and schema validation. * src/pages/Admin/index.jsx — three model inputs (fast / standard / reasoning) replacing the single legacy field; legacy value falls back for the standard tier so existing overrides survive. Adds Zod and Vitest, plus an "npm run test" script. Also cleans up the pre-existing repo-wide ESLint failures so phase 1's "npm run lint passes" acceptance criterion can be checked: drops unused React imports across the JSX tree (React 19 JSX runtime auto-imports), attaches cause to rethrown errors in the service modules, ignores pb_migrations in the ESLint config (PocketBase JSVM globals), and removes one dead handleCreateCustom function in Leren.jsx. A real behaviour bug surfaced in Testen.jsx — the quiz timer captured a stale finishQuiz via setInterval closure; now updated via finishQuizRef so the timer always invokes the latest callback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
40 lines
1.1 KiB
JavaScript
40 lines
1.1 KiB
JavaScript
/**
|
|
* Back-compatibility shim for the legacy `anthropicApi` interface.
|
|
*
|
|
* All real work lives in `./llm.js`. Existing callers (extractionPipeline,
|
|
* learningService, testService, KnowledgeGraph, useChat) keep working
|
|
* unchanged; new code should import `callLLM` from `./llm.js` directly.
|
|
*/
|
|
|
|
import { callLLM } from './llm';
|
|
|
|
export const anthropicApi = {
|
|
async generateContent(systemPrompt, userMessage /*, maxRetries */) {
|
|
const { text } = await callLLM({
|
|
task: 'legacy.generateContent',
|
|
tier: 'standard',
|
|
system: systemPrompt,
|
|
user: userMessage,
|
|
maxTokens: 8192,
|
|
temperature: 0,
|
|
});
|
|
return text;
|
|
},
|
|
|
|
async chat(systemPrompt, messages, opts = {}) {
|
|
const r = await callLLM({
|
|
task: 'legacy.chat',
|
|
tier: 'standard',
|
|
system: systemPrompt,
|
|
messages,
|
|
tools: opts.tools,
|
|
maxTokens: 1024,
|
|
temperature: 0.3,
|
|
});
|
|
const content = [];
|
|
if (r.text) content.push({ type: 'text', text: r.text });
|
|
for (const tu of r.toolUses) content.push({ type: 'tool_use', name: tu.name, input: tu.input });
|
|
return { content, stop_reason: r.stopReason };
|
|
},
|
|
};
|