Skip to content

Commit bf58d73

Browse files
dmealingclaude
andcommitted
feat(ai)!: descope ai-runtime — remove vendor clients + builtinCost rate table (ADR-0024)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8c8860e commit bf58d73

11 files changed

Lines changed: 46 additions & 251 deletions

File tree

bun.lock

Lines changed: 0 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/typescript/packages/ai-runtime/package.json

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,6 @@
1111
"types": "./dist/index.d.ts",
1212
"default": "./dist/index.js"
1313
},
14-
"./anthropic": {
15-
"bun": "./src/anthropic.ts",
16-
"types": "./dist/anthropic.d.ts",
17-
"default": "./dist/anthropic.js"
18-
},
19-
"./openai": {
20-
"bun": "./src/openai.ts",
21-
"types": "./dist/openai.d.ts",
22-
"default": "./dist/openai.js"
23-
},
2414
"./langfuse": {
2515
"bun": "./src/langfuse.ts",
2616
"types": "./dist/langfuse.d.ts",
@@ -54,14 +44,10 @@
5444
"@metaobjectsdev/runtime-ts": "workspace:*"
5545
},
5646
"peerDependencies": {
57-
"@anthropic-ai/sdk": ">=0.30.0",
58-
"openai": ">=4.0.0",
5947
"langfuse": ">=3.0.0",
6048
"@opentelemetry/api": ">=1.0.0"
6149
},
6250
"peerDependenciesMeta": {
63-
"@anthropic-ai/sdk": { "optional": true },
64-
"openai": { "optional": true },
6551
"langfuse": { "optional": true },
6652
"@opentelemetry/api": { "optional": true }
6753
},

server/typescript/packages/ai-runtime/src/anthropic.ts

Lines changed: 0 additions & 66 deletions
This file was deleted.

server/typescript/packages/ai-runtime/src/call-loop.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
type LlmRequest,
1616
type LlmCompletion,
1717
} from "./client.js";
18-
import { builtinCost, type CostFn } from "./cost.js";
18+
import type { CostFn } from "./cost.js";
1919

