llm-relay 0.0.1

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.
@@ -0,0 +1,18 @@
1
+ import { type AssistantMessage } from "./anthropic.js";
2
+ /**
3
+ * Serialize an AssistantMessage into an Anthropic SSE byte string — the inverse
4
+ * of reconstructFromSse. Used by repair mode to re-emit a reshaped (or original)
5
+ * response as a fresh stream when the client asked for streaming. Emits the
6
+ * event choreography the Claude harness expects:
7
+ * message_start → (content_block_start → delta → stop)* → message_delta → message_stop
8
+ */
9
+ export declare function emitSse(msg: AssistantMessage): string;
10
+ /**
11
+ * Serialize ONLY the trailing content blocks (from `startIndex` onward) plus the
12
+ * message terminators — no `message_start`, no earlier blocks. Used by streaming
13
+ * repair: the proxy has already forwarded `message_start` and any leading text
14
+ * blocks byte-for-byte, then withheld from the first tool_use block. When a repair
15
+ * lands, this re-emits just the (corrected) tool_use blocks at their original
16
+ * indices so the client sees one coherent, contiguous stream.
17
+ */
18
+ export declare function emitSseTail(msg: AssistantMessage, startIndex: number): string;
@@ -0,0 +1,88 @@
1
+ import { isToolUseBlock } from "./anthropic.js";
2
+ /**
3
+ * Serialize an AssistantMessage into an Anthropic SSE byte string — the inverse
4
+ * of reconstructFromSse. Used by repair mode to re-emit a reshaped (or original)
5
+ * response as a fresh stream when the client asked for streaming. Emits the
6
+ * event choreography the Claude harness expects:
7
+ * message_start → (content_block_start → delta → stop)* → message_delta → message_stop
8
+ */
9
+ export function emitSse(msg) {
10
+ const out = [];
11
+ const push = (type, data) => {
12
+ out.push(`event: ${type}\ndata: ${JSON.stringify({ type, ...data })}\n\n`);
13
+ };
14
+ const outputTokens = msg.usage?.output_tokens ?? 0;
15
+ const inputTokens = msg.usage?.input_tokens ?? 0;
16
+ push("message_start", {
17
+ message: {
18
+ id: "msg_repair",
19
+ type: "message",
20
+ role: "assistant",
21
+ model: "",
22
+ content: [],
23
+ stop_reason: null,
24
+ stop_sequence: null,
25
+ usage: { input_tokens: inputTokens, output_tokens: 0 },
26
+ },
27
+ });
28
+ msg.content.forEach((block, index) => {
29
+ emitBlock(push, block, index);
30
+ });
31
+ push("message_delta", {
32
+ delta: { stop_reason: msg.stop_reason ?? "end_turn", stop_sequence: null },
33
+ usage: { output_tokens: outputTokens },
34
+ });
35
+ push("message_stop", {});
36
+ return out.join("");
37
+ }
38
+ /**
39
+ * Serialize ONLY the trailing content blocks (from `startIndex` onward) plus the
40
+ * message terminators — no `message_start`, no earlier blocks. Used by streaming
41
+ * repair: the proxy has already forwarded `message_start` and any leading text
42
+ * blocks byte-for-byte, then withheld from the first tool_use block. When a repair
43
+ * lands, this re-emits just the (corrected) tool_use blocks at their original
44
+ * indices so the client sees one coherent, contiguous stream.
45
+ */
46
+ export function emitSseTail(msg, startIndex) {
47
+ const out = [];
48
+ const push = (type, data) => {
49
+ out.push(`event: ${type}\ndata: ${JSON.stringify({ type, ...data })}\n\n`);
50
+ };
51
+ msg.content.forEach((block, index) => {
52
+ if (index < startIndex)
53
+ return;
54
+ emitBlock(push, block, index);
55
+ });
56
+ push("message_delta", {
57
+ delta: { stop_reason: msg.stop_reason ?? "end_turn", stop_sequence: null },
58
+ usage: { output_tokens: msg.usage?.output_tokens ?? 0 },
59
+ });
60
+ push("message_stop", {});
61
+ return out.join("");
62
+ }
63
+ function emitBlock(push, block, index) {
64
+ if (isToolUseBlock(block)) {
65
+ push("content_block_start", {
66
+ index,
67
+ content_block: { type: "tool_use", id: block.id, name: block.name, input: {} },
68
+ });
69
+ // Emit the full input as a single input_json_delta fragment.
70
+ const json = JSON.stringify(block.input ?? {});
71
+ push("content_block_delta", { index, delta: { type: "input_json_delta", partial_json: json } });
72
+ push("content_block_stop", { index });
73
+ return;
74
+ }
75
+ if (block.type === "text") {
76
+ push("content_block_start", { index, content_block: { type: "text", text: "" } });
77
+ push("content_block_delta", {
78
+ index,
79
+ delta: { type: "text_delta", text: block.text },
80
+ });
81
+ push("content_block_stop", { index });
82
+ return;
83
+ }
84
+ // Opaque block: pass through as a start/stop with the block echoed.
85
+ push("content_block_start", { index, content_block: block });
86
+ push("content_block_stop", { index });
87
+ }
88
+ //# sourceMappingURL=emitSse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitSse.js","sourceRoot":"","sources":["../src/emitSse.ts"],"names":[],"mappings":"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"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { Config } from "./config.js";
2
+ /**
3
+ * Metadata-only request log. NEVER records headers or bodies — only the shape of
4
+ * what happened. This is the dataset that reveals which backend models trip the
5
+ * validator (format-broken, reshapeable) vs pass cleanly.
6
+ */
7
+ export interface RequestLog {
8
+ ts: string;
9
+ path: string;
10
+ backendModel: string | null;
11
+ hadTools: boolean;
12
+ streamed: boolean;
13
+ backendStatus: number;
14
+ validated: "pass" | "fail" | "uncheckable" | "skipped";
15
+ toolUseCount: number;
16
+ uncheckableCount: number;
17
+ errorKinds: string[];
18
+ /** Repair outcome (repair mode only); "none" when repair did not run. */
19
+ repair: "none" | "fixed" | "failed" | "refused" | "refused_destructive";
20
+ latencyMs: number;
21
+ }
22
+ export declare class MetadataLogger {
23
+ private readonly cfg;
24
+ constructor(cfg: Config["log"]);
25
+ write(record: RequestLog): void;
26
+ }
package/dist/log.js ADDED
@@ -0,0 +1,24 @@
1
+ import { appendFileSync } from "node:fs";
2
+ export class MetadataLogger {
3
+ cfg;
4
+ constructor(cfg) {
5
+ this.cfg = cfg;
6
+ }
7
+ write(record) {
8
+ if (this.cfg.level === "silent")
9
+ return;
10
+ const line = JSON.stringify(record);
11
+ if (this.cfg.file) {
12
+ try {
13
+ appendFileSync(this.cfg.file, line + "\n");
14
+ }
15
+ catch {
16
+ process.stderr.write(line + "\n");
17
+ }
18
+ }
19
+ else {
20
+ process.stdout.write(line + "\n");
21
+ }
22
+ }
23
+ }
24
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAwBzC,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAEnD,KAAK,CAAC,MAAkB;QACtB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ import type { Config } from "./config.js";
2
+ import type { ModelCatalog } from "./catalog.js";
3
+ interface CapabilityScore {
4
+ bfcl_overall: number | null;
5
+ bfcl_multi_turn: number | null;
6
+ bfcl_irrelevance: number | null;
7
+ arena_rating: number | null;
8
+ arena_rank: number | null;
9
+ composite_rank: number | null;
10
+ }
11
+ interface RegistryModel {
12
+ id: string;
13
+ /** Best-effort leaderboard match (null when no confident match — matching is fuzzy). */
14
+ capability: CapabilityScore | null;
15
+ }
16
+ interface RegistryProvider {
17
+ base: string;
18
+ kind: "openai" | "anthropic";
19
+ authEnv?: string;
20
+ /** Whether the provider's auth env var is set (a provider can be configured but keyless). */
21
+ has_key: boolean;
22
+ /** openai providers only: did the live /models catalog return anything (reachable + authorized)? */
23
+ reachable: boolean | null;
24
+ models: RegistryModel[];
25
+ }
26
+ export interface RegistryView {
27
+ generated_at: string;
28
+ /** How to address a backend: namespace a request model "provider/model" (audit-tools names the
29
+ * exact model), or let routing map by Claude tier / default (dumb-client convenience). */
30
+ routing: {
31
+ default: string;
32
+ tiers: Record<string, string>;
33
+ };
34
+ providers: Record<string, RegistryProvider>;
35
+ capability_source: {
36
+ present: boolean;
37
+ synced_at?: string;
38
+ note: string;
39
+ /** Full raw leaderboard dataset, so a consumer can run a finer join than the best-effort one above. */
40
+ models?: Array<Record<string, unknown>>;
41
+ };
42
+ }
43
+ /** Build the discovery view: providers × live models (best-effort capability) + routing + raw scores. */
44
+ export declare function buildRegistry(cfg: Config, catalog: ModelCatalog, opts?: {
45
+ now?: string;
46
+ }): Promise<RegistryView>;
47
+ export {};
@@ -0,0 +1,80 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ function loadTierData() {
4
+ try {
5
+ const path = fileURLToPath(new URL("../docs/tier-data.json", import.meta.url));
6
+ const j = JSON.parse(readFileSync(path, "utf8"));
7
+ return { ...(j.synced_at ? { synced_at: j.synced_at } : {}), models: Array.isArray(j.models) ? j.models : [] };
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ function toScore(r) {
14
+ const n = (k) => (typeof r[k] === "number" ? r[k] : null);
15
+ return {
16
+ bfcl_overall: n("bfcl_overall"),
17
+ bfcl_multi_turn: n("bfcl_multi_turn"),
18
+ bfcl_irrelevance: n("bfcl_irrelevance"),
19
+ arena_rating: n("arena_rating"),
20
+ arena_rank: n("arena_rank"),
21
+ composite_rank: n("composite_rank"),
22
+ };
23
+ }
24
+ /**
25
+ * Fuzzy, conservative join of a backend model id to a leaderboard record. Leaderboard
26
+ * names ("GLM-4.6 (FC)") and API ids ("z-ai/glm-5.2") don't share a key, so this matches
27
+ * the API id's last path segment against a normalized leaderboard name — preferring a
28
+ * miss (null capability) over a wrong score. Consumers wanting a better join use the full
29
+ * dataset in capability_source.models.
30
+ */
31
+ function joinCapability(modelId, byNorm) {
32
+ const seg = (modelId.split("/").pop() ?? modelId).toLowerCase().trim();
33
+ if (seg.length < 5)
34
+ return null;
35
+ const exact = byNorm.find((e) => e.norm === seg);
36
+ if (exact)
37
+ return toScore(exact.rec);
38
+ const contained = byNorm.find((e) => e.norm.includes(seg));
39
+ return contained ? toScore(contained.rec) : null;
40
+ }
41
+ /** Build the discovery view: providers × live models (best-effort capability) + routing + raw scores. */
42
+ export async function buildRegistry(cfg, catalog, opts = {}) {
43
+ const tierData = loadTierData();
44
+ const byNorm = (tierData?.models ?? [])
45
+ .filter((r) => typeof r.norm === "string")
46
+ .map((r) => ({ norm: r.norm.toLowerCase(), rec: r }));
47
+ const providers = {};
48
+ for (const [name, p] of Object.entries(cfg.providers)) {
49
+ const has_key = p.authEnv ? !!process.env[p.authEnv]?.trim() : true;
50
+ let models = [];
51
+ let reachable = p.kind === "openai" ? false : null;
52
+ if (p.kind === "openai") {
53
+ const ids = await catalog.list(name, p);
54
+ reachable = ids.length > 0;
55
+ models = ids.map((id) => ({ id, capability: joinCapability(id, byNorm) }));
56
+ }
57
+ providers[name] = {
58
+ base: p.base,
59
+ kind: p.kind,
60
+ ...(p.authEnv ? { authEnv: p.authEnv } : {}),
61
+ has_key,
62
+ reachable,
63
+ models,
64
+ };
65
+ }
66
+ return {
67
+ generated_at: opts.now ?? new Date().toISOString(),
68
+ routing: { default: cfg.routing.default, tiers: cfg.routing.tiers },
69
+ providers,
70
+ capability_source: {
71
+ present: !!tierData,
72
+ ...(tierData?.synced_at ? { synced_at: tierData.synced_at } : {}),
73
+ note: tierData
74
+ ? "Raw BFCL + LMArena scores, never collapsed. Per-model `capability` is a best-effort id→leaderboard match; use `models` for a finer join."
75
+ : "No docs/tier-data.json — run `npm run sync:tiers`. Providers/models still returned.",
76
+ ...(tierData ? { models: tierData.models } : {}),
77
+ },
78
+ };
79
+ }
80
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type AssistantMessage, type JsonSchema } from "./anthropic.js";
2
+ import { type ToolUseValidator } from "./validator.js";
3
+ import { type Reshaper } from "./reshaper.js";
4
+ export type RepairOutcome = "fixed" | "failed" | "refused" | "refused_destructive";
5
+ export interface RepairDecision {
6
+ outcome: RepairOutcome;
7
+ message?: AssistantMessage;
8
+ }
9
+ export interface RepairDeps {
10
+ validator: ToolUseValidator;
11
+ reshaper: Reshaper;
12
+ maxAttempts: number;
13
+ isDestructive: (toolName: string) => boolean;
14
+ }
15
+ /**
16
+ * Attempt to repair an invalid tool-bearing assistant message. Safety-first:
17
+ * if ANY tool_use in the (invalid) response targets a destructive tool, we
18
+ * REFUSE rather than fabricate its arguments — because repair output may run
19
+ * under --dangerously-skip-permissions. Otherwise reshape ≤ maxAttempts,
20
+ * re-validating each attempt; a reshape that doesn't validate is never emitted.
21
+ */
22
+ export declare function repair(assistant: AssistantMessage, tools: Map<string, JsonSchema | null>, deps: RepairDeps): Promise<RepairDecision>;
23
+ /** Build a destructive-tool matcher from name patterns (case-insensitive substring). */
24
+ export declare function destructiveMatcher(namePatterns: string[]): (name: string) => boolean;
package/dist/repair.js ADDED
@@ -0,0 +1,44 @@
1
+ import { isToolUseBlock } from "./anthropic.js";
2
+ /**
3
+ * Attempt to repair an invalid tool-bearing assistant message. Safety-first:
4
+ * if ANY tool_use in the (invalid) response targets a destructive tool, we
5
+ * REFUSE rather than fabricate its arguments — because repair output may run
6
+ * under --dangerously-skip-permissions. Otherwise reshape ≤ maxAttempts,
7
+ * re-validating each attempt; a reshape that doesn't validate is never emitted.
8
+ */
9
+ export async function repair(assistant, tools, deps) {
10
+ const destructiveHit = assistant.content
11
+ .filter(isToolUseBlock)
12
+ .some((b) => deps.isDestructive(b.name));
13
+ if (destructiveHit) {
14
+ return { outcome: "refused_destructive" };
15
+ }
16
+ let errors = deps.validator.validate(assistant, tools).errors;
17
+ let current = assistant;
18
+ for (let attempt = 0; attempt < deps.maxAttempts; attempt++) {
19
+ const result = await deps.reshaper.reshape({
20
+ tools,
21
+ rawAssistant: current,
22
+ errors,
23
+ backendModel: null,
24
+ });
25
+ if (result.kind === "refuse")
26
+ return { outcome: "refused" };
27
+ const check = deps.validator.validate(result.message, tools);
28
+ if (check.valid)
29
+ return { outcome: "fixed", message: result.message };
30
+ // Feed the new errors back into the next attempt.
31
+ errors = check.errors;
32
+ current = result.message;
33
+ }
34
+ return { outcome: "failed" };
35
+ }
36
+ /** Build a destructive-tool matcher from name patterns (case-insensitive substring). */
37
+ export function destructiveMatcher(namePatterns) {
38
+ const lowered = namePatterns.map((p) => p.toLowerCase());
39
+ return (name) => {
40
+ const n = name.toLowerCase();
41
+ return lowered.some((p) => n.includes(p));
42
+ };
43
+ }
44
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../src/repair.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,cAAc,EAAmB,MAAM,gBAAgB,CAAC;AAsBxF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,SAA2B,EAC3B,KAAqC,EACrC,IAAgB;IAEhB,MAAM,cAAc,GAAG,SAAS,CAAC,OAAO;SACrC,MAAM,CAAC,cAAc,CAAC;SACtB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC;IAC9D,IAAI,OAAO,GAAG,SAAS,CAAC;IAExB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACzC,KAAK;YACL,YAAY,EAAE,OAAO;YACrB,MAAM;YACN,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;QAE5D,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC7D,IAAI,KAAK,CAAC,KAAK;YAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACtE,kDAAkD;QAClD,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QACtB,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,kBAAkB,CAAC,YAAsB;IACvD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,IAAY,EAAE,EAAE;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC7B,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { type AssistantMessage, type JsonSchema } from "./anthropic.js";
2
+ import { type ValidationError } from "./validator.js";
3
+ export interface ReshapeRequest {
4
+ /** The declared tools (name → schema|null) so the reshaper knows the contract. */
5
+ tools: Map<string, JsonSchema | null>;
6
+ /** The backend's failing assistant message. */
7
+ rawAssistant: AssistantMessage;
8
+ /** Why it failed validation. */
9
+ errors: ValidationError[];
10
+ backendModel: string | null;
11
+ }
12
+ export type ReshapeResult = {
13
+ kind: "message";
14
+ message: AssistantMessage;
15
+ } | {
16
+ kind: "refuse";
17
+ reason: string;
18
+ };
19
+ export interface Reshaper {
20
+ reshape(req: ReshapeRequest): Promise<ReshapeResult>;
21
+ }
22
+ export type CorrectedInputs = {
23
+ kind: "inputs";
24
+ inputs: Record<string, unknown>;
25
+ } | {
26
+ kind: "refuse";
27
+ reason: string;
28
+ };
29
+ /** Parse the reshaper model's text into a per-id corrected-inputs map. */
30
+ export declare function parseCorrectedInputs(text: string): CorrectedInputs;
31
+ /** Rebuild the assistant message, replacing each failing tool_use's input by id. */
32
+ export declare function reconstruct(raw: AssistantMessage, inputs: Record<string, unknown>): AssistantMessage;
33
+ /** Reshaper backed by an Anthropic- or OpenAI-compatible endpoint. */
34
+ export declare class HttpReshaper implements Reshaper {
35
+ private readonly cfg;
36
+ private readonly fetchFn;
37
+ constructor(cfg: {
38
+ base: string;
39
+ model: string;
40
+ kind: "anthropic" | "openai";
41
+ authEnv?: string;
42
+ authHeader: "x-api-key" | "authorization";
43
+ timeoutMs: number;
44
+ }, fetchFn?: typeof fetch);
45
+ reshape(req: ReshapeRequest): Promise<ReshapeResult>;
46
+ }
@@ -0,0 +1,147 @@
1
+ import { DEFAULT_ANTHROPIC_VERSION } from "./config.js";
2
+ import { isToolUseBlock } from "./anthropic.js";
3
+ /**
4
+ * The reshaper only has to produce the corrected ARGUMENTS per tool-call id — NOT
5
+ * the whole Anthropic content envelope. This drastically lowers the formatting
6
+ * burden on weaker reshaper models (empirically far more reliable than asking
7
+ * them to regenerate the full message), and the proxy reconstructs the message.
8
+ */
9
+ const SYSTEM_PROMPT = `You fix the ARGUMENTS of malformed tool calls so they satisfy a given JSON schema.
10
+
11
+ RULES:
12
+ - Preserve the model's EXPRESSED intent. Reconstruct arguments ONLY from what is present in the malformed call — do NOT invent values.
13
+ - If you cannot fix a call without guessing, refuse.
14
+ - Output ONLY one raw JSON object (no prose, no markdown fences), mapping each tool_use id to its corrected "input" object:
15
+ {"inputs": {"<tool_use_id>": { ...corrected input satisfying the schema... }}}
16
+ or, if you must guess: {"refuse": true, "reason": "<why>"}`;
17
+ function buildUserContent(req) {
18
+ const toolList = [...req.tools.entries()].map(([name, input_schema]) => ({ name, input_schema }));
19
+ const failing = req.rawAssistant.content
20
+ .filter(isToolUseBlock)
21
+ .map((b) => ({ id: b.id, name: b.name, current_input: b.input }));
22
+ return JSON.stringify({
23
+ tools: toolList,
24
+ failing_tool_calls: failing,
25
+ validation_errors: req.errors.map((e) => ({ tool: e.tool, message: e.message })),
26
+ }, null, 2);
27
+ }
28
+ /** Parse the reshaper model's text into a per-id corrected-inputs map. */
29
+ export function parseCorrectedInputs(text) {
30
+ const json = extractJson(text);
31
+ if (typeof json !== "object" || json === null) {
32
+ return { kind: "refuse", reason: "reshaper returned no parseable JSON" };
33
+ }
34
+ const obj = json;
35
+ if (obj.refuse === true) {
36
+ return { kind: "refuse", reason: typeof obj.reason === "string" ? obj.reason : "refused" };
37
+ }
38
+ if (typeof obj.inputs === "object" && obj.inputs !== null) {
39
+ return { kind: "inputs", inputs: obj.inputs };
40
+ }
41
+ return { kind: "refuse", reason: "reshaper output was not a recognized shape" };
42
+ }
43
+ /** Rebuild the assistant message, replacing each failing tool_use's input by id. */
44
+ export function reconstruct(raw, inputs) {
45
+ const content = raw.content.map((b) => isToolUseBlock(b) && Object.prototype.hasOwnProperty.call(inputs, b.id)
46
+ ? { ...b, input: inputs[b.id] }
47
+ : b);
48
+ return { content, stop_reason: raw.stop_reason ?? "tool_use" };
49
+ }
50
+ /** Reshaper backed by an Anthropic- or OpenAI-compatible endpoint. */
51
+ export class HttpReshaper {
52
+ cfg;
53
+ fetchFn;
54
+ constructor(cfg, fetchFn = fetch) {
55
+ this.cfg = cfg;
56
+ this.fetchFn = fetchFn;
57
+ }
58
+ async reshape(req) {
59
+ const controller = new AbortController();
60
+ const timer = setTimeout(() => controller.abort(), this.cfg.timeoutMs);
61
+ try {
62
+ const headers = { "content-type": "application/json" };
63
+ const key = this.cfg.authEnv ? process.env[this.cfg.authEnv]?.trim() : undefined;
64
+ if (key) {
65
+ if (this.cfg.authHeader === "authorization")
66
+ headers["authorization"] = `Bearer ${key}`;
67
+ else
68
+ headers["x-api-key"] = key;
69
+ }
70
+ const userContent = buildUserContent(req);
71
+ const { url, body } = this.cfg.kind === "openai"
72
+ ? {
73
+ url: `${this.cfg.base}/chat/completions`,
74
+ body: {
75
+ model: this.cfg.model,
76
+ max_tokens: 1024,
77
+ messages: [
78
+ { role: "system", content: SYSTEM_PROMPT },
79
+ { role: "user", content: userContent },
80
+ ],
81
+ },
82
+ }
83
+ : {
84
+ url: `${this.cfg.base}/v1/messages`,
85
+ body: {
86
+ model: this.cfg.model,
87
+ max_tokens: 1024,
88
+ system: SYSTEM_PROMPT,
89
+ messages: [{ role: "user", content: userContent }],
90
+ },
91
+ };
92
+ if (this.cfg.kind === "anthropic")
93
+ headers["anthropic-version"] = DEFAULT_ANTHROPIC_VERSION;
94
+ const res = await this.fetchFn(url, {
95
+ method: "POST",
96
+ headers,
97
+ body: JSON.stringify(body),
98
+ signal: controller.signal,
99
+ });
100
+ if (!res.ok)
101
+ return { kind: "refuse", reason: `reshaper HTTP ${res.status}` };
102
+ const json = (await res.json());
103
+ const text = this.cfg.kind === "openai" ? openaiText(json) : anthropicText(json);
104
+ const parsed = parseCorrectedInputs(text);
105
+ if (parsed.kind === "refuse")
106
+ return { kind: "refuse", reason: parsed.reason };
107
+ return { kind: "message", message: reconstruct(req.rawAssistant, parsed.inputs) };
108
+ }
109
+ catch (e) {
110
+ return { kind: "refuse", reason: `reshaper error: ${e.message}` };
111
+ }
112
+ finally {
113
+ clearTimeout(timer);
114
+ }
115
+ }
116
+ }
117
+ function anthropicText(json) {
118
+ const content = json.content ?? [];
119
+ return content.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
120
+ }
121
+ function openaiText(json) {
122
+ const choices = json.choices ?? [];
123
+ const c = choices[0]?.message?.content;
124
+ return typeof c === "string" ? c : "";
125
+ }
126
+ function extractJson(text) {
127
+ const trimmed = text.trim();
128
+ const candidates = [];
129
+ const fence = /```(?:json)?\s*([\s\S]*?)```/i.exec(trimmed);
130
+ if (fence?.[1])
131
+ candidates.push(fence[1].trim());
132
+ candidates.push(trimmed);
133
+ const start = trimmed.indexOf("{");
134
+ const end = trimmed.lastIndexOf("}");
135
+ if (start !== -1 && end > start)
136
+ candidates.push(trimmed.slice(start, end + 1));
137
+ for (const c of candidates) {
138
+ try {
139
+ return JSON.parse(c);
140
+ }
141
+ catch {
142
+ // try next candidate
143
+ }
144
+ }
145
+ return undefined;
146
+ }
147
+ //# sourceMappingURL=reshaper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reshaper.js","sourceRoot":"","sources":["../src/reshaper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type IncomingMessage, type ServerResponse } from "node:http";
2
+ import { type Config } from "./config.js";
3
+ import { type Reshaper } from "./reshaper.js";
4
+ import { ModelCatalog } from "./catalog.js";
5
+ export interface ProxyDeps {
6
+ reshaper?: Reshaper;
7
+ catalog?: ModelCatalog;
8
+ }
9
+ export declare function createProxy(cfg: Config, deps?: ProxyDeps): import("http").Server<typeof IncomingMessage, typeof ServerResponse>;