Skip to content

g1t/services/models/src/gateway.test.ts

166 lines7,320 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens1import assert from "node:assert/strict";
2import { test } from "node:test";
3
AI Gateway: OpenAI's format, open models, and your own providers4import type { User } from "@g1t/contracts";
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens5
6import {
7 anthropicError,
8 callerOf,
9 errorMessage,
10 gatewayRecord,
11 gatewayRoute,
12 hostedRequest,
13 requestId,
14 sessionOf,
15 unpriced,
16} from "./gateway.ts";
17
18const workspaceToken = (scopes: string[] | null, name = "ci"): User => ({
19 id: "wsp_1",
20 username: "Acme",
21 kind: "workspace",
22 workspaces: [{ slug: "acme", role: "member" }],
23 token: { token_id: "tok_1", scopes, name },
24});
25
26test("a workspace's token with models:write is let through, as that workspace", () => {
27 const who = callerOf(workspaceToken(["repo:read", "models:write"]));
28 assert.ok("caller" in who);
29 assert.deepEqual(who.caller, { workspace: "acme", tokenId: "tok_1", tokenName: "ci" });
30 // Full access holds every scope.
31 assert.ok("caller" in callerOf(workspaceToken(null)));
32});
33
34test("without models:write, or not a workspace's token, it is refused as Anthropic would", () => {
35 const reader = callerOf(workspaceToken(["models:read", "billing:write"]));
36 assert.ok(!("caller" in reader));
37 assert.equal(reader.status, 403);
38 assert.equal(reader.type, "permission_error");
39 assert.match(reader.message, /models:write/);
40
41 const person: User = { id: "usr_1", username: "ada", token: { token_id: "tok_2", scopes: null } };
42 const personal = callerOf(person);
43 assert.ok(!("caller" in personal));
44 assert.equal(personal.status, 403);
45 assert.match(personal.message, /workspace's access token/);
46
47 const unknown = callerOf(null);
48 assert.ok(!("caller" in unknown));
49 assert.equal(unknown.status, 401);
50 assert.equal(unknown.type, "authentication_error");
51
52 // A workspace acting through a signed-in session, not a token, is not a caller either.
53 const session: User = { id: "wsp_1", username: "acme", kind: "workspace" };
54 assert.ok(!("caller" in callerOf(session)));
55});
56
57test("errors are in Anthropic's shape", async () => {
58 const response = anthropicError(402, "billing_error", "Out of AI credit.");
59 assert.equal(response.status, 402);
60 assert.deepEqual(await response.json(), { type: "error", error: { type: "billing_error", message: "Out of AI credit." } });
61});
62
63test("the gateway answers messages and counting tokens only", () => {
64 assert.equal(gatewayRoute("/v1/messages"), "messages");
65 assert.equal(gatewayRoute("/v1/messages?beta=true"), "messages");
66 assert.equal(gatewayRoute("/v1/messages/count_tokens"), "count_tokens");
67 assert.equal(gatewayRoute("/v1/messages/batches"), null);
68 assert.equal(gatewayRoute("/v1/models"), null);
69});
70
71test("requests to g1t's models go through its gateway, tagged, without the caller's token", () => {
72 const hosted = { AI_GATEWAY_ID: "g1t", CLOUDFLARE_ACCOUNT_ID: "acct", AI_GATEWAY_TOKEN: "gw-token" };
73 const incoming = new Headers({
74 "x-api-key": "g1t_secret",
75 authorization: "Bearer g1t_secret",
76 "anthropic-version": "2023-06-01",
77 "cf-aig-metadata": "{\"workspace\":\"someone-else\"}",
78 });
79 const caller = { workspace: "acme", tokenId: "tok_1", tokenName: "ci" };
80 const { url, headers } = hostedRequest(hosted, "/v1/messages", incoming, caller, "gw_tok_1_2026100712");
81 assert.equal(url, "https://gateway.ai.cloudflare.com/v1/acct/g1t/anthropic/v1/messages");
82 assert.equal(headers.get("x-api-key"), null);
83 assert.equal(headers.get("authorization"), null);
84 assert.equal(headers.get("cf-aig-authorization"), "Bearer gw-token");
85 assert.equal(headers.get("anthropic-version"), "2023-06-01");
86 assert.deepEqual(JSON.parse(headers.get("cf-aig-metadata") ?? "{}"), {
87 task: "gateway",
88 workspace: "acme",
89 token: "tok_1",
90 session: "gw_tok_1_2026100712",
91 });
92 // With no gateway, straight to Anthropic with g1t's key.
93 const direct = hostedRequest({ AI_GATEWAY_ID: "", CLOUDFLARE_ACCOUNT_ID: "", ANTHROPIC_API_KEY: "sk-g1t" }, "/v1/messages", incoming, caller, "s");
94 assert.equal(direct.url, "https://api.anthropic.com/v1/messages");
95 assert.equal(direct.headers.get("x-api-key"), "sk-g1t");
96});
97
98test("on g1t's models, only what is charged by its tokens is let through", () => {
99 const plain = { model: "claude-sonnet-5-5", max_tokens: 10, messages: [] };
100 assert.equal(unpriced(plain), null);
101 assert.equal(unpriced({ ...plain, speed: "standard", inference_geo: "global" }), null);
102 // The caller's own tools, and the client tools Anthropic defines, cost only their tokens.
103 const clientTools = [{ name: "lookup", input_schema: {} }, { type: "custom", name: "x" }, { type: "bash_20250124", name: "bash" }, { type: "text_editor_20250728", name: "e" }, { type: "computer_toolset_20260801", name: "c" }, { type: "memory_20250818", name: "memory" }];
104 assert.equal(unpriced({ ...plain, tools: clientTools }), null);
105 // Billed otherwise by the provider: refused, pointing at the workspace's own key.
106 assert.match(unpriced({ ...plain, speed: "fast" }) ?? "", /Fast mode/);
107 assert.match(unpriced({ ...plain, inference_geo: "us" }) ?? "", /inference_geo/);
108 assert.match(unpriced({ ...plain, fallbacks: "default" }) ?? "", /fallbacks/);
109 assert.match(unpriced({ ...plain, container: { skills: [] } }) ?? "", /Containers/);
110 const search = unpriced({ ...plain, tools: [{ type: "web_search_20260209", name: "web_search" }] }) ?? "";
111 assert.match(search, /web_search_20260209/);
112 assert.match(search, /own Anthropic key/);
113});
114
115test("a caller cannot set the gateway's own headers", () => {
116 const caller = { workspace: "acme", tokenId: "tok_1", tokenName: null };
117 const incoming = new Headers({ "cf-aig-custom-cost": "{\"total_cost\":0}", "cf-aig-cache-ttl": "3600", "cf-aig-skip-cache": "true" });
118 const { headers } = hostedRequest({ AI_GATEWAY_ID: "g1t", CLOUDFLARE_ACCOUNT_ID: "acct" }, "/v1/messages", incoming, caller, "s");
119 assert.equal(headers.get("cf-aig-custom-cost"), null);
120 assert.equal(headers.get("cf-aig-cache-ttl"), null);
121 assert.equal(headers.get("cf-aig-skip-cache"), null);
122});
123
124test("a request has its own id and is logged under its token's hour", () => {
125 assert.match(requestId(), /^gw_[0-9a-f]{24}$/);
126 assert.notEqual(requestId(), requestId());
127 assert.equal(sessionOf("tok_1", new Date("2026-10-07T12:34:56Z")), "gw_tok_1_2026100712");
128});
129
130test("what billing is told: tokens by kind, whose key, and how it went", () => {
131 const record = gatewayRecord({
132 id: "gw_1",
133 caller: { workspace: "acme", tokenId: "tok_1", tokenName: "ci" },
134 model: "claude-sonnet-5-5",
135 tokens: { input: 10, output: 5, cacheRead: 100, cacheWrite: 0 },
136 status: 200,
137 ownKey: true,
138 streamed: true,
139 durationMs: 812.4,
140 });
141 assert.deepEqual(record, {
142 id: "gw_1",
143 workspace: "acme",
144 tokenId: "tok_1",
145 tokenName: "ci",
146 model: "claude-sonnet-5-5",
147 input: 10,
148 output: 5,
149 cacheRead: 100,
150 cacheWrite: 0,
AI Gateway: OpenAI's format, open models, and your own providers151 cacheWriteHour: 0,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens152 status: 200,
153 ownKey: true,
AI Gateway: OpenAI's format, open models, and your own providers154 format: "anthropic",
155 provider: "",
156 connection: null,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens157 streamed: true,
158 durationMs: 812,
159 error: null,
160 });
161});
162
163test("a provider's error is logged by its message", () => {
164 assert.equal(errorMessage(429, JSON.stringify({ type: "error", error: { type: "rate_limit_error", message: "Slow down." } })), "Slow down.");
165 assert.equal(errorMessage(502, "<html>bad gateway</html>"), "The model provider answered 502.");
166});

This file's history is long; its oldest lines are credited to the oldest commit read.