Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion layers/identity/server/tasks/send-digests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { renderDigest } from '../utils/digest-template'
import { sendEmailWithEnv, signUnsubToken } from '../utils/email'

const CRON = '0 9 1 * *'
// Kill switch: the per-repo digest sentence runs `anthropic/claude-haiku-4.5`,
// Kill switch: the per-repo digest sentence runs `anthropic/claude-sonnet-5.5`,
// a partner model brokered through the Workers AI binding, so every call bills
// against AI Gateway credits. Those credits are exhausted, so each monthly run
// returned `provider_failure: 2021: Insufficient AI Gateway credits`, delivered
Expand Down
7 changes: 4 additions & 3 deletions layers/identity/server/utils/digest-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ export interface AiBinding {
messages: Array<{ role: 'user' | 'assistant', content: string }>
max_tokens: number
system?: string
temperature?: number
thinking?: { type: 'between_tools' }
}) => Promise<unknown>
}

// Anthropic Haiku 4.5 brokered through Workers AI. The binding handles auth;
// Anthropic Sonnet 5.5 brokered through Workers AI. The binding handles auth;
// no Anthropic key or gateway token needed. Swap to a llama/qwen if cost or
// latency need to drop later β€” caller doesn't care which model produced
// `response`.
const MODEL = 'anthropic/claude-haiku-4.5'
const MODEL = 'anthropic/claude-sonnet-5.5'

