From 749b156b6f8244f18eec45c745b5e3d3bb0477f4 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Fri, 25 Sep 2026 21:17:47 -0700 Subject: [PATCH] test: add an opt-in live xAI proxy test --- bunfig.toml | 2 -- e2e/live.test.ts | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 ++-- tsconfig.json | 2 +- 4 files changed, 67 insertions(+), 5 deletions(-) delete mode 100644 bunfig.toml create mode 100644 e2e/live.test.ts diff --git a/bunfig.toml b/bunfig.toml deleted file mode 100644 index cd45629..0000000 --- a/bunfig.toml +++ /dev/null @@ -1,2 +0,0 @@ -[test] -root = "src" diff --git a/e2e/live.test.ts b/e2e/live.test.ts new file mode 100644 index 0000000..77a5f95 --- /dev/null +++ b/e2e/live.test.ts @@ -0,0 +1,63 @@ +// Live calls against xAI's CLI chat proxy. Opt in by setting +// XAI_LIVE_ACCESS_TOKEN (a grok CLI OAuth access token). XAI_LIVE_MODEL is +// optional and must be one of XAI_DEFAULT_MODELS. + +import { describe, expect, test } from "bun:test"; +import { createDependencies, runInference } from "@intx/inference"; +import type { InferenceEvent, InferenceSource } from "@intx/types/runtime"; +import { + createXaiResponsesAdapter, + XAI_DEFAULT_MODELS, + XAI_OAUTH_PROXY_BASE_URL, + XAI_PROVIDER, + XAI_USER_ID_OPTION, + xaiUserIdFromAccessToken, +} from "../src/index"; + +const accessToken = process.env["XAI_LIVE_ACCESS_TOKEN"] ?? ""; +const model = process.env["XAI_LIVE_MODEL"] ?? XAI_DEFAULT_MODELS[0]; + +const source: InferenceSource = { + id: `xai:${model}`, + provider: XAI_PROVIDER, + baseURL: XAI_OAUTH_PROXY_BASE_URL, + credentialId: "xai", + model, +}; + +const deps = createDependencies({ + has: (provider) => provider === XAI_PROVIDER, + resolve: createXaiResponsesAdapter, +}); + +describe.skipIf(accessToken === "")("live xAI CLI chat proxy", () => { + test("streams a text turn", async () => { + const userId = xaiUserIdFromAccessToken(accessToken); + if (userId === undefined) + throw new Error("XAI_LIVE_ACCESS_TOKEN is not a JWT with a sub claim"); + let seq = 0; + const events: InferenceEvent[] = []; + for await (const ev of runInference({ + turns: [ + { + role: "user", + timestamp: 0, + content: [{ type: "text", text: "Reply with the word pong." }], + }, + ], + source, + inferenceOptions: { + providerOptions: { [XAI_USER_ID_OPTION]: userId }, + }, + nextSeq: () => seq++, + readMaterial: () => ({ secret: accessToken }), + deps, + })) + events.push(ev); + const done = events.find((e) => e.type === "inference.done"); + if (done?.type !== "inference.done") + throw new Error(`expected inference.done, got ${JSON.stringify(events)}`); + const text = done.data.turn.content.find((b) => b.type === "text"); + expect(text?.type === "text" && text.text.toLowerCase()).toContain("pong"); + }, 60_000); +}); diff --git a/package.json b/package.json index 2c60a88..5e94e3e 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,9 @@ "lint": "eslint .", "format": "prettier --write .", "format:check": "prettier --check .", - "test": "bun test", - "check": "bun run typecheck && bun run lint && bun run format:check && bun run test" + "test": "bun test src", + "check": "bun run typecheck && bun run lint && bun run format:check && bun run test", + "test:e2e": "bun test e2e" }, "dependencies": { "arktype": "^2.2.3" diff --git a/tsconfig.json b/tsconfig.json index d585bb9..b883ac9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,5 @@ "lib": ["ESNext"], "types": ["bun"] }, - "include": ["src"] + "include": ["src", "e2e"] }