@aglyn/plugins-ai 1.0.0-beta.257 → 1.0.0-beta.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/assist-panel.component.js +21 -8
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/insights/ai-figure-readers.js +15 -8
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +9 -0
- package/src/lib/jobs/ai-job-build-step.js +19 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +33 -0
- package/src/lib/jobs/ai-job-page-language.js +46 -6
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +12 -1
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +14 -0
- package/src/lib/jobs/ai-job-page-step.js +79 -39
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.js +3 -2
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +7 -1
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +10 -5
- package/src/lib/jobs/ai-layout-stock-photos.js +89 -7
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.js +126 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.js +52 -52
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-copy.d.ts +9 -5
- package/src/lib/layout-language/ai-layout-copy.js +22 -14
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +4 -4
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-motion.d.ts +75 -0
- package/src/lib/layout-language/ai-layout-motion.js +172 -0
- package/src/lib/layout-language/ai-layout-motion.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +44 -15
- package/src/lib/layout-language/ai-layout-pictures.js +29 -8
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.js +14 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-host-publish-context.d.ts +6 -0
- package/src/lib/model/ai-host-publish-context.js +15 -2
- package/src/lib/model/ai-host-publish-context.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +35 -0
- package/src/lib/model/assist-follow-up.js +44 -4
- package/src/lib/model/assist-follow-up.js.map +1 -1
- package/src/lib/operator-alerts.js +1 -1
- package/src/lib/operator-alerts.js.map +1 -1
- package/src/lib/providers/anthropic.js +17 -4
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/cache-write-hour.d.ts +37 -0
- package/src/lib/providers/cache-write-hour.js +46 -0
- package/src/lib/providers/cache-write-hour.js.map +1 -0
- package/src/lib/providers/contract.d.ts +16 -3
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/routing.d.ts +13 -0
- package/src/lib/providers/routing.js +27 -11
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-dev-env.d.ts +13 -3
- package/src/lib/runtime/ai-dev-env.js +17 -3
- package/src/lib/runtime/ai-dev-env.js.map +1 -1
- package/src/lib/runtime/ai-dev-replay.js +4 -2
- package/src/lib/runtime/ai-dev-replay.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +70 -1
- package/src/lib/runtime/ai-doctrine-validators.js +332 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +12 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +1 -1
- package/src/lib/runtime/ai-eval.js +18 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-placeholder-tokens.d.ts +70 -0
- package/src/lib/runtime/ai-placeholder-tokens.js +169 -0
- package/src/lib/runtime/ai-placeholder-tokens.js.map +1 -0
- package/src/lib/runtime/ai-repeated-items.js +5 -2
- package/src/lib/runtime/ai-repeated-items.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +2 -3
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/server/assist-chat.js +8 -3
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.d.ts +21 -0
- package/src/lib/server/assist-edit.js +99 -21
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +5 -1
- package/src/lib/server/assist-view-context.js +8 -3
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +2 -0
- package/src/lib/usage/assist-usage.js +8 -3
- package/src/lib/usage/assist-usage.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/assist-follow-up.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_JOB_BESIGNER_SEGMENT } from './ai-job-notice'\nimport type { AiJobOutput } from './ai-jobs.types'\n\n/**\n * A follow-up on what a build made (AGL-3616): \"make the about page shorter\"\n * after Assist built the About page.\n *\n * The edit card works only on the document open in the Besigner. So when a\n * request is about a draft the build in this chat made, Assist proposes\n * OPENING that draft's Besigner, and the panel asks the same request again\n * once the draft's canvas is open — where the edit rung answers it with an\n * edit card the author applies. Nothing is changed by the proposal itself.\n *\n * The panel names the drafts it can open by a short ref (`d1`, `d2`, …) and a\n * label; the ids and the address stay in the panel, which got them from the\n * job door. The chat sees only refs and labels, and a proposal it makes is\n * held to a ref the request listed.\n *\n * Shared by the chat door and the panel. Pure: no request, no React.\n */\n\n/** The navigation's id, in the proposal channel and the analytics event. */\nexport const ASSIST_OPEN_DRAFT_ACTION_ID = 'open.build.draft'\n\n/** The one parameter the navigation takes: the ref of the draft to open. */\nexport const ASSIST_OPEN_DRAFT_PARAM = 'draft'\n\n/** The most drafts one request lists: a build makes at most 16 units. */\nexport const ASSIST_BUILD_DRAFTS_MAX = 16\n\n/** The longest label a request carries for one draft. */\nexport const ASSIST_BUILD_DRAFT_LABEL_MAX = 80\n\n/** How long a follow-up waits for its canvas before it is dropped. */\nexport const ASSIST_FOLLOW_UP_TTL_MS = 2 * 60_000\n\n/** A draft the chat may offer to open, as the request names it. */\nexport interface AssistBuildDraft {\n /** `d1`, `d2`, …: what the model chooses from. */\n ref: string\n /** What the draft is called: the page's title, the layout's name. */\n label: string\n /** What kind of thing it is, in a word a person reads. */\n noun: string\n}\n\n/** A draft the panel can open, with the address it opens at. */\nexport interface AssistBuildDraftLink extends AssistBuildDraft {\n href: string\n}\n\n/** What one Besigner-backed output is called in a sentence. */\nconst NOUNS: Partial<Record<AiJobOutput['resource'], string>> = {\n screen: 'page',\n reusableComponent: 'component',\n layout: 'layout',\n template: 'page template',\n emailScreen: 'email design',\n}\n\n/** The Besigner address a job's output opens at, or `null` where it has none. */\nexport function assistDraftBesignerHref(output: AiJobOutput, orgSlug: string): string | null {\n const segment = AI_JOB_BESIGNER_SEGMENT[output.resource]\n if (!segment || !orgSlug || !output.hostSubdomain || !output.versionId) return null\n return `/${orgSlug}/hosts/${output.hostSubdomain}/${segment}/${output.id}/versions/${output.versionId}/besigner`\n}\n\n/**\n * The drafts the panel can offer, newest build first: every output of the\n * given jobs that opens in the Besigner on the version the job wrote, once\n * each, at most {@link ASSIST_BUILD_DRAFTS_MAX}.\n */\nexport function assistBuildDraftLinks(\n jobs: ReadonlyArray<{ outputs: readonly AiJobOutput[] }>,\n orgSlug: string,\n): AssistBuildDraftLink[] {\n const links: AssistBuildDraftLink[] = []\n const seen = new Set<string>()\n for (const job of jobs) {\n for (const output of job.outputs) {\n if (links.length >= ASSIST_BUILD_DRAFTS_MAX) return links\n const href = assistDraftBesignerHref(output, orgSlug)\n if (!href || seen.has(href)) continue\n seen.add(href)\n links.push({\n ref: `d${links.length + 1}`,\n label: assistDraftLabel(output.label),\n noun: NOUNS[output.resource] ?? 'draft',\n href,\n })\n }\n }\n return links\n}\n\n/** A label as it may enter a prompt: one line, no fence, quotes or brackets, bounded. */\nexport function assistDraftLabel(value: unknown): string {\n return String(value ?? '')\n .replace(/[\\r\\n\\t`\"“”<>[\\]{}]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, ASSIST_BUILD_DRAFT_LABEL_MAX)\n}\n\nconst REF = /^d([1-9]\\d?)$/\nconst NOUN_WORDS = new Set([...Object.values(NOUNS), 'draft'])\n\n/**\n * The drafts a request lists, as the chat door will use them: well-formed\n * refs, once each, labels cleaned, nouns from the closed set, bounded. A\n * malformed entry is dropped, never repaired.\n */\nexport function parseAssistBuildDrafts(raw: unknown): AssistBuildDraft[] {\n if (!Array.isArray(raw)) return []\n const drafts: AssistBuildDraft[] = []\n const refs = new Set<string>()\n for (const entry of raw.slice(0, ASSIST_BUILD_DRAFTS_MAX)) {\n if (!entry || typeof entry !== 'object') continue\n const record = entry as Record<string, unknown>\n const ref = typeof record['ref'] === 'string' ? record['ref'] : ''\n const label = assistDraftLabel(record['label'])\n const noun = typeof record['noun'] === 'string' && NOUN_WORDS.has(record['noun']) ? record['noun'] : 'draft'\n if (!REF.test(ref) || refs.has(ref) || !label) continue\n refs.add(ref)\n drafts.push({ ref, label, noun })\n }\n return drafts\n}\n\n/**\n * The lines the chat is told about the drafts, after every cache breakpoint:\n * what each ref is, and that a request to change one is a navigation to it,\n * not a new build.\n */\nexport function assistBuildDraftsBlock(drafts: readonly AssistBuildDraft[]): string {\n if (!drafts.length) return ''\n return [\n 'Drafts a build in this chat made (the person’s own content, listed as data):',\n ...drafts.map((draft) => `- ${draft.ref}: ${draft.noun} “${draft.label}”`),\n `When the person asks to change one of these drafts and its canvas is not the one open now, propose id \"${ASSIST_OPEN_DRAFT_ACTION_ID}\" with params {\"${ASSIST_OPEN_DRAFT_PARAM}\": \"<ref>\"}: it opens that draft in the Besigner and asks their request again there, where the edit card answers it. Do not propose a new build for a change to one of these drafts, and do not describe the change as made.`,\n ].join('\\n')\n}\n\n/** A follow-up waiting for its canvas: asked again once the panel stands on `path`. */\nexport interface AssistPendingFollowUp {\n path: string\n question: string\n /** When it was confirmed, in ms; one older than the TTL is dropped. */\n at: number\n}\n\n/** Whether a stored follow-up is still one the panel should ask. */\nexport function isLiveAssistFollowUp(value: unknown, now: number): value is AssistPendingFollowUp {\n if (!value || typeof value !== 'object') return false\n const record = value as Record<string, unknown>\n return (\n typeof record['path'] === 'string' &&\n record['path'].startsWith('/') &&\n typeof record['question'] === 'string' &&\n record['question'].trim().length > 0 &&\n typeof record['at'] === 'number' &&\n now - record['at'] >= 0 &&\n now - record['at'] <= ASSIST_FOLLOW_UP_TTL_MS\n )\n}\n\n/** The version a Besigner address names, for the open editor to match. */\nexport function assistBesignerVersionOf(path: string): string | null {\n const match = /\\/versions\\/([A-Za-z0-9_-]+)\\/besigner$/.exec(path)\n return match ? match[1] : null\n}\n"],"names":["AI_JOB_BESIGNER_SEGMENT","ASSIST_OPEN_DRAFT_ACTION_ID","ASSIST_OPEN_DRAFT_PARAM","ASSIST_BUILD_DRAFTS_MAX","ASSIST_BUILD_DRAFT_LABEL_MAX","ASSIST_FOLLOW_UP_TTL_MS","NOUNS","screen","reusableComponent","layout","template","emailScreen","assistDraftBesignerHref","output","orgSlug","segment","resource","hostSubdomain","versionId","id","assistBuildDraftLinks","jobs","links","seen","Set","job","outputs","length","href","has","add","push","ref","label","assistDraftLabel","noun","value","String","replace","trim","slice","REF","NOUN_WORDS","Object","values","parseAssistBuildDrafts","raw","Array","isArray","drafts","refs","entry","record","test","assistBuildDraftsBlock","map","draft","join","isLiveAssistFollowUp","now","startsWith","assistBesignerVersionOf","path","match","exec"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/assist-follow-up.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_JOB_BESIGNER_SEGMENT } from './ai-job-notice'\nimport type { AiJobOutput } from './ai-jobs.types'\n\n/**\n * A follow-up on what a build made (AGL-3616): \"make the about page shorter\"\n * after Assist built the About page.\n *\n * The edit card works only on the document open in the Besigner. So when a\n * request is about a draft the build in this chat made, Assist proposes\n * OPENING that draft's Besigner, and the panel asks the same request again\n * once the draft's canvas is open — where the edit rung answers it with an\n * edit card the author applies. Nothing is changed by the proposal itself.\n *\n * The panel names the drafts it can open by a short ref (`d1`, `d2`, …) and a\n * label; the ids and the address stay in the panel, which got them from the\n * job door. The chat sees only refs and labels, and a proposal it makes is\n * held to a ref the request listed.\n *\n * Shared by the chat door and the panel. Pure: no request, no React.\n */\n\n/** The navigation's id, in the proposal channel and the analytics event. */\nexport const ASSIST_OPEN_DRAFT_ACTION_ID = 'open.build.draft'\n\n/** The one parameter the navigation takes: the ref of the draft to open. */\nexport const ASSIST_OPEN_DRAFT_PARAM = 'draft'\n\n/** The most drafts one request lists: a build makes at most 16 units. */\nexport const ASSIST_BUILD_DRAFTS_MAX = 16\n\n/** The longest label a request carries for one draft. */\nexport const ASSIST_BUILD_DRAFT_LABEL_MAX = 80\n\n/** How long a follow-up waits for its canvas before it is dropped. */\nexport const ASSIST_FOLLOW_UP_TTL_MS = 2 * 60_000\n\n/** A draft the chat may offer to open, as the request names it. */\nexport interface AssistBuildDraft {\n /** `d1`, `d2`, …: what the model chooses from. */\n ref: string\n /** What the draft is called: the page's title, the layout's name. */\n label: string\n /** What kind of thing it is, in a word a person reads. */\n noun: string\n /**\n * The draft whose canvas is open in the editor the request was asked from\n * (AGL-3833). Set only when the request also carries that canvas: the\n * answer edits it there, and never offers to open it again.\n */\n open?: true\n}\n\n/** A draft the panel can open, with the address it opens at. */\nexport interface AssistBuildDraftLink extends AssistBuildDraft {\n href: string\n}\n\n/** What one Besigner-backed output is called in a sentence. */\nconst NOUNS: Partial<Record<AiJobOutput['resource'], string>> = {\n screen: 'page',\n reusableComponent: 'component',\n layout: 'layout',\n template: 'page template',\n emailScreen: 'email design',\n}\n\n/** The Besigner address a job's output opens at, or `null` where it has none. */\nexport function assistDraftBesignerHref(output: AiJobOutput, orgSlug: string): string | null {\n const segment = AI_JOB_BESIGNER_SEGMENT[output.resource]\n if (!segment || !orgSlug || !output.hostSubdomain || !output.versionId) return null\n return `/${orgSlug}/hosts/${output.hostSubdomain}/${segment}/${output.id}/versions/${output.versionId}/besigner`\n}\n\n/**\n * The drafts the panel can offer, newest build first: every output of the\n * given jobs that opens in the Besigner on the version the job wrote, once\n * each, at most {@link ASSIST_BUILD_DRAFTS_MAX}.\n */\nexport function assistBuildDraftLinks(\n jobs: ReadonlyArray<{ outputs: readonly AiJobOutput[] }>,\n orgSlug: string,\n): AssistBuildDraftLink[] {\n const links: AssistBuildDraftLink[] = []\n const seen = new Set<string>()\n for (const job of jobs) {\n for (const output of job.outputs) {\n if (links.length >= ASSIST_BUILD_DRAFTS_MAX) return links\n const href = assistDraftBesignerHref(output, orgSlug)\n if (!href || seen.has(href)) continue\n seen.add(href)\n links.push({\n ref: `d${links.length + 1}`,\n label: assistDraftLabel(output.label),\n noun: NOUNS[output.resource] ?? 'draft',\n href,\n })\n }\n }\n return links\n}\n\n/** A label as it may enter a prompt: one line, no fence, quotes or brackets, bounded. */\nexport function assistDraftLabel(value: unknown): string {\n return String(value ?? '')\n .replace(/[\\r\\n\\t`\"“”<>[\\]{}]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, ASSIST_BUILD_DRAFT_LABEL_MAX)\n}\n\nconst REF = /^d([1-9]\\d?)$/\nconst NOUN_WORDS = new Set([...Object.values(NOUNS), 'draft'])\n\n/**\n * The drafts a request lists, as the chat door will use them: well-formed\n * refs, once each, labels cleaned, nouns from the closed set, bounded. A\n * malformed entry is dropped, never repaired.\n */\nexport function parseAssistBuildDrafts(raw: unknown): AssistBuildDraft[] {\n if (!Array.isArray(raw)) return []\n const drafts: AssistBuildDraft[] = []\n const refs = new Set<string>()\n for (const entry of raw.slice(0, ASSIST_BUILD_DRAFTS_MAX)) {\n if (!entry || typeof entry !== 'object') continue\n const record = entry as Record<string, unknown>\n const ref = typeof record['ref'] === 'string' ? record['ref'] : ''\n const label = assistDraftLabel(record['label'])\n const noun = typeof record['noun'] === 'string' && NOUN_WORDS.has(record['noun']) ? record['noun'] : 'draft'\n if (!REF.test(ref) || refs.has(ref) || !label) continue\n refs.add(ref)\n drafts.push({ ref, label, noun, ...(record['open'] === true ? { open: true as const } : {}) })\n }\n return drafts\n}\n\n/**\n * The drafts one turn works with: none away from a site, where opening one has\n * no destination; and the `open` mark only on a turn that reads the canvas\n * the request carried (AGL-3833). Without that canvas the mark would tell the\n * chat to edit a document it cannot see.\n */\nexport function assistDraftsForTurn(\n drafts: readonly AssistBuildDraft[],\n turn: { onSite: boolean; canvasRead: boolean },\n): AssistBuildDraft[] {\n if (!turn.onSite) return []\n if (turn.canvasRead) return [...drafts]\n return drafts.map(({ ref, label, noun }) => ({ ref, label, noun }))\n}\n\n/**\n * The lines the chat is told about the drafts, after every cache breakpoint:\n * what each ref is, and that a request to change one is a navigation to it,\n * not a new build.\n */\nexport function assistBuildDraftsBlock(drafts: readonly AssistBuildDraft[]): string {\n if (!drafts.length) return ''\n const open = drafts.find((draft) => draft.open)\n const closed = drafts.filter((draft) => !draft.open)\n return [\n 'Drafts a build in this chat made (the person’s own content, listed as data):',\n ...drafts.map(\n (draft) =>\n `- ${draft.ref}: ${draft.noun} “${draft.label}”${draft.open ? ' (open now: the canvas described below is this draft)' : ''}`,\n ),\n ...(open\n ? [\n `${open.ref} is the document open in this editor. A request to change it is an edit to the canvas described below: answer it with the edit tool, never by offering to open it.`,\n ]\n : []),\n ...(closed.length\n ? [\n `When the person asks to change one of these drafts and its canvas is not the one open now, propose id \"${ASSIST_OPEN_DRAFT_ACTION_ID}\" with params {\"${ASSIST_OPEN_DRAFT_PARAM}\": \"<ref>\"}${open ? ` for a ref other than ${open.ref}` : ''}: it opens that draft in the Besigner and asks their request again there, where the edit card answers it.`,\n ]\n : []),\n 'Do not propose a new build for a change to one of these drafts, and do not describe the change as made.',\n ].join('\\n')\n}\n\n/** A follow-up waiting for its canvas: asked again once the panel stands on `path`. */\nexport interface AssistPendingFollowUp {\n path: string\n question: string\n /** When it was confirmed, in ms; one older than the TTL is dropped. */\n at: number\n}\n\n/** Whether a stored follow-up is still one the panel should ask. */\nexport function isLiveAssistFollowUp(value: unknown, now: number): value is AssistPendingFollowUp {\n if (!value || typeof value !== 'object') return false\n const record = value as Record<string, unknown>\n return (\n typeof record['path'] === 'string' &&\n record['path'].startsWith('/') &&\n typeof record['question'] === 'string' &&\n record['question'].trim().length > 0 &&\n typeof record['at'] === 'number' &&\n now - record['at'] >= 0 &&\n now - record['at'] <= ASSIST_FOLLOW_UP_TTL_MS\n )\n}\n\n/** The version a Besigner address names, for the open editor to match. */\nexport function assistBesignerVersionOf(path: string): string | null {\n const match = /\\/versions\\/([A-Za-z0-9_-]+)\\/besigner$/.exec(path)\n return match ? match[1] : null\n}\n\n/** Where the panel stands when it decides whether to ask a follow-up again. */\nexport interface AssistFollowUpStand {\n /** The route the panel is on. */\n path: string\n /** The version the open editor session reports, or null with none open. */\n versionId: string | null\n /** Whether the canvas holds the open document's nodes yet. */\n canvasLoaded: boolean\n}\n\n/**\n * Whether a follow-up is asked again now (AGL-3833): on its draft's address,\n * with the editor reporting that draft's version AND its canvas loaded.\n *\n * The editor reports its version from the route the moment it mounts, well\n * before the document's nodes reach the canvas. Asked in that gap, the request\n * carried no canvas, so the chat could not see that the draft was open and\n * offered to open it again: a loop that spent credits on every turn.\n */\nexport function isAssistFollowUpDue(followUp: AssistPendingFollowUp, stand: AssistFollowUpStand): boolean {\n if (stand.path !== followUp.path) return false\n if (!stand.versionId || stand.versionId !== assistBesignerVersionOf(followUp.path)) return false\n return stand.canvasLoaded\n}\n"],"names":["AI_JOB_BESIGNER_SEGMENT","ASSIST_OPEN_DRAFT_ACTION_ID","ASSIST_OPEN_DRAFT_PARAM","ASSIST_BUILD_DRAFTS_MAX","ASSIST_BUILD_DRAFT_LABEL_MAX","ASSIST_FOLLOW_UP_TTL_MS","NOUNS","screen","reusableComponent","layout","template","emailScreen","assistDraftBesignerHref","output","orgSlug","segment","resource","hostSubdomain","versionId","id","assistBuildDraftLinks","jobs","links","seen","Set","job","outputs","length","href","has","add","push","ref","label","assistDraftLabel","noun","value","String","replace","trim","slice","REF","NOUN_WORDS","Object","values","parseAssistBuildDrafts","raw","Array","isArray","drafts","refs","entry","record","test","open","assistDraftsForTurn","turn","onSite","canvasRead","map","assistBuildDraftsBlock","find","draft","closed","filter","join","isLiveAssistFollowUp","now","startsWith","assistBesignerVersionOf","path","match","exec","isAssistFollowUpDue","followUp","stand","canvasLoaded"],"mappings":"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"}
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
description: 'An AI build (a site, or a multi-part build) finished, but some of its parts failed on the platform’s side, so the customer got less than they asked for. Read which parts and why, then search the logs for the job id.',
|
|
69
69
|
tier: 'should',
|
|
70
70
|
category: 'ops',
|
|
71
|
-
title: 'An AI {{kind}}
|
|
71
|
+
title: 'An AI {{kind}} job finished with {{failed}} of {{total}} parts failed',
|
|
72
72
|
body: 'AI job {{jobId}} ({{kind}}) in workspace {{orgId}} finished, but {{failed}} of its {{total}} parts failed on our side: {{items}}. The customer was given back those parts’ credits. Further partial {{kind}} builds within the hour are counted into the next alert.',
|
|
73
73
|
link: '/admin/orgs/{{orgId}}',
|
|
74
74
|
delivery: 'immediate',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/operator-alerts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { OperatorAlertDefinition } from '@aglyn/aglyn/app-utils/operator-alerts'\nimport { AI_PLUGIN_ID } from './constants'\n\n/**\n * The AI plugin's operator alert (AGL-3377): the platform's provider account\n * refused its key or ran out of credit, so every AI feature on the install\n * fails. Provider-generic — each adapter classifies its own vendor's answer\n * as an `accountProblem`, and the runtime raises this with the provider's\n * label — so a self-hoster on any provider hears the same thing.\n */\nexport const AI_PROVIDER_UNAVAILABLE: OperatorAlertDefinition = {\n type: 'ai.providerUnavailable',\n pluginId: AI_PLUGIN_ID,\n label: 'Platform AI provider unavailable',\n description:\n 'The platform’s AI provider refused its key or ran out of credit, so every AI feature on the install is failing until the key or the balance is fixed.',\n tier: 'should',\n category: 'ops',\n title: '{{provider}} is refusing requests',\n body:\n 'The platform’s {{provider}} account refused a request because {{reason}}. AI features fail until it is fixed ({{keyEnv}}).',\n delivery: 'immediate',\n dedupeWindowMinutes: 6 * 60,\n defaultEnabled: true,\n}\n\n/**\n * An AI job failed on our side: a provider error, a step that produced\n * nothing usable, a build that delivered nothing. Not a model declining the\n * brief, and not a site with AI switched off — those are the customer's, and\n * the customer is told. Deduped per job kind, so an outage is one alert an\n * hour for each kind it breaks rather than one per job; the runner's own\n * words are in `{{error}}`, and every failure is in the log as `ai job failed`.\n */\nexport const AI_JOB_FAILED: OperatorAlertDefinition = {\n type: 'ai.jobFailed',\n pluginId: AI_PLUGIN_ID,\n label: 'An AI job failed',\n description:\n 'An AI job (a site, page, build, theme…) failed on the platform’s side, so a customer got nothing for their request. Read the error, then search the logs for “ai job failed” to see whether it is one job or every job of that kind.',\n tier: 'should',\n category: 'ops',\n title: 'An AI {{kind}} job failed',\n body:\n 'AI job {{jobId}} ({{kind}}) in workspace {{orgId}} failed at step {{step}}: {{error}}. The customer was told and given back the credits. Further {{kind}} failures within the hour are counted into the next alert.',\n link: '/admin/orgs/{{orgId}}',\n delivery: 'immediate',\n dedupeWindowMinutes: 60,\n defaultEnabled: true,\n}\n\n/**\n * A build finished with part of it missing: it delivered something, so the\n * job is `done`, but one or more of its items failed on our side (AGL-3683).\n * The customer has a site or build with holes in it and no failed job to\n * show for it, which is why it is its own alert. Items the model declined\n * are the customer's and do not count. Deduped per job kind, like\n * `ai.jobFailed`.\n */\nexport const AI_BUILD_PARTLY_FAILED: OperatorAlertDefinition = {\n type: 'ai.buildPartlyFailed',\n pluginId: AI_PLUGIN_ID,\n label: 'An AI build finished with parts missing',\n description:\n 'An AI build (a site, or a multi-part build) finished, but some of its parts failed on the platform’s side, so the customer got less than they asked for. Read which parts and why, then search the logs for the job id.',\n tier: 'should',\n category: 'ops',\n title: 'An AI {{kind}}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/operator-alerts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { OperatorAlertDefinition } from '@aglyn/aglyn/app-utils/operator-alerts'\nimport { AI_PLUGIN_ID } from './constants'\n\n/**\n * The AI plugin's operator alert (AGL-3377): the platform's provider account\n * refused its key or ran out of credit, so every AI feature on the install\n * fails. Provider-generic — each adapter classifies its own vendor's answer\n * as an `accountProblem`, and the runtime raises this with the provider's\n * label — so a self-hoster on any provider hears the same thing.\n */\nexport const AI_PROVIDER_UNAVAILABLE: OperatorAlertDefinition = {\n type: 'ai.providerUnavailable',\n pluginId: AI_PLUGIN_ID,\n label: 'Platform AI provider unavailable',\n description:\n 'The platform’s AI provider refused its key or ran out of credit, so every AI feature on the install is failing until the key or the balance is fixed.',\n tier: 'should',\n category: 'ops',\n title: '{{provider}} is refusing requests',\n body:\n 'The platform’s {{provider}} account refused a request because {{reason}}. AI features fail until it is fixed ({{keyEnv}}).',\n delivery: 'immediate',\n dedupeWindowMinutes: 6 * 60,\n defaultEnabled: true,\n}\n\n/**\n * An AI job failed on our side: a provider error, a step that produced\n * nothing usable, a build that delivered nothing. Not a model declining the\n * brief, and not a site with AI switched off — those are the customer's, and\n * the customer is told. Deduped per job kind, so an outage is one alert an\n * hour for each kind it breaks rather than one per job; the runner's own\n * words are in `{{error}}`, and every failure is in the log as `ai job failed`.\n */\nexport const AI_JOB_FAILED: OperatorAlertDefinition = {\n type: 'ai.jobFailed',\n pluginId: AI_PLUGIN_ID,\n label: 'An AI job failed',\n description:\n 'An AI job (a site, page, build, theme…) failed on the platform’s side, so a customer got nothing for their request. Read the error, then search the logs for “ai job failed” to see whether it is one job or every job of that kind.',\n tier: 'should',\n category: 'ops',\n title: 'An AI {{kind}} job failed',\n body:\n 'AI job {{jobId}} ({{kind}}) in workspace {{orgId}} failed at step {{step}}: {{error}}. The customer was told and given back the credits. Further {{kind}} failures within the hour are counted into the next alert.',\n link: '/admin/orgs/{{orgId}}',\n delivery: 'immediate',\n dedupeWindowMinutes: 60,\n defaultEnabled: true,\n}\n\n/**\n * A build finished with part of it missing: it delivered something, so the\n * job is `done`, but one or more of its items failed on our side (AGL-3683).\n * The customer has a site or build with holes in it and no failed job to\n * show for it, which is why it is its own alert. Items the model declined\n * are the customer's and do not count. Deduped per job kind, like\n * `ai.jobFailed`.\n */\nexport const AI_BUILD_PARTLY_FAILED: OperatorAlertDefinition = {\n type: 'ai.buildPartlyFailed',\n pluginId: AI_PLUGIN_ID,\n label: 'An AI build finished with parts missing',\n description:\n 'An AI build (a site, or a multi-part build) finished, but some of its parts failed on the platform’s side, so the customer got less than they asked for. Read which parts and why, then search the logs for the job id.',\n tier: 'should',\n category: 'ops',\n title: 'An AI {{kind}} job finished with {{failed}} of {{total}} parts failed',\n body:\n 'AI job {{jobId}} ({{kind}}) in workspace {{orgId}} finished, but {{failed}} of its {{total}} parts failed on our side: {{items}}. The customer was given back those parts’ credits. Further partial {{kind}} builds within the hour are counted into the next alert.',\n link: '/admin/orgs/{{orgId}}',\n delivery: 'immediate',\n dedupeWindowMinutes: 60,\n defaultEnabled: true,\n}\n"],"names":["AI_PLUGIN_ID","AI_PROVIDER_UNAVAILABLE","type","pluginId","label","description","tier","category","title","body","delivery","dedupeWindowMinutes","defaultEnabled","AI_JOB_FAILED","link","AI_BUILD_PARTLY_FAILED"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,YAAY,QAAQ,iBAAa;AAE1C;;;;;;CAMC,GACD,OAAO,MAAMC,0BAAmD;IAC9DC,MAAM;IACNC,UAAUH;IACVI,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFC,UAAU;IACVC,qBAAqB,IAAI;IACzBC,gBAAgB;AAClB,EAAC;AAED;;;;;;;CAOC,GACD,OAAO,MAAMC,gBAAyC;IACpDX,MAAM;IACNC,UAAUH;IACVI,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFK,MAAM;IACNJ,UAAU;IACVC,qBAAqB;IACrBC,gBAAgB;AAClB,EAAC;AAED;;;;;;;CAOC,GACD,OAAO,MAAMG,yBAAkD;IAC7Db,MAAM;IACNC,UAAUH;IACVI,OAAO;IACPC,aACE;IACFC,MAAM;IACNC,UAAU;IACVC,OAAO;IACPC,MACE;IACFK,MAAM;IACNJ,UAAU;IACVC,qBAAqB;IACrBC,gBAAgB;AAClB,EAAC"}
|
|
@@ -17,7 +17,8 @@ import { _ as _wrap_async_generator } from "@swc/helpers/_/_wrap_async_generator
|
|
|
17
17
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
18
|
* See the License for the specific language governing permissions and
|
|
19
19
|
* limitations under the License.
|
|
20
|
-
*/ import { aiModelIdsForProvider, estimateAiBilledUsd
|
|
20
|
+
*/ import { aiBilledRatesForModel, aiCatalogEntry, aiModelIdsForProvider, estimateAiBilledUsd } from "./catalog.js";
|
|
21
|
+
import { aiCacheWriteHourSurchargeUsd } from "./cache-write-hour.js";
|
|
21
22
|
import { AiBatchUnfinishedError, AiUpstreamError, aiRawOutputOf, aiStoppedAtCeiling, aiTokenCount, aiToolInputOf } from "./contract.js";
|
|
22
23
|
/**
|
|
23
24
|
* The Anthropic adapter (AGL-2903, behind the contract since AGL-2939):
|
|
@@ -100,15 +101,26 @@ const ANTHROPIC_VERSION = '2023-06-01';
|
|
|
100
101
|
// down, so an answer that reports none says nothing rather than zero.
|
|
101
102
|
const details = record['output_tokens_details'];
|
|
102
103
|
const thinking = details && typeof details === 'object' ? details['thinking_tokens'] : undefined;
|
|
104
|
+
// The writes split by lifetime (AGL-3830). Only an hour-long write is kept,
|
|
105
|
+
// because it is the one billed above the rate table's write rate, and only
|
|
106
|
+
// where there was one, so every five-minute exchange keeps its shape.
|
|
107
|
+
const creation = record['cache_creation'];
|
|
108
|
+
const hour = creation && typeof creation === 'object' ? aiTokenCount(creation['ephemeral_1h_input_tokens']) : 0;
|
|
103
109
|
return _extends({
|
|
104
110
|
inputTokens: aiTokenCount(record['input_tokens']),
|
|
105
111
|
outputTokens: aiTokenCount(record['output_tokens']),
|
|
106
112
|
cacheReadTokens: aiTokenCount(record['cache_read_input_tokens']),
|
|
107
113
|
cacheWriteTokens: aiTokenCount(record['cache_creation_input_tokens'])
|
|
108
|
-
},
|
|
114
|
+
}, hour > 0 ? {
|
|
115
|
+
cacheWriteHourTokens: hour
|
|
116
|
+
} : {}, thinking === undefined ? {} : {
|
|
109
117
|
thinkingTokens: aiTokenCount(thinking)
|
|
110
118
|
});
|
|
111
119
|
}
|
|
120
|
+
/** What an exchange draws, its hour-long cache writes included (AGL-3830). */ function anthropicBilledUsd(usage, model) {
|
|
121
|
+
const usd = estimateAiBilledUsd(usage, model) + aiCacheWriteHourSurchargeUsd(usage, aiBilledRatesForModel(model));
|
|
122
|
+
return Math.round(usd * 1000000) / 1000000;
|
|
123
|
+
}
|
|
112
124
|
/**
|
|
113
125
|
* How "thinking off" is said to a model that refuses `{type: "disabled"}`.
|
|
114
126
|
* Claude Sonnet 5.5 turns it off only through `between_tools` (accepted at
|
|
@@ -297,7 +309,7 @@ async function complete(input) {
|
|
|
297
309
|
};
|
|
298
310
|
});
|
|
299
311
|
const usage = anthropicUsageFrom(payload == null ? void 0 : payload.usage);
|
|
300
|
-
const estCostUsd = Math.round(
|
|
312
|
+
const estCostUsd = Math.round(anthropicBilledUsd(usage, model) * priceFactor * 1000000) / 1000000;
|
|
301
313
|
const stopReason = typeof (payload == null ? void 0 : payload.stop_reason) === 'string' && payload.stop_reason ? payload.stop_reason : null;
|
|
302
314
|
if (stopReason === 'refusal') {
|
|
303
315
|
return {
|
|
@@ -379,6 +391,7 @@ async function stream(input) {
|
|
|
379
391
|
usage.inputTokens = start.inputTokens;
|
|
380
392
|
usage.cacheReadTokens = start.cacheReadTokens;
|
|
381
393
|
usage.cacheWriteTokens = start.cacheWriteTokens;
|
|
394
|
+
if (start.cacheWriteHourTokens) usage.cacheWriteHourTokens = start.cacheWriteHourTokens;
|
|
382
395
|
break;
|
|
383
396
|
}
|
|
384
397
|
case 'content_block_start':
|
|
@@ -474,7 +487,7 @@ async function stream(input) {
|
|
|
474
487
|
yield _extends({
|
|
475
488
|
type: 'done',
|
|
476
489
|
usage,
|
|
477
|
-
estCostUsd:
|
|
490
|
+
estCostUsd: anthropicBilledUsd(usage, model),
|
|
478
491
|
stopReason
|
|
479
492
|
}, aiStoppedAtCeiling(stopReason) ? {
|
|
480
493
|
rawOutput: aiRawOutputOf(written.join(''))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/anthropic.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { aiModelIdsForProvider, estimateAiBilledUsd, aiCatalogEntry } from './catalog'\nimport {\n AiBatchUnfinishedError,\n AiUpstreamError,\n type AiAccountProblem,\n type AiBatchOptions,\n type AiEffort,\n aiRawOutputOf,\n aiStoppedAtCeiling,\n aiTokenCount,\n aiToolInputOf,\n type AiMessage,\n type AiModelDescriptor,\n type AiProvider,\n type AiProviderRequest,\n type AiResult,\n type AiStreamEvent,\n type AiToolSchemaLimits,\n type AiToolUse,\n type AiUsage,\n} from './contract'\n\n/**\n * The Anthropic adapter (AGL-2903, behind the contract since AGL-2939):\n * the Messages API over raw `fetch`. No provider SDK is installed, and the\n * meter prices by model id from the catalog, so nothing an SDK would add\n * is used here. This file and the catalog are the only places in the\n * plugin that name the vendor's URL, header or model ids.\n *\n * ── Errors (AGL-2815) ─────────────────────────────────────────────────────\n *\n * Provider error text describes OUR account with the vendor — a key, a rate\n * limit, a balance — and names the vendor to a white-label org's users. It\n * never leaves this module as a message: a non-2xx answer becomes an\n * `AiUpstreamError` carrying the status, whether the failure is retryable\n * and a fixed customer-safe sentence; the provider's body goes to\n * `console.error` beside the request id, where an operator can find it.\n * A `refusal` stop is not an error at all — the tokens were spent and the\n * model answered — so it comes back as a typed `AiRefusal` result.\n */\n\nexport const ANTHROPIC_PROVIDER_ID = 'anthropic'\nexport const ANTHROPIC_API_KEY_ENV = 'ANTHROPIC_API_KEY'\nconst ANTHROPIC_HOST = 'api.anthropic.com'\nconst ANTHROPIC_URL = `https://${ANTHROPIC_HOST}/v1/messages`\nconst ANTHROPIC_BATCHES_URL = `https://${ANTHROPIC_HOST}/v1/messages/batches`\nconst ANTHROPIC_VERSION = '2023-06-01'\n\n/** The Messages API accepts at most this many `cache_control` markers. */\nexport const ANTHROPIC_MAX_CACHE_BREAKPOINTS = 4\n\n/**\n * What the Messages API compiles of one request's strict schemas, as its\n * structured-outputs documentation states the limits: 20 strict tools, 24\n * optional parameters and 16 parameters with union types, each a total over\n * every strict schema the request carries. Past any of them the request is\n * refused with a 400 before the model runs.\n *\n * `compiledSchemaBytes` is not one of those. The API also refuses a request\n * whose schemas compile to too large a grammar — \"Simplify your tool schemas\n * or reduce the number of strict tools\" — and states no size for it, so this\n * bound is measured rather than read (AGL-3096). Its evidence is two live\n * requests for the automation draft, the largest strict schema anything here\n * sends:\n *\n * - 8,757 bytes, refused for grammar size (`req_011CfC6Fif9iJvmKPTRgtLdp`,\n * 2026-09-19), with 1 strict tool, 0 optional parameters and 2 unions:\n * every published count was green;\n * - 3,807 bytes, served (`req_011CfELn62WVLeaNzwK4TWgb`, 2026-09-20), the\n * same tool with the guard lifted out of the steps' union and one variant\n * per set of fields.\n *\n * Everything else the plugin sends is smaller still, the next largest being\n * the theme tool at 2,410 bytes. The bound sits at what has been served, so a\n * schema that grows past it has to be proved live before the bound moves.\n */\nexport const ANTHROPIC_TOOL_SCHEMA_LIMITS: AiToolSchemaLimits = {\n strictTools: 20,\n optionalParameters: 24,\n unionParameters: 16,\n compiledSchemaBytes: 3_807,\n}\n\n/**\n * Whether a provider failure is the kind that clears on its own. Matched on\n * the status AND the error type, because the in-stream `error` event has no\n * status to go by.\n */\nexport function anthropicFailureIsRetryable(\n status: number | null,\n errorType: string | undefined,\n): boolean {\n return (\n status === 429 ||\n status === 529 ||\n errorType === 'rate_limit_error' ||\n errorType === 'overloaded_error'\n )\n}\n\n/**\n * Whether a failure is about the platform's Anthropic account rather than the\n * request (AGL-3377): a refused key, or a balance too low to answer.\n */\nexport function anthropicAccountProblem(\n status: number | null,\n error: { type?: string; message?: string } | null | undefined,\n): AiAccountProblem | null {\n if (status === 401 || status === 403) return 'credentials'\n if (error?.type === 'authentication_error' || error?.type === 'permission_error') {\n return 'credentials'\n }\n if (error?.type === 'billing_error' || /credit balance/i.test(error?.message ?? '')) {\n return 'credit'\n }\n return null\n}\n\n/** Anthropic's `usage` object, in the meter's shape. Missing fields read 0. */\nexport function anthropicUsageFrom(usage: unknown): AiUsage {\n const record = (usage ?? {}) as Record<string, unknown>\n // Part of `output_tokens`, and carried only where the response breaks them\n // down, so an answer that reports none says nothing rather than zero.\n const details = record['output_tokens_details']\n const thinking =\n details && typeof details === 'object'\n ? (details as Record<string, unknown>)['thinking_tokens']\n : undefined\n return {\n inputTokens: aiTokenCount(record['input_tokens']),\n outputTokens: aiTokenCount(record['output_tokens']),\n cacheReadTokens: aiTokenCount(record['cache_read_input_tokens']),\n cacheWriteTokens: aiTokenCount(record['cache_creation_input_tokens']),\n ...(thinking === undefined ? {} : { thinkingTokens: aiTokenCount(thinking) }),\n }\n}\n\n/**\n * How \"thinking off\" is said to a model that refuses `{type: \"disabled\"}`.\n * Claude Sonnet 5.5 turns it off only through `between_tools` (accepted at\n * effort high or below, which is every effort the contract names); Claude\n * Opus 5.5 cannot turn it off at all, so the field is left out and the\n * model's adaptive thinking runs. Every other model takes `disabled`.\n */\nconst ANTHROPIC_THINKING_OFF: Readonly<Record<string, 'between_tools' | 'omit'>> = {\n 'claude-sonnet-5-5': 'between_tools',\n 'claude-opus-5-5': 'omit',\n}\n\n/**\n * The effort a request that names none is sent, for a model whose own\n * default is lower than its siblings': Claude Opus 5.5 defaults to medium,\n * where Claude Opus 5 defaults to high.\n */\nconst ANTHROPIC_DEFAULT_EFFORT: Readonly<Record<string, AiEffort>> = {\n 'claude-opus-5-5': 'high',\n}\n\n/**\n * The Messages API body for a request. Exported so a spec can assert the\n * wire shape without a network. The cache-prefix rule is enforced by the\n * runtime before the request reaches any adapter; this maps the surviving\n * breakpoints onto `cache_control` and refuses only the count the API\n * itself refuses.\n */\nexport function buildAnthropicRequestBody(\n input: Omit<AiProviderRequest, 'apiKey' | 'signal'> & { stream: boolean },\n): Record<string, unknown> {\n const conversation = Boolean(input.cacheConversation) && input.messages.length > 0\n const breakpoints = input.system.filter((block) => block.cacheBreakpoint).length + (conversation ? 1 : 0)\n if (breakpoints > ANTHROPIC_MAX_CACHE_BREAKPOINTS) {\n throw new Error(\n `${breakpoints} cache breakpoints; the Messages API allows ${ANTHROPIC_MAX_CACHE_BREAKPOINTS}`,\n )\n }\n // One lifetime for every breakpoint: the API refuses a longer-lived entry\n // after a shorter one, and a request asks for one or the other.\n const cacheControl = input.cacheTtl === '1h' ? { type: 'ephemeral', ttl: '1h' } : { type: 'ephemeral' }\n const system = input.system.map((block) => ({\n type: 'text',\n text: block.text,\n ...(block.cacheBreakpoint ? { cache_control: cacheControl } : {}),\n }))\n const tools = input.tools?.map((tool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: { ...tool.inputSchema, additionalProperties: false },\n strict: true,\n }))\n // A model that rejects an explicit thinking setting gets none, whatever\n // the door asked for, and no effort either: effort rides the same rule\n // (see the contract), and the catalog says which models those are.\n const settable = aiCatalogEntry(input.model)?.capabilities.thinking !== false\n const thinking = settable ? input.thinking : undefined\n const effort = settable ? (input.effort ?? ANTHROPIC_DEFAULT_EFFORT[input.model]) : undefined\n const thinkingOff = ANTHROPIC_THINKING_OFF[input.model] ?? 'disabled'\n return {\n model: input.model,\n max_tokens: input.maxTokens,\n ...(input.stream ? { stream: true } : {}),\n ...(thinking === 'off'\n ? thinkingOff === 'omit'\n ? {}\n : { thinking: { type: thinkingOff } }\n : thinking === 'adaptive'\n ? { thinking: { type: 'adaptive' } }\n : {}),\n ...(effort ? { output_config: { effort } } : {}),\n ...(system.length ? { system } : {}),\n messages: input.messages.map((message, index) => ({\n role: message.role,\n content: conversation\n ? anthropicBlocksOf(message, index === input.messages.length - 1 ? cacheControl : null)\n : anthropicContentOf(message),\n })),\n ...(tools?.length ? { tools, tool_choice: { type: 'auto' } } : {}),\n }\n}\n\n/**\n * A turn's content in the Messages API's shape: a text-only turn as the\n * string it is, and a turn with pictures as content blocks in order, each\n * picture a base64 image block (AGL-2916).\n */\nfunction anthropicContentOf(message: AiMessage): string | Array<Record<string, unknown>> {\n if (typeof message.content === 'string') return message.content\n return message.content.map((part) =>\n part.type === 'image'\n ? { type: 'image', source: { type: 'base64', media_type: part.mediaType, data: part.data } }\n : { type: 'text', text: part.text },\n )\n}\n\n/**\n * A turn as content blocks whatever its shape, the last block carrying the\n * conversation's breakpoint when it is the request's last turn (AGL-3809).\n * Every turn of a request that caches its conversation is sent as blocks, so\n * a turn reads the same on the call that marked it and on every call after,\n * where it is no longer last.\n */\nfunction anthropicBlocksOf(\n message: AiMessage,\n cacheControl: Record<string, unknown> | null,\n): Array<Record<string, unknown>> {\n const content = anthropicContentOf(message)\n const blocks = typeof content === 'string' ? [{ type: 'text', text: content }] : content\n if (!cacheControl || !blocks.length) return blocks\n const last = blocks.length - 1\n return blocks.map((block, index) => (index === last ? { ...block, cache_control: cacheControl } : block))\n}\n\n/** The provider's request id, when it sent one — the log's join key. */\nfunction requestIdOf(response: Response): string | null {\n return response.headers?.get?.('request-id') ?? null\n}\n\nasync function send(\n input: AiProviderRequest,\n stream: boolean,\n): Promise<Response> {\n const body = buildAnthropicRequestBody({ ...input, stream })\n const response = await fetch(ANTHROPIC_URL, {\n method: 'POST',\n headers: {\n 'x-api-key': input.apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n ...(input.signal ? { signal: input.signal } : {}),\n })\n if (!response.ok || (stream && !response.body)) {\n const requestId = requestIdOf(response)\n const payload = (await response.json().catch(() => null)) as {\n error?: { type?: string; message?: string }\n } | null\n console.error('ai upstream error', {\n provider: ANTHROPIC_PROVIDER_ID,\n status: response.status,\n requestId,\n model: input.model,\n error: payload?.error ?? null,\n })\n throw new AiUpstreamError(\n response.status,\n anthropicFailureIsRetryable(response.status, payload?.error?.type),\n requestId,\n anthropicAccountProblem(response.status, payload?.error),\n )\n }\n return response\n}\n\nasync function complete(input: AiProviderRequest): Promise<AiResult> {\n const response = await send(input, false)\n return anthropicResultFrom(await response.json(), input.model)\n}\n\n/**\n * A Messages API answer as the contract's result: the shape `complete` reads\n * off the wire and a batch's succeeded entry carries. `priceFactor` is what a\n * discount leaves of the price (a batch's half).\n */\nexport function anthropicResultFrom(raw: unknown, model: string, priceFactor = 1): AiResult {\n const payload = raw as {\n content?: Array<Record<string, unknown>>\n stop_reason?: unknown\n usage?: unknown\n } | null\n const blocks = Array.isArray(payload?.content) ? payload.content : []\n const text = blocks\n .filter((block) => block?.['type'] === 'text')\n .map((block) => String(block['text'] ?? ''))\n .join('')\n const toolUse: AiToolUse[] = blocks\n .filter((block) => block?.['type'] === 'tool_use')\n .map((block) => ({\n name: String(block['name'] ?? ''),\n input: aiToolInputOf(block['input']),\n }))\n const usage = anthropicUsageFrom(payload?.usage)\n const estCostUsd = Math.round(estimateAiBilledUsd(usage, model) * priceFactor * 1_000_000) / 1_000_000\n const stopReason =\n typeof payload?.stop_reason === 'string' && payload.stop_reason\n ? payload.stop_reason\n : null\n if (stopReason === 'refusal') {\n return { kind: 'refusal', text, usage, estCostUsd, stopReason }\n }\n return {\n kind: 'completion',\n text,\n toolUse,\n usage,\n estCostUsd,\n stopReason,\n // The blocks as the provider answered them, for a call its ceiling cut\n // off (AGL-3143): `toolUse` holds only what still parsed out of them.\n ...(aiStoppedAtCeiling(stopReason) ? { rawOutput: aiRawOutputOf(blocks) } : {}),\n }\n}\n\nasync function stream(input: AiProviderRequest): Promise<AsyncIterable<AiStreamEvent>> {\n const response = await send(input, true)\n return streamEvents(\n response.body as ReadableStream<Uint8Array>,\n input.model,\n requestIdOf(response),\n )\n}\n\n/**\n * Anthropic's SSE stream as contract events. Usage is taken from\n * `message_start` (input and cache figures) and `message_delta` (the\n * output count, and any cumulative input figures a newer API sends); the\n * `done` event carries whatever was known when the body ended, so a stream\n * cut short still meters what it cost.\n */\nasync function* streamEvents(\n body: ReadableStream<Uint8Array>,\n model: string,\n requestId: string | null,\n): AsyncGenerator<AiStreamEvent> {\n const decoder = new TextDecoder()\n const reader = body.getReader()\n const usage: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n }\n let stopReason: string | null = null\n /** Tool-use blocks in flight, by content index; input arrives in pieces. */\n const tools = new Map<number, { name: string; json: string }>()\n /**\n * What each tool call was actually written in, in the order its block\n * opened (AGL-3143). The `tool` events below carry only as much of these\n * as `aiToolInputOf` could still parse, so for a call the ceiling cut off\n * this is the one place the emitted bytes survive.\n */\n const written: string[] = []\n let buffer = ''\n let drained = false\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) {\n drained = true\n break\n }\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n let event: Record<string, unknown>\n try {\n event = JSON.parse(line.slice('data: '.length))\n } catch {\n continue\n }\n const index = Number(event['index'])\n switch (event['type']) {\n case 'message_start': {\n const start = anthropicUsageFrom(\n (event['message'] as { usage?: unknown } | undefined)?.usage,\n )\n usage.inputTokens = start.inputTokens\n usage.cacheReadTokens = start.cacheReadTokens\n usage.cacheWriteTokens = start.cacheWriteTokens\n break\n }\n case 'content_block_start': {\n const block = event['content_block'] as\n | { type?: string; name?: string }\n | undefined\n if (block?.type === 'tool_use') {\n tools.set(index, { name: String(block.name ?? ''), json: '' })\n }\n break\n }\n case 'content_block_delta': {\n const delta = event['delta'] as\n | { type?: string; text?: string; partial_json?: string }\n | undefined\n if (delta?.type === 'text_delta' && delta.text) {\n yield { type: 'delta', text: delta.text }\n } else if (delta?.type === 'input_json_delta') {\n const tool = tools.get(index)\n if (tool) tool.json += delta.partial_json ?? ''\n }\n break\n }\n case 'content_block_stop': {\n const tool = tools.get(index)\n if (tool) {\n tools.delete(index)\n written.push(tool.json)\n yield { type: 'tool', name: tool.name, input: aiToolInputOf(tool.json) }\n }\n break\n }\n case 'message_delta': {\n const delta = anthropicUsageFrom(event['usage'])\n usage.outputTokens = delta.outputTokens || usage.outputTokens\n usage.inputTokens = delta.inputTokens || usage.inputTokens\n usage.cacheReadTokens = delta.cacheReadTokens || usage.cacheReadTokens\n usage.cacheWriteTokens = delta.cacheWriteTokens || usage.cacheWriteTokens\n // Kept where the breakdown is reported and left absent where it\n // is not (AGL-3143), never set to a zero the provider did not\n // send. A stream needs it for the reason a completion does: it\n // is the figure that accounts for a spent ceiling first, and it\n // arrives in the same usage object as the four above.\n if (delta.thinkingTokens !== undefined) usage.thinkingTokens = delta.thinkingTokens\n const reason = (event['delta'] as { stop_reason?: unknown } | undefined)\n ?.stop_reason\n if (typeof reason === 'string' && reason) stopReason = reason\n break\n }\n case 'error': {\n const error = event['error'] as\n | { type?: string; message?: string }\n | undefined\n console.error('ai upstream stream error', {\n provider: ANTHROPIC_PROVIDER_ID,\n requestId,\n model,\n error: error ?? null,\n })\n yield { type: 'error', retryable: anthropicFailureIsRetryable(null, error?.type) }\n break\n }\n default:\n break\n }\n }\n }\n } finally {\n // A consumer that stops iterating early — a client that hung up — must\n // not leave the provider generating into a socket nobody reads: cancel\n // tells the upstream to stop, and the tokens it has already produced are\n // the only ones billed.\n if (!drained) await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n // A message the ceiling ends mid-block never reaches that block's stop\n // event, so what it had written is still held in flight.\n for (const tool of tools.values()) written.push(tool.json)\n yield {\n type: 'done',\n usage,\n estCostUsd: estimateAiBilledUsd(usage, model),\n stopReason,\n // What the model emitted into its tool calls, for a stream its ceiling\n // cut off (AGL-3143): the `tool` events carry only what parsed out.\n ...(aiStoppedAtCeiling(stopReason) ? { rawOutput: aiRawOutputOf(written.join('')) } : {}),\n }\n}\n\n/** What a batch's discount leaves of a request's price. */\nexport const ANTHROPIC_BATCH_PRICE_FACTOR = 0.5\n\nfunction batchHeaders(apiKey: string): Record<string, string> {\n return {\n 'x-api-key': apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n 'Content-Type': 'application/json',\n }\n}\n\n/** A batch call that did not answer 2xx, as the contract's error. */\nasync function batchFailure(response: Response, model: string): Promise<AiUpstreamError> {\n const requestId = requestIdOf(response)\n const payload = (await response.json().catch(() => null)) as {\n error?: { type?: string; message?: string }\n } | null\n console.error('ai upstream batch error', {\n provider: ANTHROPIC_PROVIDER_ID,\n status: response.status,\n requestId,\n model,\n error: payload?.error ?? null,\n })\n return new AiUpstreamError(\n response.status,\n anthropicFailureIsRetryable(response.status, payload?.error?.type),\n requestId,\n anthropicAccountProblem(response.status, payload?.error),\n )\n}\n\n/** The batch as its create and poll calls describe it. */\ninterface AnthropicBatch {\n id: string\n processing_status?: string\n results_url?: string | null\n}\n\n/** Results a batch can end a request with that answer nothing yet: asked again, not failed (AGL-3781). */\nconst ANTHROPIC_BATCH_UNFINISHED = new Set(['canceled', 'expired'])\n\n/**\n * Many requests through the Message Batches API (AGL-3660): created as one\n * batch, polled until it has ended, its results read by `custom_id` — they\n * come back in any order — and each turned into the result `complete` would\n * have returned, at half the price. Development only: the live evals' batch\n * mode is the one caller, and no door waits minutes for an answer.\n *\n * A batch still processing when its wait runs out is canceled, and read once\n * the cancel has ended it (AGL-3781): a cancel keeps every answer the batch\n * already has, so those come back as results, and only the requests it never\n * reached come back as `AiBatchUnfinishedError`, for the caller to ask on\n * their own. Throwing away a round's paid-for answers because one batch was\n * slow is how a whole golden run lost its grades.\n */\nasync function completeBatch(\n requests: readonly AiProviderRequest[],\n options: AiBatchOptions = {},\n): Promise<Array<AiResult | Error>> {\n if (!requests.length) return []\n const apiKey = requests[0].apiKey\n const model = requests[0].model\n const pollMs = options.pollMs ?? 10_000\n const timeoutMs = options.timeoutMs ?? 60 * 60_000\n const cancelGraceMs = options.cancelGraceMs ?? 10 * 60_000\n const unfinished = (id: string, reason: string) => requests.map(() => new AiBatchUnfinishedError(id, reason))\n const created = await fetch(ANTHROPIC_BATCHES_URL, {\n method: 'POST',\n headers: batchHeaders(apiKey),\n body: JSON.stringify({\n requests: requests.map((request, index) => {\n const { apiKey: _key, signal: _signal, ...rest } = request\n return { custom_id: `r${index}`, params: buildAnthropicRequestBody({ ...rest, stream: false }) }\n }),\n }),\n })\n if (!created.ok) throw await batchFailure(created, model)\n let batch = (await created.json()) as AnthropicBatch\n const deadline = Date.now() + timeoutMs\n let cancelDeadline: number | null = null\n while (batch.processing_status !== 'ended') {\n if (cancelDeadline === null && Date.now() >= deadline) {\n console.warn(`[ai] batch ${batch.id} has not ended after ${timeoutMs} ms; canceling it to read what it answered`)\n await fetch(`${ANTHROPIC_BATCHES_URL}/${batch.id}/cancel`, { method: 'POST', headers: batchHeaders(apiKey) }).catch(\n () => undefined,\n )\n cancelDeadline = Date.now() + cancelGraceMs\n }\n // A cancel that never ends leaves no results to read; every request goes back unanswered.\n if (cancelDeadline !== null && Date.now() >= cancelDeadline) return unfinished(batch.id, 'cancel did not end')\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n const polled = await fetch(`${ANTHROPIC_BATCHES_URL}/${batch.id}`, { headers: batchHeaders(apiKey) })\n if (!polled.ok) throw await batchFailure(polled, model)\n batch = (await polled.json()) as AnthropicBatch\n }\n if (!batch.results_url) {\n if (cancelDeadline !== null) return unfinished(batch.id, 'canceled with no results')\n throw new Error(`batch ${batch.id} ended with no results`)\n }\n const results = await fetch(batch.results_url, { headers: batchHeaders(apiKey) })\n if (!results.ok) throw await batchFailure(results, model)\n const byId = new Map<string, { type?: string; message?: unknown; error?: unknown }>()\n for (const line of (await results.text()).split('\\n')) {\n if (!line.trim()) continue\n const entry = JSON.parse(line) as { custom_id: string; result?: { type?: string; message?: unknown; error?: unknown } }\n byId.set(entry.custom_id, entry.result ?? {})\n }\n return requests.map((request, index) => {\n const result = byId.get(`r${index}`)\n if (result?.type === 'succeeded') {\n return anthropicResultFrom(result.message, request.model, ANTHROPIC_BATCH_PRICE_FACTOR)\n }\n const type = result?.type ?? 'missing'\n if (ANTHROPIC_BATCH_UNFINISHED.has(type) || type === 'missing') return new AiBatchUnfinishedError(batch.id, type)\n console.error('ai batch request not answered', {\n provider: ANTHROPIC_PROVIDER_ID,\n batchId: batch.id,\n customId: `r${index}`,\n result: type,\n error: result?.error ?? null,\n })\n return new AiUpstreamError(null, type === 'errored', batch.id, null)\n })\n}\n\nexport const anthropicProvider: AiProvider = {\n id: ANTHROPIC_PROVIDER_ID,\n label: 'Anthropic',\n apiKeyEnv: ANTHROPIC_API_KEY_ENV,\n readApiKey: () => process.env.ANTHROPIC_API_KEY?.trim() || undefined,\n endpointHost: ANTHROPIC_HOST,\n toolSchemaLimits: ANTHROPIC_TOOL_SCHEMA_LIMITS,\n models(): readonly AiModelDescriptor[] {\n return aiModelIdsForProvider(ANTHROPIC_PROVIDER_ID)\n .map((id) => aiCatalogEntry(id))\n .filter((entry): entry is NonNullable<typeof entry> => Boolean(entry))\n },\n complete,\n stream,\n completeBatch,\n}\n"],"names":["aiModelIdsForProvider","estimateAiBilledUsd","aiCatalogEntry","AiBatchUnfinishedError","AiUpstreamError","aiRawOutputOf","aiStoppedAtCeiling","aiTokenCount","aiToolInputOf","ANTHROPIC_PROVIDER_ID","ANTHROPIC_API_KEY_ENV","ANTHROPIC_HOST","ANTHROPIC_URL","ANTHROPIC_BATCHES_URL","ANTHROPIC_VERSION","ANTHROPIC_MAX_CACHE_BREAKPOINTS","ANTHROPIC_TOOL_SCHEMA_LIMITS","strictTools","optionalParameters","unionParameters","compiledSchemaBytes","anthropicFailureIsRetryable","status","errorType","anthropicAccountProblem","error","type","test","message","anthropicUsageFrom","usage","record","details","thinking","undefined","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","thinkingTokens","ANTHROPIC_THINKING_OFF","ANTHROPIC_DEFAULT_EFFORT","buildAnthropicRequestBody","input","conversation","Boolean","cacheConversation","messages","length","breakpoints","system","filter","block","cacheBreakpoint","Error","cacheControl","cacheTtl","ttl","map","text","cache_control","tools","tool","name","description","input_schema","inputSchema","additionalProperties","strict","settable","model","capabilities","effort","thinkingOff","max_tokens","maxTokens","stream","output_config","index","role","content","anthropicBlocksOf","anthropicContentOf","tool_choice","part","source","media_type","mediaType","data","blocks","last","requestIdOf","response","headers","get","send","body","fetch","method","apiKey","JSON","stringify","signal","ok","payload","requestId","json","catch","console","provider","complete","anthropicResultFrom","raw","priceFactor","Array","isArray","String","join","toolUse","estCostUsd","Math","round","stopReason","stop_reason","kind","rawOutput","streamEvents","decoder","TextDecoder","reader","getReader","Map","written","buffer","drained","lines","done","value","read","decode","split","pop","line","startsWith","event","parse","slice","Number","start","set","delta","partial_json","delete","push","reason","retryable","cancel","releaseLock","values","ANTHROPIC_BATCH_PRICE_FACTOR","batchHeaders","batchFailure","ANTHROPIC_BATCH_UNFINISHED","Set","completeBatch","requests","options","pollMs","timeoutMs","cancelGraceMs","unfinished","id","created","request","_key","_signal","rest","custom_id","params","batch","deadline","Date","now","cancelDeadline","processing_status","warn","Promise","resolve","setTimeout","polled","results_url","results","byId","entry","trim","result","has","batchId","customId","anthropicProvider","label","apiKeyEnv","readApiKey","process","env","ANTHROPIC_API_KEY","endpointHost","toolSchemaLimits","models"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/anthropic.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { aiBilledRatesForModel, aiCatalogEntry, aiModelIdsForProvider, estimateAiBilledUsd } from './catalog'\nimport { aiCacheWriteHourSurchargeUsd } from './cache-write-hour'\nimport {\n AiBatchUnfinishedError,\n AiUpstreamError,\n type AiAccountProblem,\n type AiBatchOptions,\n type AiEffort,\n aiRawOutputOf,\n aiStoppedAtCeiling,\n aiTokenCount,\n aiToolInputOf,\n type AiMessage,\n type AiModelDescriptor,\n type AiProvider,\n type AiProviderRequest,\n type AiResult,\n type AiStreamEvent,\n type AiToolSchemaLimits,\n type AiToolUse,\n type AiUsage,\n} from './contract'\n\n/**\n * The Anthropic adapter (AGL-2903, behind the contract since AGL-2939):\n * the Messages API over raw `fetch`. No provider SDK is installed, and the\n * meter prices by model id from the catalog, so nothing an SDK would add\n * is used here. This file and the catalog are the only places in the\n * plugin that name the vendor's URL, header or model ids.\n *\n * ── Errors (AGL-2815) ─────────────────────────────────────────────────────\n *\n * Provider error text describes OUR account with the vendor — a key, a rate\n * limit, a balance — and names the vendor to a white-label org's users. It\n * never leaves this module as a message: a non-2xx answer becomes an\n * `AiUpstreamError` carrying the status, whether the failure is retryable\n * and a fixed customer-safe sentence; the provider's body goes to\n * `console.error` beside the request id, where an operator can find it.\n * A `refusal` stop is not an error at all — the tokens were spent and the\n * model answered — so it comes back as a typed `AiRefusal` result.\n */\n\nexport const ANTHROPIC_PROVIDER_ID = 'anthropic'\nexport const ANTHROPIC_API_KEY_ENV = 'ANTHROPIC_API_KEY'\nconst ANTHROPIC_HOST = 'api.anthropic.com'\nconst ANTHROPIC_URL = `https://${ANTHROPIC_HOST}/v1/messages`\nconst ANTHROPIC_BATCHES_URL = `https://${ANTHROPIC_HOST}/v1/messages/batches`\nconst ANTHROPIC_VERSION = '2023-06-01'\n\n/** The Messages API accepts at most this many `cache_control` markers. */\nexport const ANTHROPIC_MAX_CACHE_BREAKPOINTS = 4\n\n/**\n * What the Messages API compiles of one request's strict schemas, as its\n * structured-outputs documentation states the limits: 20 strict tools, 24\n * optional parameters and 16 parameters with union types, each a total over\n * every strict schema the request carries. Past any of them the request is\n * refused with a 400 before the model runs.\n *\n * `compiledSchemaBytes` is not one of those. The API also refuses a request\n * whose schemas compile to too large a grammar — \"Simplify your tool schemas\n * or reduce the number of strict tools\" — and states no size for it, so this\n * bound is measured rather than read (AGL-3096). Its evidence is two live\n * requests for the automation draft, the largest strict schema anything here\n * sends:\n *\n * - 8,757 bytes, refused for grammar size (`req_011CfC6Fif9iJvmKPTRgtLdp`,\n * 2026-09-19), with 1 strict tool, 0 optional parameters and 2 unions:\n * every published count was green;\n * - 3,807 bytes, served (`req_011CfELn62WVLeaNzwK4TWgb`, 2026-09-20), the\n * same tool with the guard lifted out of the steps' union and one variant\n * per set of fields.\n *\n * Everything else the plugin sends is smaller still, the next largest being\n * the theme tool at 2,410 bytes. The bound sits at what has been served, so a\n * schema that grows past it has to be proved live before the bound moves.\n */\nexport const ANTHROPIC_TOOL_SCHEMA_LIMITS: AiToolSchemaLimits = {\n strictTools: 20,\n optionalParameters: 24,\n unionParameters: 16,\n compiledSchemaBytes: 3_807,\n}\n\n/**\n * Whether a provider failure is the kind that clears on its own. Matched on\n * the status AND the error type, because the in-stream `error` event has no\n * status to go by.\n */\nexport function anthropicFailureIsRetryable(\n status: number | null,\n errorType: string | undefined,\n): boolean {\n return (\n status === 429 ||\n status === 529 ||\n errorType === 'rate_limit_error' ||\n errorType === 'overloaded_error'\n )\n}\n\n/**\n * Whether a failure is about the platform's Anthropic account rather than the\n * request (AGL-3377): a refused key, or a balance too low to answer.\n */\nexport function anthropicAccountProblem(\n status: number | null,\n error: { type?: string; message?: string } | null | undefined,\n): AiAccountProblem | null {\n if (status === 401 || status === 403) return 'credentials'\n if (error?.type === 'authentication_error' || error?.type === 'permission_error') {\n return 'credentials'\n }\n if (error?.type === 'billing_error' || /credit balance/i.test(error?.message ?? '')) {\n return 'credit'\n }\n return null\n}\n\n/** Anthropic's `usage` object, in the meter's shape. Missing fields read 0. */\nexport function anthropicUsageFrom(usage: unknown): AiUsage {\n const record = (usage ?? {}) as Record<string, unknown>\n // Part of `output_tokens`, and carried only where the response breaks them\n // down, so an answer that reports none says nothing rather than zero.\n const details = record['output_tokens_details']\n const thinking =\n details && typeof details === 'object'\n ? (details as Record<string, unknown>)['thinking_tokens']\n : undefined\n // The writes split by lifetime (AGL-3830). Only an hour-long write is kept,\n // because it is the one billed above the rate table's write rate, and only\n // where there was one, so every five-minute exchange keeps its shape.\n const creation = record['cache_creation']\n const hour =\n creation && typeof creation === 'object'\n ? aiTokenCount((creation as Record<string, unknown>)['ephemeral_1h_input_tokens'])\n : 0\n return {\n inputTokens: aiTokenCount(record['input_tokens']),\n outputTokens: aiTokenCount(record['output_tokens']),\n cacheReadTokens: aiTokenCount(record['cache_read_input_tokens']),\n cacheWriteTokens: aiTokenCount(record['cache_creation_input_tokens']),\n ...(hour > 0 ? { cacheWriteHourTokens: hour } : {}),\n ...(thinking === undefined ? {} : { thinkingTokens: aiTokenCount(thinking) }),\n }\n}\n\n/** What an exchange draws, its hour-long cache writes included (AGL-3830). */\nfunction anthropicBilledUsd(usage: AiUsage, model: string): number {\n const usd = estimateAiBilledUsd(usage, model) + aiCacheWriteHourSurchargeUsd(usage, aiBilledRatesForModel(model))\n return Math.round(usd * 1_000_000) / 1_000_000\n}\n\n/**\n * How \"thinking off\" is said to a model that refuses `{type: \"disabled\"}`.\n * Claude Sonnet 5.5 turns it off only through `between_tools` (accepted at\n * effort high or below, which is every effort the contract names); Claude\n * Opus 5.5 cannot turn it off at all, so the field is left out and the\n * model's adaptive thinking runs. Every other model takes `disabled`.\n */\nconst ANTHROPIC_THINKING_OFF: Readonly<Record<string, 'between_tools' | 'omit'>> = {\n 'claude-sonnet-5-5': 'between_tools',\n 'claude-opus-5-5': 'omit',\n}\n\n/**\n * The effort a request that names none is sent, for a model whose own\n * default is lower than its siblings': Claude Opus 5.5 defaults to medium,\n * where Claude Opus 5 defaults to high.\n */\nconst ANTHROPIC_DEFAULT_EFFORT: Readonly<Record<string, AiEffort>> = {\n 'claude-opus-5-5': 'high',\n}\n\n/**\n * The Messages API body for a request. Exported so a spec can assert the\n * wire shape without a network. The cache-prefix rule is enforced by the\n * runtime before the request reaches any adapter; this maps the surviving\n * breakpoints onto `cache_control` and refuses only the count the API\n * itself refuses.\n */\nexport function buildAnthropicRequestBody(\n input: Omit<AiProviderRequest, 'apiKey' | 'signal'> & { stream: boolean },\n): Record<string, unknown> {\n const conversation = Boolean(input.cacheConversation) && input.messages.length > 0\n const breakpoints = input.system.filter((block) => block.cacheBreakpoint).length + (conversation ? 1 : 0)\n if (breakpoints > ANTHROPIC_MAX_CACHE_BREAKPOINTS) {\n throw new Error(\n `${breakpoints} cache breakpoints; the Messages API allows ${ANTHROPIC_MAX_CACHE_BREAKPOINTS}`,\n )\n }\n // One lifetime for every breakpoint: the API refuses a longer-lived entry\n // after a shorter one, and a request asks for one or the other.\n const cacheControl = input.cacheTtl === '1h' ? { type: 'ephemeral', ttl: '1h' } : { type: 'ephemeral' }\n const system = input.system.map((block) => ({\n type: 'text',\n text: block.text,\n ...(block.cacheBreakpoint ? { cache_control: cacheControl } : {}),\n }))\n const tools = input.tools?.map((tool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: { ...tool.inputSchema, additionalProperties: false },\n strict: true,\n }))\n // A model that rejects an explicit thinking setting gets none, whatever\n // the door asked for, and no effort either: effort rides the same rule\n // (see the contract), and the catalog says which models those are.\n const settable = aiCatalogEntry(input.model)?.capabilities.thinking !== false\n const thinking = settable ? input.thinking : undefined\n const effort = settable ? (input.effort ?? ANTHROPIC_DEFAULT_EFFORT[input.model]) : undefined\n const thinkingOff = ANTHROPIC_THINKING_OFF[input.model] ?? 'disabled'\n return {\n model: input.model,\n max_tokens: input.maxTokens,\n ...(input.stream ? { stream: true } : {}),\n ...(thinking === 'off'\n ? thinkingOff === 'omit'\n ? {}\n : { thinking: { type: thinkingOff } }\n : thinking === 'adaptive'\n ? { thinking: { type: 'adaptive' } }\n : {}),\n ...(effort ? { output_config: { effort } } : {}),\n ...(system.length ? { system } : {}),\n messages: input.messages.map((message, index) => ({\n role: message.role,\n content: conversation\n ? anthropicBlocksOf(message, index === input.messages.length - 1 ? cacheControl : null)\n : anthropicContentOf(message),\n })),\n ...(tools?.length ? { tools, tool_choice: { type: 'auto' } } : {}),\n }\n}\n\n/**\n * A turn's content in the Messages API's shape: a text-only turn as the\n * string it is, and a turn with pictures as content blocks in order, each\n * picture a base64 image block (AGL-2916).\n */\nfunction anthropicContentOf(message: AiMessage): string | Array<Record<string, unknown>> {\n if (typeof message.content === 'string') return message.content\n return message.content.map((part) =>\n part.type === 'image'\n ? { type: 'image', source: { type: 'base64', media_type: part.mediaType, data: part.data } }\n : { type: 'text', text: part.text },\n )\n}\n\n/**\n * A turn as content blocks whatever its shape, the last block carrying the\n * conversation's breakpoint when it is the request's last turn (AGL-3809).\n * Every turn of a request that caches its conversation is sent as blocks, so\n * a turn reads the same on the call that marked it and on every call after,\n * where it is no longer last.\n */\nfunction anthropicBlocksOf(\n message: AiMessage,\n cacheControl: Record<string, unknown> | null,\n): Array<Record<string, unknown>> {\n const content = anthropicContentOf(message)\n const blocks = typeof content === 'string' ? [{ type: 'text', text: content }] : content\n if (!cacheControl || !blocks.length) return blocks\n const last = blocks.length - 1\n return blocks.map((block, index) => (index === last ? { ...block, cache_control: cacheControl } : block))\n}\n\n/** The provider's request id, when it sent one — the log's join key. */\nfunction requestIdOf(response: Response): string | null {\n return response.headers?.get?.('request-id') ?? null\n}\n\nasync function send(\n input: AiProviderRequest,\n stream: boolean,\n): Promise<Response> {\n const body = buildAnthropicRequestBody({ ...input, stream })\n const response = await fetch(ANTHROPIC_URL, {\n method: 'POST',\n headers: {\n 'x-api-key': input.apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n ...(input.signal ? { signal: input.signal } : {}),\n })\n if (!response.ok || (stream && !response.body)) {\n const requestId = requestIdOf(response)\n const payload = (await response.json().catch(() => null)) as {\n error?: { type?: string; message?: string }\n } | null\n console.error('ai upstream error', {\n provider: ANTHROPIC_PROVIDER_ID,\n status: response.status,\n requestId,\n model: input.model,\n error: payload?.error ?? null,\n })\n throw new AiUpstreamError(\n response.status,\n anthropicFailureIsRetryable(response.status, payload?.error?.type),\n requestId,\n anthropicAccountProblem(response.status, payload?.error),\n )\n }\n return response\n}\n\nasync function complete(input: AiProviderRequest): Promise<AiResult> {\n const response = await send(input, false)\n return anthropicResultFrom(await response.json(), input.model)\n}\n\n/**\n * A Messages API answer as the contract's result: the shape `complete` reads\n * off the wire and a batch's succeeded entry carries. `priceFactor` is what a\n * discount leaves of the price (a batch's half).\n */\nexport function anthropicResultFrom(raw: unknown, model: string, priceFactor = 1): AiResult {\n const payload = raw as {\n content?: Array<Record<string, unknown>>\n stop_reason?: unknown\n usage?: unknown\n } | null\n const blocks = Array.isArray(payload?.content) ? payload.content : []\n const text = blocks\n .filter((block) => block?.['type'] === 'text')\n .map((block) => String(block['text'] ?? ''))\n .join('')\n const toolUse: AiToolUse[] = blocks\n .filter((block) => block?.['type'] === 'tool_use')\n .map((block) => ({\n name: String(block['name'] ?? ''),\n input: aiToolInputOf(block['input']),\n }))\n const usage = anthropicUsageFrom(payload?.usage)\n const estCostUsd = Math.round(anthropicBilledUsd(usage, model) * priceFactor * 1_000_000) / 1_000_000\n const stopReason =\n typeof payload?.stop_reason === 'string' && payload.stop_reason\n ? payload.stop_reason\n : null\n if (stopReason === 'refusal') {\n return { kind: 'refusal', text, usage, estCostUsd, stopReason }\n }\n return {\n kind: 'completion',\n text,\n toolUse,\n usage,\n estCostUsd,\n stopReason,\n // The blocks as the provider answered them, for a call its ceiling cut\n // off (AGL-3143): `toolUse` holds only what still parsed out of them.\n ...(aiStoppedAtCeiling(stopReason) ? { rawOutput: aiRawOutputOf(blocks) } : {}),\n }\n}\n\nasync function stream(input: AiProviderRequest): Promise<AsyncIterable<AiStreamEvent>> {\n const response = await send(input, true)\n return streamEvents(\n response.body as ReadableStream<Uint8Array>,\n input.model,\n requestIdOf(response),\n )\n}\n\n/**\n * Anthropic's SSE stream as contract events. Usage is taken from\n * `message_start` (input and cache figures) and `message_delta` (the\n * output count, and any cumulative input figures a newer API sends); the\n * `done` event carries whatever was known when the body ended, so a stream\n * cut short still meters what it cost.\n */\nasync function* streamEvents(\n body: ReadableStream<Uint8Array>,\n model: string,\n requestId: string | null,\n): AsyncGenerator<AiStreamEvent> {\n const decoder = new TextDecoder()\n const reader = body.getReader()\n const usage: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n }\n let stopReason: string | null = null\n /** Tool-use blocks in flight, by content index; input arrives in pieces. */\n const tools = new Map<number, { name: string; json: string }>()\n /**\n * What each tool call was actually written in, in the order its block\n * opened (AGL-3143). The `tool` events below carry only as much of these\n * as `aiToolInputOf` could still parse, so for a call the ceiling cut off\n * this is the one place the emitted bytes survive.\n */\n const written: string[] = []\n let buffer = ''\n let drained = false\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) {\n drained = true\n break\n }\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n let event: Record<string, unknown>\n try {\n event = JSON.parse(line.slice('data: '.length))\n } catch {\n continue\n }\n const index = Number(event['index'])\n switch (event['type']) {\n case 'message_start': {\n const start = anthropicUsageFrom(\n (event['message'] as { usage?: unknown } | undefined)?.usage,\n )\n usage.inputTokens = start.inputTokens\n usage.cacheReadTokens = start.cacheReadTokens\n usage.cacheWriteTokens = start.cacheWriteTokens\n if (start.cacheWriteHourTokens) usage.cacheWriteHourTokens = start.cacheWriteHourTokens\n break\n }\n case 'content_block_start': {\n const block = event['content_block'] as\n | { type?: string; name?: string }\n | undefined\n if (block?.type === 'tool_use') {\n tools.set(index, { name: String(block.name ?? ''), json: '' })\n }\n break\n }\n case 'content_block_delta': {\n const delta = event['delta'] as\n | { type?: string; text?: string; partial_json?: string }\n | undefined\n if (delta?.type === 'text_delta' && delta.text) {\n yield { type: 'delta', text: delta.text }\n } else if (delta?.type === 'input_json_delta') {\n const tool = tools.get(index)\n if (tool) tool.json += delta.partial_json ?? ''\n }\n break\n }\n case 'content_block_stop': {\n const tool = tools.get(index)\n if (tool) {\n tools.delete(index)\n written.push(tool.json)\n yield { type: 'tool', name: tool.name, input: aiToolInputOf(tool.json) }\n }\n break\n }\n case 'message_delta': {\n const delta = anthropicUsageFrom(event['usage'])\n usage.outputTokens = delta.outputTokens || usage.outputTokens\n usage.inputTokens = delta.inputTokens || usage.inputTokens\n usage.cacheReadTokens = delta.cacheReadTokens || usage.cacheReadTokens\n usage.cacheWriteTokens = delta.cacheWriteTokens || usage.cacheWriteTokens\n // Kept where the breakdown is reported and left absent where it\n // is not (AGL-3143), never set to a zero the provider did not\n // send. A stream needs it for the reason a completion does: it\n // is the figure that accounts for a spent ceiling first, and it\n // arrives in the same usage object as the four above.\n if (delta.thinkingTokens !== undefined) usage.thinkingTokens = delta.thinkingTokens\n const reason = (event['delta'] as { stop_reason?: unknown } | undefined)\n ?.stop_reason\n if (typeof reason === 'string' && reason) stopReason = reason\n break\n }\n case 'error': {\n const error = event['error'] as\n | { type?: string; message?: string }\n | undefined\n console.error('ai upstream stream error', {\n provider: ANTHROPIC_PROVIDER_ID,\n requestId,\n model,\n error: error ?? null,\n })\n yield { type: 'error', retryable: anthropicFailureIsRetryable(null, error?.type) }\n break\n }\n default:\n break\n }\n }\n }\n } finally {\n // A consumer that stops iterating early — a client that hung up — must\n // not leave the provider generating into a socket nobody reads: cancel\n // tells the upstream to stop, and the tokens it has already produced are\n // the only ones billed.\n if (!drained) await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n // A message the ceiling ends mid-block never reaches that block's stop\n // event, so what it had written is still held in flight.\n for (const tool of tools.values()) written.push(tool.json)\n yield {\n type: 'done',\n usage,\n estCostUsd: anthropicBilledUsd(usage, model),\n stopReason,\n // What the model emitted into its tool calls, for a stream its ceiling\n // cut off (AGL-3143): the `tool` events carry only what parsed out.\n ...(aiStoppedAtCeiling(stopReason) ? { rawOutput: aiRawOutputOf(written.join('')) } : {}),\n }\n}\n\n/** What a batch's discount leaves of a request's price. */\nexport const ANTHROPIC_BATCH_PRICE_FACTOR = 0.5\n\nfunction batchHeaders(apiKey: string): Record<string, string> {\n return {\n 'x-api-key': apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n 'Content-Type': 'application/json',\n }\n}\n\n/** A batch call that did not answer 2xx, as the contract's error. */\nasync function batchFailure(response: Response, model: string): Promise<AiUpstreamError> {\n const requestId = requestIdOf(response)\n const payload = (await response.json().catch(() => null)) as {\n error?: { type?: string; message?: string }\n } | null\n console.error('ai upstream batch error', {\n provider: ANTHROPIC_PROVIDER_ID,\n status: response.status,\n requestId,\n model,\n error: payload?.error ?? null,\n })\n return new AiUpstreamError(\n response.status,\n anthropicFailureIsRetryable(response.status, payload?.error?.type),\n requestId,\n anthropicAccountProblem(response.status, payload?.error),\n )\n}\n\n/** The batch as its create and poll calls describe it. */\ninterface AnthropicBatch {\n id: string\n processing_status?: string\n results_url?: string | null\n}\n\n/** Results a batch can end a request with that answer nothing yet: asked again, not failed (AGL-3781). */\nconst ANTHROPIC_BATCH_UNFINISHED = new Set(['canceled', 'expired'])\n\n/**\n * Many requests through the Message Batches API (AGL-3660): created as one\n * batch, polled until it has ended, its results read by `custom_id` — they\n * come back in any order — and each turned into the result `complete` would\n * have returned, at half the price. Development only: the live evals' batch\n * mode is the one caller, and no door waits minutes for an answer.\n *\n * A batch still processing when its wait runs out is canceled, and read once\n * the cancel has ended it (AGL-3781): a cancel keeps every answer the batch\n * already has, so those come back as results, and only the requests it never\n * reached come back as `AiBatchUnfinishedError`, for the caller to ask on\n * their own. Throwing away a round's paid-for answers because one batch was\n * slow is how a whole golden run lost its grades.\n */\nasync function completeBatch(\n requests: readonly AiProviderRequest[],\n options: AiBatchOptions = {},\n): Promise<Array<AiResult | Error>> {\n if (!requests.length) return []\n const apiKey = requests[0].apiKey\n const model = requests[0].model\n const pollMs = options.pollMs ?? 10_000\n const timeoutMs = options.timeoutMs ?? 60 * 60_000\n const cancelGraceMs = options.cancelGraceMs ?? 10 * 60_000\n const unfinished = (id: string, reason: string) => requests.map(() => new AiBatchUnfinishedError(id, reason))\n const created = await fetch(ANTHROPIC_BATCHES_URL, {\n method: 'POST',\n headers: batchHeaders(apiKey),\n body: JSON.stringify({\n requests: requests.map((request, index) => {\n const { apiKey: _key, signal: _signal, ...rest } = request\n return { custom_id: `r${index}`, params: buildAnthropicRequestBody({ ...rest, stream: false }) }\n }),\n }),\n })\n if (!created.ok) throw await batchFailure(created, model)\n let batch = (await created.json()) as AnthropicBatch\n const deadline = Date.now() + timeoutMs\n let cancelDeadline: number | null = null\n while (batch.processing_status !== 'ended') {\n if (cancelDeadline === null && Date.now() >= deadline) {\n console.warn(`[ai] batch ${batch.id} has not ended after ${timeoutMs} ms; canceling it to read what it answered`)\n await fetch(`${ANTHROPIC_BATCHES_URL}/${batch.id}/cancel`, { method: 'POST', headers: batchHeaders(apiKey) }).catch(\n () => undefined,\n )\n cancelDeadline = Date.now() + cancelGraceMs\n }\n // A cancel that never ends leaves no results to read; every request goes back unanswered.\n if (cancelDeadline !== null && Date.now() >= cancelDeadline) return unfinished(batch.id, 'cancel did not end')\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n const polled = await fetch(`${ANTHROPIC_BATCHES_URL}/${batch.id}`, { headers: batchHeaders(apiKey) })\n if (!polled.ok) throw await batchFailure(polled, model)\n batch = (await polled.json()) as AnthropicBatch\n }\n if (!batch.results_url) {\n if (cancelDeadline !== null) return unfinished(batch.id, 'canceled with no results')\n throw new Error(`batch ${batch.id} ended with no results`)\n }\n const results = await fetch(batch.results_url, { headers: batchHeaders(apiKey) })\n if (!results.ok) throw await batchFailure(results, model)\n const byId = new Map<string, { type?: string; message?: unknown; error?: unknown }>()\n for (const line of (await results.text()).split('\\n')) {\n if (!line.trim()) continue\n const entry = JSON.parse(line) as { custom_id: string; result?: { type?: string; message?: unknown; error?: unknown } }\n byId.set(entry.custom_id, entry.result ?? {})\n }\n return requests.map((request, index) => {\n const result = byId.get(`r${index}`)\n if (result?.type === 'succeeded') {\n return anthropicResultFrom(result.message, request.model, ANTHROPIC_BATCH_PRICE_FACTOR)\n }\n const type = result?.type ?? 'missing'\n if (ANTHROPIC_BATCH_UNFINISHED.has(type) || type === 'missing') return new AiBatchUnfinishedError(batch.id, type)\n console.error('ai batch request not answered', {\n provider: ANTHROPIC_PROVIDER_ID,\n batchId: batch.id,\n customId: `r${index}`,\n result: type,\n error: result?.error ?? null,\n })\n return new AiUpstreamError(null, type === 'errored', batch.id, null)\n })\n}\n\nexport const anthropicProvider: AiProvider = {\n id: ANTHROPIC_PROVIDER_ID,\n label: 'Anthropic',\n apiKeyEnv: ANTHROPIC_API_KEY_ENV,\n readApiKey: () => process.env.ANTHROPIC_API_KEY?.trim() || undefined,\n endpointHost: ANTHROPIC_HOST,\n toolSchemaLimits: ANTHROPIC_TOOL_SCHEMA_LIMITS,\n models(): readonly AiModelDescriptor[] {\n return aiModelIdsForProvider(ANTHROPIC_PROVIDER_ID)\n .map((id) => aiCatalogEntry(id))\n .filter((entry): entry is NonNullable<typeof entry> => Boolean(entry))\n },\n complete,\n stream,\n completeBatch,\n}\n"],"names":["aiBilledRatesForModel","aiCatalogEntry","aiModelIdsForProvider","estimateAiBilledUsd","aiCacheWriteHourSurchargeUsd","AiBatchUnfinishedError","AiUpstreamError","aiRawOutputOf","aiStoppedAtCeiling","aiTokenCount","aiToolInputOf","ANTHROPIC_PROVIDER_ID","ANTHROPIC_API_KEY_ENV","ANTHROPIC_HOST","ANTHROPIC_URL","ANTHROPIC_BATCHES_URL","ANTHROPIC_VERSION","ANTHROPIC_MAX_CACHE_BREAKPOINTS","ANTHROPIC_TOOL_SCHEMA_LIMITS","strictTools","optionalParameters","unionParameters","compiledSchemaBytes","anthropicFailureIsRetryable","status","errorType","anthropicAccountProblem","error","type","test","message","anthropicUsageFrom","usage","record","details","thinking","undefined","creation","hour","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","cacheWriteHourTokens","thinkingTokens","anthropicBilledUsd","model","usd","Math","round","ANTHROPIC_THINKING_OFF","ANTHROPIC_DEFAULT_EFFORT","buildAnthropicRequestBody","input","conversation","Boolean","cacheConversation","messages","length","breakpoints","system","filter","block","cacheBreakpoint","Error","cacheControl","cacheTtl","ttl","map","text","cache_control","tools","tool","name","description","input_schema","inputSchema","additionalProperties","strict","settable","capabilities","effort","thinkingOff","max_tokens","maxTokens","stream","output_config","index","role","content","anthropicBlocksOf","anthropicContentOf","tool_choice","part","source","media_type","mediaType","data","blocks","last","requestIdOf","response","headers","get","send","body","fetch","method","apiKey","JSON","stringify","signal","ok","payload","requestId","json","catch","console","provider","complete","anthropicResultFrom","raw","priceFactor","Array","isArray","String","join","toolUse","estCostUsd","stopReason","stop_reason","kind","rawOutput","streamEvents","decoder","TextDecoder","reader","getReader","Map","written","buffer","drained","lines","done","value","read","decode","split","pop","line","startsWith","event","parse","slice","Number","start","set","delta","partial_json","delete","push","reason","retryable","cancel","releaseLock","values","ANTHROPIC_BATCH_PRICE_FACTOR","batchHeaders","batchFailure","ANTHROPIC_BATCH_UNFINISHED","Set","completeBatch","requests","options","pollMs","timeoutMs","cancelGraceMs","unfinished","id","created","request","_key","_signal","rest","custom_id","params","batch","deadline","Date","now","cancelDeadline","processing_status","warn","Promise","resolve","setTimeout","polled","results_url","results","byId","entry","trim","result","has","batchId","customId","anthropicProvider","label","apiKeyEnv","readApiKey","process","env","ANTHROPIC_API_KEY","endpointHost","toolSchemaLimits","models"],"mappings":";;;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,EAAEC,cAAc,EAAEC,qBAAqB,EAAEC,mBAAmB,QAAQ,eAAW;AAC7G,SAASC,4BAA4B,QAAQ,wBAAoB;AACjE,SACEC,sBAAsB,EACtBC,eAAe,EAIfC,aAAa,EACbC,kBAAkB,EAClBC,YAAY,EACZC,aAAa,QAUR,gBAAY;AAEnB;;;;;;;;;;;;;;;;;CAiBC,GAED,OAAO,MAAMC,wBAAwB,YAAW;AAChD,OAAO,MAAMC,wBAAwB,oBAAmB;AACxD,MAAMC,iBAAiB;AACvB,MAAMC,gBAAgB,CAAC,QAAQ,EAAED,eAAe,YAAY,CAAC;AAC7D,MAAME,wBAAwB,CAAC,QAAQ,EAAEF,eAAe,oBAAoB,CAAC;AAC7E,MAAMG,oBAAoB;AAE1B,wEAAwE,GACxE,OAAO,MAAMC,kCAAkC,EAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,MAAMC,+BAAmD;IAC9DC,aAAa;IACbC,oBAAoB;IACpBC,iBAAiB;IACjBC,qBAAqB;AACvB,EAAC;AAED;;;;CAIC,GACD,OAAO,SAASC,4BACdC,MAAqB,EACrBC,SAA6B;IAE7B,OACED,WAAW,OACXA,WAAW,OACXC,cAAc,sBACdA,cAAc;AAElB;AAEA;;;CAGC,GACD,OAAO,SAASC,wBACdF,MAAqB,EACrBG,KAA6D;;IAE7D,IAAIH,WAAW,OAAOA,WAAW,KAAK,OAAO;IAC7C,IAAIG,CAAAA,yBAAAA,MAAOC,IAAI,MAAK,0BAA0BD,CAAAA,yBAAAA,MAAOC,IAAI,MAAK,oBAAoB;QAChF,OAAO;IACT;IACA,IAAID,CAAAA,yBAAAA,MAAOC,IAAI,MAAK,mBAAmB,kBAAkBC,IAAI,SAACF,yBAAAA,MAAOG,OAAO,mBAAI,KAAK;QACnF,OAAO;IACT;IACA,OAAO;AACT;AAEA,6EAA6E,GAC7E,OAAO,SAASC,mBAAmBC,KAAc;IAC/C,MAAMC,SAAUD,gBAAAA,QAAS,CAAC;IAC1B,2EAA2E;IAC3E,sEAAsE;IACtE,MAAME,UAAUD,MAAM,CAAC,wBAAwB;IAC/C,MAAME,WACJD,WAAW,OAAOA,YAAY,WAC1B,AAACA,OAAmC,CAAC,kBAAkB,GACvDE;IACN,4EAA4E;IAC5E,2EAA2E;IAC3E,sEAAsE;IACtE,MAAMC,WAAWJ,MAAM,CAAC,iBAAiB;IACzC,MAAMK,OACJD,YAAY,OAAOA,aAAa,WAC5B5B,aAAa,AAAC4B,QAAoC,CAAC,4BAA4B,IAC/E;IACN,OAAO;QACLE,aAAa9B,aAAawB,MAAM,CAAC,eAAe;QAChDO,cAAc/B,aAAawB,MAAM,CAAC,gBAAgB;QAClDQ,iBAAiBhC,aAAawB,MAAM,CAAC,0BAA0B;QAC/DS,kBAAkBjC,aAAawB,MAAM,CAAC,8BAA8B;OAChEK,OAAO,IAAI;QAAEK,sBAAsBL;IAAK,IAAI,CAAC,GAC7CH,aAAaC,YAAY,CAAC,IAAI;QAAEQ,gBAAgBnC,aAAa0B;IAAU;AAE/E;AAEA,4EAA4E,GAC5E,SAASU,mBAAmBb,KAAc,EAAEc,KAAa;IACvD,MAAMC,MAAM5C,oBAAoB6B,OAAOc,SAAS1C,6BAA6B4B,OAAOhC,sBAAsB8C;IAC1G,OAAOE,KAAKC,KAAK,CAACF,MAAM,WAAa;AACvC;AAEA;;;;;;CAMC,GACD,MAAMG,yBAA6E;IACjF,qBAAqB;IACrB,mBAAmB;AACrB;AAEA;;;;CAIC,GACD,MAAMC,2BAA+D;IACnE,mBAAmB;AACrB;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,0BACdC,KAAyE;QA4B9CA,eACPH;QAZNG,cASGpD;IAxBjB,MAAMqD,eAAeC,QAAQF,MAAMG,iBAAiB,KAAKH,MAAMI,QAAQ,CAACC,MAAM,GAAG;IACjF,MAAMC,cAAcN,MAAMO,MAAM,CAACC,MAAM,CAAC,CAACC,QAAUA,MAAMC,eAAe,EAAEL,MAAM,GAAIJ,CAAAA,eAAe,IAAI,CAAA;IACvG,IAAIK,cAAc1C,iCAAiC;QACjD,MAAM,IAAI+C,MACR,GAAGL,YAAY,4CAA4C,EAAE1C,iCAAiC;IAElG;IACA,0EAA0E;IAC1E,gEAAgE;IAChE,MAAMgD,eAAeZ,MAAMa,QAAQ,KAAK,OAAO;QAAEtC,MAAM;QAAauC,KAAK;IAAK,IAAI;QAAEvC,MAAM;IAAY;IACtG,MAAMgC,SAASP,MAAMO,MAAM,CAACQ,GAAG,CAAC,CAACN,QAAW;YAC1ClC,MAAM;YACNyC,MAAMP,MAAMO,IAAI;WACZP,MAAMC,eAAe,GAAG;YAAEO,eAAeL;QAAa,IAAI,CAAC;IAEjE,MAAMM,SAAQlB,eAAAA,MAAMkB,KAAK,qBAAXlB,aAAae,GAAG,CAAC,CAACI,OAAU,CAAA;YACxCC,MAAMD,KAAKC,IAAI;YACfC,aAAaF,KAAKE,WAAW;YAC7BC,cAAc,aAAKH,KAAKI,WAAW;gBAAEC,sBAAsB;;YAC3DC,QAAQ;QACV,CAAA;IACA,wEAAwE;IACxE,uEAAuE;IACvE,mEAAmE;IACnE,MAAMC,WAAW9E,EAAAA,kBAAAA,eAAeoD,MAAMP,KAAK,sBAA1B7C,gBAA6B+E,YAAY,CAAC7C,QAAQ,MAAK;IACxE,MAAMA,WAAW4C,WAAW1B,MAAMlB,QAAQ,GAAGC;IAC7C,MAAM6C,SAASF,YAAY1B,gBAAAA,MAAM4B,MAAM,YAAZ5B,gBAAgBF,wBAAwB,CAACE,MAAMP,KAAK,CAAC,GAAIV;IACpF,MAAM8C,eAAchC,sCAAAA,sBAAsB,CAACG,MAAMP,KAAK,CAAC,YAAnCI,sCAAuC;IAC3D,OAAO;QACLJ,OAAOO,MAAMP,KAAK;QAClBqC,YAAY9B,MAAM+B,SAAS;OACvB/B,MAAMgC,MAAM,GAAG;QAAEA,QAAQ;IAAK,IAAI,CAAC,GACnClD,aAAa,QACb+C,gBAAgB,SACd,CAAC,IACD;QAAE/C,UAAU;YAAEP,MAAMsD;QAAY;IAAE,IACpC/C,aAAa,aACX;QAAEA,UAAU;YAAEP,MAAM;QAAW;IAAE,IACjC,CAAC,GACHqD,SAAS;QAAEK,eAAe;YAAEL;QAAO;IAAE,IAAI,CAAC,GAC1CrB,OAAOF,MAAM,GAAG;QAAEE;IAAO,IAAI,CAAC;QAClCH,UAAUJ,MAAMI,QAAQ,CAACW,GAAG,CAAC,CAACtC,SAASyD,QAAW,CAAA;gBAChDC,MAAM1D,QAAQ0D,IAAI;gBAClBC,SAASnC,eACLoC,kBAAkB5D,SAASyD,UAAUlC,MAAMI,QAAQ,CAACC,MAAM,GAAG,IAAIO,eAAe,QAChF0B,mBAAmB7D;YACzB,CAAA;OACIyC,CAAAA,yBAAAA,MAAOb,MAAM,IAAG;QAAEa;QAAOqB,aAAa;YAAEhE,MAAM;QAAO;IAAE,IAAI,CAAC;AAEpE;AAEA;;;;CAIC,GACD,SAAS+D,mBAAmB7D,OAAkB;IAC5C,IAAI,OAAOA,QAAQ2D,OAAO,KAAK,UAAU,OAAO3D,QAAQ2D,OAAO;IAC/D,OAAO3D,QAAQ2D,OAAO,CAACrB,GAAG,CAAC,CAACyB,OAC1BA,KAAKjE,IAAI,KAAK,UACV;YAAEA,MAAM;YAASkE,QAAQ;gBAAElE,MAAM;gBAAUmE,YAAYF,KAAKG,SAAS;gBAAEC,MAAMJ,KAAKI,IAAI;YAAC;QAAE,IACzF;YAAErE,MAAM;YAAQyC,MAAMwB,KAAKxB,IAAI;QAAC;AAExC;AAEA;;;;;;CAMC,GACD,SAASqB,kBACP5D,OAAkB,EAClBmC,YAA4C;IAE5C,MAAMwB,UAAUE,mBAAmB7D;IACnC,MAAMoE,SAAS,OAAOT,YAAY,WAAW;QAAC;YAAE7D,MAAM;YAAQyC,MAAMoB;QAAQ;KAAE,GAAGA;IACjF,IAAI,CAACxB,gBAAgB,CAACiC,OAAOxC,MAAM,EAAE,OAAOwC;IAC5C,MAAMC,OAAOD,OAAOxC,MAAM,GAAG;IAC7B,OAAOwC,OAAO9B,GAAG,CAAC,CAACN,OAAOyB,QAAWA,UAAUY,OAAO,aAAKrC;YAAOQ,eAAeL;aAAiBH;AACpG;AAEA,sEAAsE,GACtE,SAASsC,YAAYC,QAAkB;;QAC9BA,uBAAAA;IAAP,gBAAOA,oBAAAA,SAASC,OAAO,sBAAhBD,wBAAAA,kBAAkBE,GAAG,qBAArBF,2BAAAA,mBAAwB,gCAAiB;AAClD;AAEA,eAAeG,KACbnD,KAAwB,EACxBgC,MAAe;IAEf,MAAMoB,OAAOrD,0BAA0B,aAAKC;QAAOgC;;IACnD,MAAMgB,WAAW,MAAMK,MAAM5F,eAAe;QAC1C6F,QAAQ;QACRL,SAAS;YACP,aAAajD,MAAMuD,MAAM;YACzB,qBAAqB5F;YACrB,gBAAgB;QAClB;QACAyF,MAAMI,KAAKC,SAAS,CAACL;OACjBpD,MAAM0D,MAAM,GAAG;QAAEA,QAAQ1D,MAAM0D,MAAM;IAAC,IAAI,CAAC;IAEjD,IAAI,CAACV,SAASW,EAAE,IAAK3B,UAAU,CAACgB,SAASI,IAAI,EAAG;;YAcCQ;QAb/C,MAAMC,YAAYd,YAAYC;QAC9B,MAAMY,UAAW,MAAMZ,SAASc,IAAI,GAAGC,KAAK,CAAC,IAAM;QAGnDC,QAAQ1F,KAAK,CAAC,qBAAqB;YACjC2F,UAAU3G;YACVa,QAAQ6E,SAAS7E,MAAM;YACvB0F;YACApE,OAAOO,MAAMP,KAAK;YAClBnB,KAAK,UAAEsF,2BAAAA,QAAStF,KAAK,mBAAI;QAC3B;QACA,MAAM,IAAIrB,gBACR+F,SAAS7E,MAAM,EACfD,4BAA4B8E,SAAS7E,MAAM,EAAEyF,4BAAAA,iBAAAA,QAAStF,KAAK,qBAAdsF,eAAgBrF,IAAI,GACjEsF,WACAxF,wBAAwB2E,SAAS7E,MAAM,EAAEyF,2BAAAA,QAAStF,KAAK;IAE3D;IACA,OAAO0E;AACT;AAEA,eAAekB,SAASlE,KAAwB;IAC9C,MAAMgD,WAAW,MAAMG,KAAKnD,OAAO;IACnC,OAAOmE,oBAAoB,MAAMnB,SAASc,IAAI,IAAI9D,MAAMP,KAAK;AAC/D;AAEA;;;;CAIC,GACD,OAAO,SAAS0E,oBAAoBC,GAAY,EAAE3E,KAAa,EAAE4E,cAAc,CAAC;IAC9E,MAAMT,UAAUQ;IAKhB,MAAMvB,SAASyB,MAAMC,OAAO,CAACX,2BAAAA,QAASxB,OAAO,IAAIwB,QAAQxB,OAAO,GAAG,EAAE;IACrE,MAAMpB,OAAO6B,OACVrC,MAAM,CAAC,CAACC,QAAUA,CAAAA,yBAAAA,KAAO,CAAC,OAAO,MAAK,QACtCM,GAAG,CAAC,CAACN;YAAiBA;eAAP+D,QAAO/D,cAAAA,KAAK,CAAC,OAAO,YAAbA,cAAiB;OACvCgE,IAAI,CAAC;IACR,MAAMC,UAAuB7B,OAC1BrC,MAAM,CAAC,CAACC,QAAUA,CAAAA,yBAAAA,KAAO,CAAC,OAAO,MAAK,YACtCM,GAAG,CAAC,CAACN;YACSA;eADE;YACfW,MAAMoD,QAAO/D,cAAAA,KAAK,CAAC,OAAO,YAAbA,cAAiB;YAC9BT,OAAO3C,cAAcoD,KAAK,CAAC,QAAQ;QACrC;;IACF,MAAM9B,QAAQD,mBAAmBkF,2BAAAA,QAASjF,KAAK;IAC/C,MAAMgG,aAAahF,KAAKC,KAAK,CAACJ,mBAAmBb,OAAOc,SAAS4E,cAAc,WAAa;IAC5F,MAAMO,aACJ,QAAOhB,2BAAAA,QAASiB,WAAW,MAAK,YAAYjB,QAAQiB,WAAW,GAC3DjB,QAAQiB,WAAW,GACnB;IACN,IAAID,eAAe,WAAW;QAC5B,OAAO;YAAEE,MAAM;YAAW9D;YAAMrC;YAAOgG;YAAYC;QAAW;IAChE;IACA,OAAO;QACLE,MAAM;QACN9D;QACA0D;QACA/F;QACAgG;QACAC;OAGIzH,mBAAmByH,cAAc;QAAEG,WAAW7H,cAAc2F;IAAQ,IAAI,CAAC;AAEjF;AAEA,eAAeb,OAAOhC,KAAwB;IAC5C,MAAMgD,WAAW,MAAMG,KAAKnD,OAAO;IACnC,OAAOgF,aACLhC,SAASI,IAAI,EACbpD,MAAMP,KAAK,EACXsD,YAAYC;AAEhB;AAEA;;;;;;CAMC,GACD,SAAgBgC,aACd5B,IAAgC,EAChC3D,KAAa,EACboE,SAAwB;;QAExB,MAAMoB,UAAU,IAAIC;QACpB,MAAMC,SAAS/B,KAAKgC,SAAS;QAC7B,MAAMzG,QAAiB;YACrBO,aAAa;YACbC,cAAc;YACdC,iBAAiB;YACjBC,kBAAkB;QACpB;QACA,IAAIuF,aAA4B;QAChC,0EAA0E,GAC1E,MAAM1D,QAAQ,IAAImE;QAClB;;;;;GAKC,GACD,MAAMC,UAAoB,EAAE;QAC5B,IAAIC,SAAS;QACb,IAAIC,UAAU;QACd,IAAI;YACF,OAAS;oBAQEC;gBAPT,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAA,uBAAMR,OAAOS,IAAI;gBACzC,IAAIF,MAAM;oBACRF,UAAU;oBACV;gBACF;gBACAD,UAAUN,QAAQY,MAAM,CAACF,OAAO;oBAAE3D,QAAQ;gBAAK;gBAC/C,MAAMyD,QAAQF,OAAOO,KAAK,CAAC;gBAC3BP,UAASE,aAAAA,MAAMM,GAAG,cAATN,aAAe;gBACxB,KAAK,MAAMO,QAAQP,MAAO;oBACxB,IAAI,CAACO,KAAKC,UAAU,CAAC,WAAW;oBAChC,IAAIC;oBACJ,IAAI;wBACFA,QAAQ1C,KAAK2C,KAAK,CAACH,KAAKI,KAAK,CAAC,SAAS/F,MAAM;oBAC/C,EAAE,eAAM;wBACN;oBACF;oBACA,MAAM6B,QAAQmE,OAAOH,KAAK,CAAC,QAAQ;oBACnC,OAAQA,KAAK,CAAC,OAAO;wBACnB,KAAK;4BAAiB;oCAEjBA;gCADH,MAAMI,QAAQ5H,oBACXwH,iBAAAA,KAAK,CAAC,UAAU,qBAAjB,AAACA,eAAsDvH,KAAK;gCAE9DA,MAAMO,WAAW,GAAGoH,MAAMpH,WAAW;gCACrCP,MAAMS,eAAe,GAAGkH,MAAMlH,eAAe;gCAC7CT,MAAMU,gBAAgB,GAAGiH,MAAMjH,gBAAgB;gCAC/C,IAAIiH,MAAMhH,oBAAoB,EAAEX,MAAMW,oBAAoB,GAAGgH,MAAMhH,oBAAoB;gCACvF;4BACF;wBACA,KAAK;4BAAuB;gCAC1B,MAAMmB,QAAQyF,KAAK,CAAC,gBAAgB;gCAGpC,IAAIzF,CAAAA,yBAAAA,MAAOlC,IAAI,MAAK,YAAY;wCACEkC;oCAAhCS,MAAMqF,GAAG,CAACrE,OAAO;wCAAEd,MAAMoD,QAAO/D,cAAAA,MAAMW,IAAI,YAAVX,cAAc;wCAAKqD,MAAM;oCAAG;gCAC9D;gCACA;4BACF;wBACA,KAAK;4BAAuB;gCAC1B,MAAM0C,QAAQN,KAAK,CAAC,QAAQ;gCAG5B,IAAIM,CAAAA,yBAAAA,MAAOjI,IAAI,MAAK,gBAAgBiI,MAAMxF,IAAI,EAAE;oCAC9C,MAAM;wCAAEzC,MAAM;wCAASyC,MAAMwF,MAAMxF,IAAI;oCAAC;gCAC1C,OAAO,IAAIwF,CAAAA,yBAAAA,MAAOjI,IAAI,MAAK,oBAAoB;wCAEtBiI;oCADvB,MAAMrF,OAAOD,MAAMgC,GAAG,CAAChB;oCACvB,IAAIf,MAAMA,KAAK2C,IAAI,KAAI0C,sBAAAA,MAAMC,YAAY,YAAlBD,sBAAsB;gCAC/C;gCACA;4BACF;wBACA,KAAK;4BAAsB;gCACzB,MAAMrF,OAAOD,MAAMgC,GAAG,CAAChB;gCACvB,IAAIf,MAAM;oCACRD,MAAMwF,MAAM,CAACxE;oCACboD,QAAQqB,IAAI,CAACxF,KAAK2C,IAAI;oCACtB,MAAM;wCAAEvF,MAAM;wCAAQ6C,MAAMD,KAAKC,IAAI;wCAAEpB,OAAO3C,cAAc8D,KAAK2C,IAAI;oCAAE;gCACzE;gCACA;4BACF;wBACA,KAAK;4BAAiB;oCAYJoC;gCAXhB,MAAMM,QAAQ9H,mBAAmBwH,KAAK,CAAC,QAAQ;gCAC/CvH,MAAMQ,YAAY,GAAGqH,MAAMrH,YAAY,IAAIR,MAAMQ,YAAY;gCAC7DR,MAAMO,WAAW,GAAGsH,MAAMtH,WAAW,IAAIP,MAAMO,WAAW;gCAC1DP,MAAMS,eAAe,GAAGoH,MAAMpH,eAAe,IAAIT,MAAMS,eAAe;gCACtET,MAAMU,gBAAgB,GAAGmH,MAAMnH,gBAAgB,IAAIV,MAAMU,gBAAgB;gCACzE,gEAAgE;gCAChE,8DAA8D;gCAC9D,+DAA+D;gCAC/D,gEAAgE;gCAChE,sDAAsD;gCACtD,IAAImH,MAAMjH,cAAc,KAAKR,WAAWJ,MAAMY,cAAc,GAAGiH,MAAMjH,cAAc;gCACnF,MAAMqH,UAAUV,eAAAA,KAAK,CAAC,QAAQ,qBAAf,AAACA,aACZrB,WAAW;gCACf,IAAI,OAAO+B,WAAW,YAAYA,QAAQhC,aAAagC;gCACvD;4BACF;wBACA,KAAK;4BAAS;gCACZ,MAAMtI,QAAQ4H,KAAK,CAAC,QAAQ;gCAG5BlC,QAAQ1F,KAAK,CAAC,4BAA4B;oCACxC2F,UAAU3G;oCACVuG;oCACApE;oCACAnB,KAAK,EAAEA,gBAAAA,QAAS;gCAClB;gCACA,MAAM;oCAAEC,MAAM;oCAASsI,WAAW3I,4BAA4B,MAAMI,yBAAAA,MAAOC,IAAI;gCAAE;gCACjF;4BACF;wBACA;4BACE;oBACJ;gBACF;YACF;QACF,SAAU;YACR,uEAAuE;YACvE,uEAAuE;YACvE,yEAAyE;YACzE,wBAAwB;YACxB,IAAI,CAACiH,SAAS,MAAA,uBAAML,OAAO2B,MAAM,GAAG/C,KAAK,CAAC,IAAMhF;YAChDoG,OAAO4B,WAAW;QACpB;QACA,uEAAuE;QACvE,yDAAyD;QACzD,KAAK,MAAM5F,QAAQD,MAAM8F,MAAM,GAAI1B,QAAQqB,IAAI,CAACxF,KAAK2C,IAAI;QACzD,MAAM;YACJvF,MAAM;YACNI;YACAgG,YAAYnF,mBAAmBb,OAAOc;YACtCmF;WAGIzH,mBAAmByH,cAAc;YAAEG,WAAW7H,cAAcoI,QAAQb,IAAI,CAAC;QAAK,IAAI,CAAC;IAE3F;;AAEA,yDAAyD,GACzD,OAAO,MAAMwC,+BAA+B,IAAG;AAE/C,SAASC,aAAa3D,MAAc;IAClC,OAAO;QACL,aAAaA;QACb,qBAAqB5F;QACrB,gBAAgB;IAClB;AACF;AAEA,mEAAmE,GACnE,eAAewJ,aAAanE,QAAkB,EAAEvD,KAAa;;QAcZmE;IAb/C,MAAMC,YAAYd,YAAYC;IAC9B,MAAMY,UAAW,MAAMZ,SAASc,IAAI,GAAGC,KAAK,CAAC,IAAM;IAGnDC,QAAQ1F,KAAK,CAAC,2BAA2B;QACvC2F,UAAU3G;QACVa,QAAQ6E,SAAS7E,MAAM;QACvB0F;QACApE;QACAnB,KAAK,UAAEsF,2BAAAA,QAAStF,KAAK,mBAAI;IAC3B;IACA,OAAO,IAAIrB,gBACT+F,SAAS7E,MAAM,EACfD,4BAA4B8E,SAAS7E,MAAM,EAAEyF,4BAAAA,iBAAAA,QAAStF,KAAK,qBAAdsF,eAAgBrF,IAAI,GACjEsF,WACAxF,wBAAwB2E,SAAS7E,MAAM,EAAEyF,2BAAAA,QAAStF,KAAK;AAE3D;AASA,wGAAwG,GACxG,MAAM8I,6BAA6B,IAAIC,IAAI;IAAC;IAAY;CAAU;AAElE;;;;;;;;;;;;;CAaC,GACD,eAAeC,cACbC,QAAsC,EACtCC,UAA0B,CAAC,CAAC;QAKbA,iBACGA,oBACIA;IALtB,IAAI,CAACD,SAASlH,MAAM,EAAE,OAAO,EAAE;IAC/B,MAAMkD,SAASgE,QAAQ,CAAC,EAAE,CAAChE,MAAM;IACjC,MAAM9D,QAAQ8H,QAAQ,CAAC,EAAE,CAAC9H,KAAK;IAC/B,MAAMgI,UAASD,kBAAAA,QAAQC,MAAM,YAAdD,kBAAkB;IACjC,MAAME,aAAYF,qBAAAA,QAAQE,SAAS,YAAjBF,qBAAqB,KAAK;IAC5C,MAAMG,iBAAgBH,yBAAAA,QAAQG,aAAa,YAArBH,yBAAyB,KAAK;IACpD,MAAMI,aAAa,CAACC,IAAYjB,SAAmBW,SAASxG,GAAG,CAAC,IAAM,IAAI/D,uBAAuB6K,IAAIjB;IACrG,MAAMkB,UAAU,MAAMzE,MAAM3F,uBAAuB;QACjD4F,QAAQ;QACRL,SAASiE,aAAa3D;QACtBH,MAAMI,KAAKC,SAAS,CAAC;YACnB8D,UAAUA,SAASxG,GAAG,CAAC,CAACgH,SAAS7F;gBAC/B,MAAM,EAAEqB,QAAQyE,IAAI,EAAEtE,QAAQuE,OAAO,EAAW,GAAGF,SAATG,wCAASH;;;;gBACnD,OAAO;oBAAEI,WAAW,CAAC,CAAC,EAAEjG,OAAO;oBAAEkG,QAAQrI,0BAA0B,aAAKmI;wBAAMlG,QAAQ;;gBAAS;YACjG;QACF;IACF;IACA,IAAI,CAAC8F,QAAQnE,EAAE,EAAE,MAAM,MAAMwD,aAAaW,SAASrI;IACnD,IAAI4I,QAAS,MAAMP,QAAQhE,IAAI;IAC/B,MAAMwE,WAAWC,KAAKC,GAAG,KAAKd;IAC9B,IAAIe,iBAAgC;IACpC,MAAOJ,MAAMK,iBAAiB,KAAK,QAAS;QAC1C,IAAID,mBAAmB,QAAQF,KAAKC,GAAG,MAAMF,UAAU;YACrDtE,QAAQ2E,IAAI,CAAC,CAAC,WAAW,EAAEN,MAAMR,EAAE,CAAC,qBAAqB,EAAEH,UAAU,0CAA0C,CAAC;YAChH,MAAMrE,MAAM,GAAG3F,sBAAsB,CAAC,EAAE2K,MAAMR,EAAE,CAAC,OAAO,CAAC,EAAE;gBAAEvE,QAAQ;gBAAQL,SAASiE,aAAa3D;YAAQ,GAAGQ,KAAK,CACjH,IAAMhF;YAER0J,iBAAiBF,KAAKC,GAAG,KAAKb;QAChC;QACA,0FAA0F;QAC1F,IAAIc,mBAAmB,QAAQF,KAAKC,GAAG,MAAMC,gBAAgB,OAAOb,WAAWS,MAAMR,EAAE,EAAE;QACzF,MAAM,IAAIe,QAAQ,CAACC,UAAYC,WAAWD,SAASpB;QACnD,MAAMsB,SAAS,MAAM1F,MAAM,GAAG3F,sBAAsB,CAAC,EAAE2K,MAAMR,EAAE,EAAE,EAAE;YAAE5E,SAASiE,aAAa3D;QAAQ;QACnG,IAAI,CAACwF,OAAOpF,EAAE,EAAE,MAAM,MAAMwD,aAAa4B,QAAQtJ;QACjD4I,QAAS,MAAMU,OAAOjF,IAAI;IAC5B;IACA,IAAI,CAACuE,MAAMW,WAAW,EAAE;QACtB,IAAIP,mBAAmB,MAAM,OAAOb,WAAWS,MAAMR,EAAE,EAAE;QACzD,MAAM,IAAIlH,MAAM,CAAC,MAAM,EAAE0H,MAAMR,EAAE,CAAC,sBAAsB,CAAC;IAC3D;IACA,MAAMoB,UAAU,MAAM5F,MAAMgF,MAAMW,WAAW,EAAE;QAAE/F,SAASiE,aAAa3D;IAAQ;IAC/E,IAAI,CAAC0F,QAAQtF,EAAE,EAAE,MAAM,MAAMwD,aAAa8B,SAASxJ;IACnD,MAAMyJ,OAAO,IAAI7D;IACjB,KAAK,MAAMW,QAAQ,AAAC,CAAA,MAAMiD,QAAQjI,IAAI,EAAC,EAAG8E,KAAK,CAAC,MAAO;YAG3BqD;QAF1B,IAAI,CAACnD,KAAKoD,IAAI,IAAI;QAClB,MAAMD,QAAQ3F,KAAK2C,KAAK,CAACH;QACzBkD,KAAK3C,GAAG,CAAC4C,MAAMhB,SAAS,GAAEgB,gBAAAA,MAAME,MAAM,YAAZF,gBAAgB,CAAC;IAC7C;IACA,OAAO5B,SAASxG,GAAG,CAAC,CAACgH,SAAS7F;;QAC5B,MAAMmH,SAASH,KAAKhG,GAAG,CAAC,CAAC,CAAC,EAAEhB,OAAO;QACnC,IAAImH,CAAAA,0BAAAA,OAAQ9K,IAAI,MAAK,aAAa;YAChC,OAAO4F,oBAAoBkF,OAAO5K,OAAO,EAAEsJ,QAAQtI,KAAK,EAAEwH;QAC5D;QACA,MAAM1I,eAAO8K,0BAAAA,OAAQ9K,IAAI,mBAAI;QAC7B,IAAI6I,2BAA2BkC,GAAG,CAAC/K,SAASA,SAAS,WAAW,OAAO,IAAIvB,uBAAuBqL,MAAMR,EAAE,EAAEtJ;QAC5GyF,QAAQ1F,KAAK,CAAC,iCAAiC;YAC7C2F,UAAU3G;YACViM,SAASlB,MAAMR,EAAE;YACjB2B,UAAU,CAAC,CAAC,EAAEtH,OAAO;YACrBmH,QAAQ9K;YACRD,KAAK,WAAE+K,0BAAAA,OAAQ/K,KAAK,oBAAI;QAC1B;QACA,OAAO,IAAIrB,gBAAgB,MAAMsB,SAAS,WAAW8J,MAAMR,EAAE,EAAE;IACjE;AACF;AAEA,OAAO,MAAM4B,oBAAgC;IAC3C5B,IAAIvK;IACJoM,OAAO;IACPC,WAAWpM;IACXqM,YAAY;YAAMC;eAAAA,EAAAA,iCAAAA,QAAQC,GAAG,CAACC,iBAAiB,qBAA7BF,+BAA+BT,IAAI,OAAMrK;;IAC3DiL,cAAcxM;IACdyM,kBAAkBpM;IAClBqM;QACE,OAAOrN,sBAAsBS,uBAC1ByD,GAAG,CAAC,CAAC8G,KAAOjL,eAAeiL,KAC3BrH,MAAM,CAAC,CAAC2I,QAA8CjJ,QAAQiJ;IACnE;IACAjF;IACAlC;IACAsF;AACF,EAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AiTokenRates } from './catalog';
|
|
18
|
+
import type { AiUsage } from './contract';
|
|
19
|
+
/**
|
|
20
|
+
* What an hour-long cache entry costs to write on top of what the rates
|
|
21
|
+
* price it at (AGL-3830). A rate table carries one write rate, the
|
|
22
|
+
* five-minute entry's 1.25 times the input price; the provider bills an
|
|
23
|
+
* hour-long entry at {@link AI_CACHE_WRITE_HOUR_FACTOR} times, so each token
|
|
24
|
+
* of `cacheWriteHourTokens` owes the difference. Kept out of the four-figure
|
|
25
|
+
* price on purpose: only a server reader can be handed an hour-long write,
|
|
26
|
+
* and the model selector that prices nominal usage never is. Its own module,
|
|
27
|
+
* not the catalog's, because the catalog loads on every console page and a
|
|
28
|
+
* helper beside it would load there too.
|
|
29
|
+
*/
|
|
30
|
+
export declare const AI_CACHE_WRITE_HOUR_FACTOR = 2;
|
|
31
|
+
export declare function aiCacheWriteHourSurchargeUsd(usage: Pick<AiUsage, 'cacheWriteHourTokens'>, rate: AiTokenRates): number;
|
|
32
|
+
/**
|
|
33
|
+
* The hour-long writes of two exchanges added up, as a spread: absent where
|
|
34
|
+
* neither reported any, so a usage object that never had the field keeps
|
|
35
|
+
* the shape it had.
|
|
36
|
+
*/
|
|
37
|
+
export declare function aiCacheWriteHourSum(a: Pick<AiUsage, 'cacheWriteHourTokens'>, b: Pick<AiUsage, 'cacheWriteHourTokens'>): Pick<AiUsage, 'cacheWriteHourTokens'>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* What an hour-long cache entry costs to write on top of what the rates
|
|
18
|
+
* price it at (AGL-3830). A rate table carries one write rate, the
|
|
19
|
+
* five-minute entry's 1.25 times the input price; the provider bills an
|
|
20
|
+
* hour-long entry at {@link AI_CACHE_WRITE_HOUR_FACTOR} times, so each token
|
|
21
|
+
* of `cacheWriteHourTokens` owes the difference. Kept out of the four-figure
|
|
22
|
+
* price on purpose: only a server reader can be handed an hour-long write,
|
|
23
|
+
* and the model selector that prices nominal usage never is. Its own module,
|
|
24
|
+
* not the catalog's, because the catalog loads on every console page and a
|
|
25
|
+
* helper beside it would load there too.
|
|
26
|
+
*/ export const AI_CACHE_WRITE_HOUR_FACTOR = 2;
|
|
27
|
+
export function aiCacheWriteHourSurchargeUsd(usage, rate) {
|
|
28
|
+
var _usage_cacheWriteHourTokens;
|
|
29
|
+
const tokens = Number((_usage_cacheWriteHourTokens = usage.cacheWriteHourTokens) != null ? _usage_cacheWriteHourTokens : 0);
|
|
30
|
+
if (!Number.isFinite(tokens) || tokens <= 0) return 0;
|
|
31
|
+
const usd = tokens * (rate.inputPerToken * AI_CACHE_WRITE_HOUR_FACTOR - rate.cacheWritePerToken);
|
|
32
|
+
return Math.round(usd * 1000000) / 1000000;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The hour-long writes of two exchanges added up, as a spread: absent where
|
|
36
|
+
* neither reported any, so a usage object that never had the field keeps
|
|
37
|
+
* the shape it had.
|
|
38
|
+
*/ export function aiCacheWriteHourSum(a, b) {
|
|
39
|
+
var _a_cacheWriteHourTokens, _b_cacheWriteHourTokens;
|
|
40
|
+
if (a.cacheWriteHourTokens === undefined && b.cacheWriteHourTokens === undefined) return {};
|
|
41
|
+
return {
|
|
42
|
+
cacheWriteHourTokens: ((_a_cacheWriteHourTokens = a.cacheWriteHourTokens) != null ? _a_cacheWriteHourTokens : 0) + ((_b_cacheWriteHourTokens = b.cacheWriteHourTokens) != null ? _b_cacheWriteHourTokens : 0)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=cache-write-hour.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/providers/cache-write-hour.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiTokenRates } from './catalog'\nimport type { AiUsage } from './contract'\n\n/**\n * What an hour-long cache entry costs to write on top of what the rates\n * price it at (AGL-3830). A rate table carries one write rate, the\n * five-minute entry's 1.25 times the input price; the provider bills an\n * hour-long entry at {@link AI_CACHE_WRITE_HOUR_FACTOR} times, so each token\n * of `cacheWriteHourTokens` owes the difference. Kept out of the four-figure\n * price on purpose: only a server reader can be handed an hour-long write,\n * and the model selector that prices nominal usage never is. Its own module,\n * not the catalog's, because the catalog loads on every console page and a\n * helper beside it would load there too.\n */\nexport const AI_CACHE_WRITE_HOUR_FACTOR = 2\n\nexport function aiCacheWriteHourSurchargeUsd(\n usage: Pick<AiUsage, 'cacheWriteHourTokens'>,\n rate: AiTokenRates,\n): number {\n const tokens = Number(usage.cacheWriteHourTokens ?? 0)\n if (!Number.isFinite(tokens) || tokens <= 0) return 0\n const usd = tokens * (rate.inputPerToken * AI_CACHE_WRITE_HOUR_FACTOR - rate.cacheWritePerToken)\n return Math.round(usd * 1_000_000) / 1_000_000\n}\n\n/**\n * The hour-long writes of two exchanges added up, as a spread: absent where\n * neither reported any, so a usage object that never had the field keeps\n * the shape it had.\n */\nexport function aiCacheWriteHourSum(\n a: Pick<AiUsage, 'cacheWriteHourTokens'>,\n b: Pick<AiUsage, 'cacheWriteHourTokens'>,\n): Pick<AiUsage, 'cacheWriteHourTokens'> {\n if (a.cacheWriteHourTokens === undefined && b.cacheWriteHourTokens === undefined) return {}\n return { cacheWriteHourTokens: (a.cacheWriteHourTokens ?? 0) + (b.cacheWriteHourTokens ?? 0) }\n}\n"],"names":["AI_CACHE_WRITE_HOUR_FACTOR","aiCacheWriteHourSurchargeUsd","usage","rate","tokens","Number","cacheWriteHourTokens","isFinite","usd","inputPerToken","cacheWritePerToken","Math","round","aiCacheWriteHourSum","a","b","undefined"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAKD;;;;;;;;;;CAUC,GACD,OAAO,MAAMA,6BAA6B,EAAC;AAE3C,OAAO,SAASC,6BACdC,KAA4C,EAC5CC,IAAkB;QAEID;IAAtB,MAAME,SAASC,QAAOH,8BAAAA,MAAMI,oBAAoB,YAA1BJ,8BAA8B;IACpD,IAAI,CAACG,OAAOE,QAAQ,CAACH,WAAWA,UAAU,GAAG,OAAO;IACpD,MAAMI,MAAMJ,SAAUD,CAAAA,KAAKM,aAAa,GAAGT,6BAA6BG,KAAKO,kBAAkB,AAAD;IAC9F,OAAOC,KAAKC,KAAK,CAACJ,MAAM,WAAa;AACvC;AAEA;;;;CAIC,GACD,OAAO,SAASK,oBACdC,CAAwC,EACxCC,CAAwC;QAGRD,yBAAgCC;IADhE,IAAID,EAAER,oBAAoB,KAAKU,aAAaD,EAAET,oBAAoB,KAAKU,WAAW,OAAO,CAAC;IAC1F,OAAO;QAAEV,sBAAsB,EAACQ,0BAAAA,EAAER,oBAAoB,YAAtBQ,0BAA0B,OAAMC,0BAAAA,EAAET,oBAAoB,YAAtBS,0BAA0B;IAAG;AAC/F"}
|
|
@@ -215,9 +215,11 @@ export interface AiRequestBase {
|
|
|
215
215
|
/**
|
|
216
216
|
* How long a cache entry the breakpoints write should live, where the
|
|
217
217
|
* provider offers a choice: the provider's default (five minutes on the
|
|
218
|
-
* Messages API) when absent.
|
|
219
|
-
* (`
|
|
220
|
-
*
|
|
218
|
+
* Messages API) when absent. A step a site job runs asks for `1h` through
|
|
219
|
+
* its routing row (`AiRoutingRow.cacheTtl`, AGL-3830), in production too,
|
|
220
|
+
* and a development live run asks for it on every request
|
|
221
|
+
* (`runtime/ai-dev-env.ts`). It changes no answer, only what a request
|
|
222
|
+
* within the hour pays for the prefix, so a replay key does not read it.
|
|
221
223
|
*/
|
|
222
224
|
cacheTtl?: '1h';
|
|
223
225
|
/**
|
|
@@ -245,6 +247,17 @@ export interface AiUsage {
|
|
|
245
247
|
outputTokens: number;
|
|
246
248
|
cacheReadTokens: number;
|
|
247
249
|
cacheWriteTokens: number;
|
|
250
|
+
/**
|
|
251
|
+
* How many of `cacheWriteTokens` went to an hour-long cache entry
|
|
252
|
+
* (AGL-3830), where the provider breaks the writes down; absent where none
|
|
253
|
+
* did or the provider does not say.
|
|
254
|
+
*
|
|
255
|
+
* Inside `cacheWriteTokens`, never beside it, like `thinkingTokens` below.
|
|
256
|
+
* An hour-long write is billed at twice the input price where a rate
|
|
257
|
+
* table's `cacheWritePerToken` is the five-minute 1.25 times, so a price
|
|
258
|
+
* reads this to add the difference (`aiCacheWriteHourSurchargeUsd`).
|
|
259
|
+
*/
|
|
260
|
+
cacheWriteHourTokens?: number;
|
|
248
261
|
/**
|
|
249
262
|
* How many of `outputTokens` the model spent thinking, where the provider
|
|
250
263
|
* reports it; absent where it does not (AGL-3143).
|