2020
export interface RunLlmCallInput {
2121
/** Discriminator / call identity. */
@@ -54,7 +54,6 @@ export async function runLlmCall(
5454
): Promise<RunLlmCallResult> {
5555
const clock = deps.clock ?? systemClock;
5656
const ids = deps.ids ?? uuidIds;
57-
const cost = deps.cost ?? builtinCost;
5857

5958
const spanId = ids.next();
6059
const traceId = input.traceId ?? ids.next();
@@ -92,8 +91,8 @@ export async function runLlmCall(
9291
if (completion?.usage?.inputTokens !== undefined) recInput.inputTokens = completion.usage.inputTokens;
9392
if (completion?.usage?.outputTokens !== undefined) recInput.outputTokens = completion.usage.outputTokens;
9493
if (completion?.finishReason !== undefined) recInput.finishReason = completion.finishReason;
95-
if (completion !== undefined) {
96-
const c = cost(completion.model ?? input.request.model, completion.usage);
94+
if (completion !== undefined && deps.cost !== undefined) {
95+
const c = deps.cost(completion.model ?? input.request.model, completion.usage);
9796
if (c !== null) recInput.costMinor = c;
9897
}
9998

server/typescript/packages/ai-runtime/src/cost.ts

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,8 @@ import type { LlmUsage } from "./client.js";
22

33
/**
44
* Maps (model, usage) to a cost in integer USD minor units (cents), per the
5-
* field.currency wire contract. Returns null when the cost is unknown
6-
* (unknown model or missing usage) — never throws.
5+
* field.currency wire contract. Returns null when unknown — never throws. The
6+
* library ships NO rate table (ADR-0024); adopters supply their own CostFn
7+
* (from their LLM library's usage + their own rates) via `deps.cost`.
78
*/
89
export type CostFn = (model: string, usage: LlmUsage | undefined) => number | null;
9-
10-
/**
11-
* Best-effort static rate table: USD dollars per 1,000,000 tokens.
12-
* Intentionally small — NOT a maintained pricing oracle. Adopters override by
13-
* passing their own CostFn to callLlm. Public model identifiers only.
14-
*/
15-
const MODEL_RATES: Record<string, { inputPerM: number; outputPerM: number }> = {
16-
"gpt-4o": { inputPerM: 2.5, outputPerM: 10 },
17-
"gpt-4o-mini": { inputPerM: 0.15, outputPerM: 0.6 },
18-
"claude-3-5-sonnet": { inputPerM: 3, outputPerM: 15 },
19-
"claude-3-5-haiku": { inputPerM: 0.8, outputPerM: 4 },
20-
};
21-
22-
export const builtinCost: CostFn = (model, usage) => {
23-
if (usage === undefined) return null;
24-
const rate = MODEL_RATES[model];
25-
if (rate === undefined) return null;
26-
const inTok = usage.inputTokens ?? 0;
27-
const outTok = usage.outputTokens ?? 0;
28-
const dollars = (inTok * rate.inputPerM + outTok * rate.outputPerM) / 1_000_000;
29-
return Math.round(dollars * 100); // → integer cents
30-
};

server/typescript/packages/ai-runtime/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export {
1212
type IdGen,
1313
} from "./client.js";
1414

15-
export { builtinCost, type CostFn } from "./cost.js";
15+
export type { CostFn } from "./cost.js";
1616

1717
export {
1818
callLlm,

server/typescript/packages/ai-runtime/src/openai.ts

Lines changed: 0 additions & 51 deletions
This file was deleted.

server/typescript/packages/ai-runtime/test/anthropic.test.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

server/typescript/packages/ai-runtime/test/call-loop.test.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Capture extends NullRecorder {
1111
}
1212

1313
describe("callLlm", () => {
14-
test("happy path: CALL then persist the base row, captures latency/cost/ids", async () => {
14+
test("happy path: CALL then persist the base row, captures latency/ids", async () => {
1515
// Deterministic seams.
1616
let t = 1000;
1717
const clock: Clock = { now: () => (t += 500) };
@@ -40,7 +40,6 @@ describe("callLlm", () => {
4040
expect(row.callType).toBe("Verdict");
4141
expect(row.spanId).toBe("id1"); // first id → span
4242
expect(row.traceId).toBe("id2"); // second id → trace (none supplied)
43-
expect(row.costMinor).toBe(75); // builtinCost gpt-4o-mini @1M+1M
4443
expect(typeof row.latencyMs).toBe("number");
4544
expect(row.status).toBe("ok");
4645
expect(row.requestModel).toBe("gpt-4o-mini");
@@ -49,6 +48,42 @@ describe("callLlm", () => {
4948
expect("voResponse" in row).toBe(false);
5049
});
5150

51+
test("injected CostFn is called and costMinor is set", async () => {
52+
const client: LlmClient = {
53+
async complete(req) {
54+
return {
55+
body: JSON.stringify({ verdict: "ok" }),
56+
model: req.model,
57+
usage: { inputTokens: 1_000_000, outputTokens: 1_000_000 },
58+
};
59+
},
60+
};
61+
const rec = new Capture();
62+
await callLlm(
63+
{ callType: "X", request: { prompt: "p", model: "m" } },
64+
{ client, recorder: rec, cost: () => 75 },
65+
);
66+
expect(rec.rows[0]!.costMinor).toBe(75);
67+
});
68+
69+
test("no CostFn → costMinor is null (no built-in rate table)", async () => {
70+
const client: LlmClient = {
71+
async complete(req) {
72+
return {
73+
body: JSON.stringify({ verdict: "ok" }),
74+
model: req.model,
75+
usage: { inputTokens: 1_000_000, outputTokens: 1_000_000 },
76+
};
77+
},
78+
};
79+
const rec = new Capture();
80+
await callLlm(
81+
{ callType: "X", request: { prompt: "p", model: "m" } },
82+
{ client, recorder: rec },
83+
);
84+
expect(rec.rows[0]!.costMinor).toBeNull();
85+
});
86+
5287
test("supplied traceId is preserved (no new trace id)", async () => {
5388
let n = 0;
5489
const ids: IdGen = { next: () => `s${++n}` };
@@ -162,7 +197,8 @@ describe("runLlmCall", () => {
162197
expect(result.input.requestModel).toBe("gpt-4o-mini");
163198
expect(typeof result.input.latencyMs).toBe("number");
164199
expect(typeof result.input.startedAt).toBe("string");
165-
expect(result.input.costMinor).toBe(75);
200+
// No CostFn injected → costMinor absent on input (no built-in rate table, ADR-0024).
201+
expect(result.input.costMinor).toBeUndefined();
166202
expect(result.input.llmResponseText).toBe(JSON.stringify({ verdict: "ok" }));
167203
});
168204

server/typescript/packages/ai-runtime/test/cost.test.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)