export interface SubscriptionContext {
owner: string
Expand Down Expand Up @@ -133,6 +133,7 @@ Output JSON only, no prose, with this exact shape:
system: systemPrompt,
messages: [{ role: 'user', content: userPrompt }],
max_tokens: 1024,
thinking: { type: 'between_tools' },
}).then(
value => ({ _tag: 'response' as const, value }),
error => ({
Expand Down
2 changes: 1 addition & 1 deletion layers/registry/server/jobs/generate-faqs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export async function generateFaqs(ctx: FaqContext, skill: FaqSkill): Promise<Fa
if (items.length < 3)
return null

const payload: FaqPayload = { items: items.slice(0, 6), model: 'claude-haiku-4-5-20251001' }
const payload: FaqPayload = { items: items.slice(0, 6), model: 'claude-haiku-5-5' }

await putGenerated(ctx.db, {
owner: skill.owner,
Expand Down
2 changes: 1 addition & 1 deletion layers/registry/server/jobs/generate-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export async function generateSummary(ctx: SummaryContext, skill: SummarySkill):
tagline: tagline.slice(0, 140),
blurb: blurb.slice(0, 400),
useCases,
model: 'claude-haiku-4-5-20251001',
model: 'claude-haiku-5-5',
}

await putGenerated(ctx.db, {
Expand Down
2 changes: 1 addition & 1 deletion layers/registry/server/jobs/generate-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function generateTags(ctx: TagContext, skill: TagSkill): Promise<Ta
if (!tags.length)
return null

const payload: TagPayload = { tags, model: 'claude-haiku-4-5-20251001' }
const payload: TagPayload = { tags, model: 'claude-haiku-5-5' }

await putGenerated(ctx.db, {
owner: skill.owner,
Expand Down
6 changes: 3 additions & 3 deletions layers/registry/server/tasks/ai-generate-poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ interface BatchResultLine {
}
}

// Haiku 4.5 batch pricing per million tokens (50% discount off realtime).
const HAIKU_BATCH_INPUT_USD_PER_MTOK = 0.50
const HAIKU_BATCH_OUTPUT_USD_PER_MTOK = 2.50
// Haiku 5.5 batch pricing per million tokens for prompts under 100k tokens.
const HAIKU_BATCH_INPUT_USD_PER_MTOK = 0.05
const HAIKU_BATCH_OUTPUT_USD_PER_MTOK = 0.25

interface BatchStatusResponse {
id: string
Expand Down
3 changes: 2 additions & 1 deletion layers/registry/server/tasks/ai-generate-submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const BATCH_LIMIT = 50
// ceiling observed in production; wall time goes 7s β†’ ~18s, still well
// under the scheduled-handler budget.
const AI_CONCURRENCY = 3
const HAIKU_MODEL = 'claude-haiku-4-5-20251001'
const HAIKU_MODEL = 'claude-haiku-5-5'
// Kill switch: pause Anthropic Haiku batch spend while we investigate the
// scaled-content-abuse deindexing (10k AI pages β†’ 0.6% indexed, sitewide
// demotion late Apr 2026). Embeddings/abstractness (Workers AI, ~free) keep
Expand Down Expand Up @@ -265,6 +265,7 @@ async function runSubmit(db: D1Database, ai: AiBinding | undefined, vectorize: V
params: {
model: HAIKU_MODEL,
max_tokens: kind === 'faq' ? 1024 : kind === 'tags' ? 256 : 512,
thinking: { type: 'disabled' },
system: [{
type: 'text',
text: SHARED_SYSTEM_PROMPT,
Expand Down
2 changes: 1 addition & 1 deletion layers/registry/server/utils/ai-prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Anthropic's prompt caching (5-min ephemeral TTL) materially cuts cost when
* a batch submits dozens of skills in one request.
*
* Three derivation kinds run through Haiku 4.5 Batch API:
* Three derivation kinds run through Haiku 5.5 Batch API:
* - `summary` plain text, 2-3 sentences
* - `tags` JSON array of 5-10 lowercase tags
* - `faq` JSON array of 3-5 { question, answer } objects
Expand Down
7 changes: 4 additions & 3 deletions shared/server/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { spawn } from 'node:child_process'
const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'
const ANTHROPIC_VERSION = '2023-06-01'

export type HaikuModel = 'claude-haiku-4-5-20251001'
export type HaikuModel = 'claude-haiku-5-5'

export interface ClaudeUsage {
input_tokens: number
Expand Down Expand Up @@ -61,7 +61,7 @@ export async function callHaikuApi(opts: CallHaikuOpts & { apiKey: string }): Pr
systemPrompt,
userPrompt,
apiKey,
model = 'claude-haiku-4-5-20251001',
model = 'claude-haiku-5-5',
maxTokens = 1024,
cacheSystem = true,
} = opts
Expand All @@ -80,6 +80,7 @@ export async function callHaikuApi(opts: CallHaikuOpts & { apiKey: string }): Pr
body: JSON.stringify({
model,
max_tokens: maxTokens,
thinking: { type: 'disabled' },
system,
messages: [{ role: 'user', content: userPrompt }],
}),
Expand All @@ -105,7 +106,7 @@ export async function callHaikuApi(opts: CallHaikuOpts & { apiKey: string }): Pr
* so we can read structured usage info. Returns the same shape as the API call.
*/
export async function callHaikuCli(opts: CallHaikuOpts): Promise<ClaudeTextResponse> {
const { systemPrompt, userPrompt, model = 'claude-haiku-4-5-20251001' } = opts
const { systemPrompt, userPrompt, model = 'claude-haiku-5-5' } = opts

// CC wraps/injects its own framing around --system-prompt, so Haiku tends
// to stay conversational. For batch jobs we fold the system prompt into the
Expand Down
5 changes: 3 additions & 2 deletions workers/skill-harness/src/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ export async function forwardSandboxRequest(
if (!await options.consumeModelCall())
return Response.json({ code: 'MODEL_CALL_LIMIT' }, { status: 403 })
const standardInput = { ...input }
delete standardInput.speed
for (const field of ['speed', 'temperature', 'top_p', 'top_k'])
delete standardInput[field]
const thinking = input.thinking && typeof input.thinking === 'object' && 'type' in input.thinking && input.thinking.type === 'enabled'
? { type: 'enabled', budget_tokens: 2048 }
? { type: 'adaptive' }
: input.thinking
return fetchClient('https://api.anthropic.com/v1/messages', {
method: 'POST',
Expand Down
9 changes: 5 additions & 4 deletions workers/skill-harness/test/gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,19 @@ describe('sandbox credential gateway', () => {
})

it('routes Anthropic with Worker credentials and enforced model limits', async () => {
const options = { ...fixture(), provider: 'anthropic' as const, model: 'claude-sonnet-4-6' }
const options = { ...fixture(), provider: 'anthropic' as const, model: 'claude-sonnet-5-5' }
const response = await forwardSandboxRequest(new Request('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: { 'x-api-key': 'container-key', 'anthropic-beta': 'untrusted-beta' },
body: JSON.stringify({ model: 'expensive-model', max_tokens: 32000, service_tier: 'priority', speed: 'fast', messages: [], thinking: { type: 'enabled', budget_tokens: 16000 } }),
body: JSON.stringify({ model: 'expensive-model', max_tokens: 32000, service_tier: 'priority', speed: 'fast', temperature: 0.3, top_p: 0.9, top_k: 20, messages: [], thinking: { type: 'enabled', budget_tokens: 16000 } }),
}), options)
expect(response.status).toBe(200)
const [, forwarded] = options.fetch.mock.calls[0]!
expect(new Headers(forwarded?.headers).get('x-api-key')).toBe('worker-only-secret')
expect(new Headers(forwarded?.headers).get('anthropic-beta')).toBeNull()
expect(JSON.parse(String(forwarded?.body))).not.toHaveProperty('speed')
expect(JSON.parse(String(forwarded?.body))).toMatchObject({ model: 'claude-sonnet-4-6', max_tokens: 8192, service_tier: 'standard_only', thinking: { type: 'enabled', budget_tokens: 2048 } })
for (const field of ['speed', 'temperature', 'top_p', 'top_k'])
expect(JSON.parse(String(forwarded?.body))).not.toHaveProperty(field)
expect(JSON.parse(String(forwarded?.body))).toMatchObject({ model: 'claude-sonnet-5-5', max_tokens: 8192, service_tier: 'standard_only', thinking: { type: 'adaptive' } })
})

it.each([{ tools: [{ type: 'web_search_20250305' }] }, { mcp_servers: [{ url: 'https://external.example' }] }])('blocks Anthropic hosted execution %j', async (body) => {
Expand Down
Loading