fix(coreweave): harden project header setup
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Clarified CoreWeave Serverless Inference login instructions to persist `COREWEAVE_PROJECT` in the user's shell startup file.
|
||||
|
||||
## [16.2.12] - 2026-07-01
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -6,13 +6,13 @@ import type { OAuthLoginCallbacks } from "./oauth/types";
|
||||
import type { ProviderDefinition } from "./types";
|
||||
|
||||
const PROJECT_SETUP_INSTRUCTIONS =
|
||||
"Create or select a CoreWeave Serverless Inference project, set COREWEAVE_PROJECT=<team>/<project> for the OpenAI-Project header, then copy your API key from account settings";
|
||||
"Create or select a CoreWeave Serverless Inference project, add export COREWEAVE_PROJECT=<team>/<project> to your shell startup file (for example ~/.zshrc, ~/.bashrc, or your shell's profile/rc file) for the OpenAI-Project header, then copy your API key from account settings";
|
||||
|
||||
function requireCoreWeaveProjectHeaders(): Record<string, string> {
|
||||
const headers = coreWeaveProjectHeaders($env);
|
||||
if (!headers) {
|
||||
throw new AIError.ConfigurationError(
|
||||
"CoreWeave Serverless Inference requires OpenAI-Project. Set COREWEAVE_PROJECT=<team>/<project> before running /login coreweave.",
|
||||
"CoreWeave Serverless Inference requires OpenAI-Project. Set COREWEAVE_PROJECT=<team>/<project> before running /login coreweave. To persist it, add export COREWEAVE_PROJECT=<team>/<project> to your shell startup file, such as ~/.zshrc, ~/.bashrc, or your shell's profile/rc file.",
|
||||
);
|
||||
}
|
||||
return headers;
|
||||
|
||||
@@ -55,6 +55,9 @@ describe("CoreWeave Serverless Inference login", () => {
|
||||
|
||||
expect(apiKey).toBe("coreweave-test-key");
|
||||
expect(authMessages[0]).toContain("COREWEAVE_PROJECT=<team>/<project>");
|
||||
expect(authMessages[0]).toContain("~/.zshrc");
|
||||
expect(authMessages[0]).toContain("~/.bashrc");
|
||||
expect(authMessages[0]).toContain("your shell's profile/rc file");
|
||||
expect(authMessages[0]).toContain("OpenAI-Project");
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@@ -71,12 +74,13 @@ describe("CoreWeave Serverless Inference login", () => {
|
||||
});
|
||||
});
|
||||
|
||||
await expect(
|
||||
loginCoreWeave({
|
||||
onPrompt: async () => "coreweave-test-key",
|
||||
fetch: fetchMock,
|
||||
}),
|
||||
).rejects.toThrow("Set COREWEAVE_PROJECT=<team>/<project>");
|
||||
const login = loginCoreWeave({
|
||||
onPrompt: async () => "coreweave-test-key",
|
||||
fetch: fetchMock,
|
||||
});
|
||||
|
||||
await expect(login).rejects.toThrow("Set COREWEAVE_PROJECT=<team>/<project>");
|
||||
await expect(login).rejects.toThrow("your shell's profile/rc file");
|
||||
expect(fetchMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test";
|
||||
import { afterEach, describe, expect, test, vi } from "bun:test";
|
||||
import { completeSimple } from "@oh-my-pi/pi-ai";
|
||||
import { resolveOpenAIRequestSetup } from "@oh-my-pi/pi-ai/providers/openai-shared";
|
||||
import type { Context, FetchImpl } from "@oh-my-pi/pi-ai/types";
|
||||
import { getBundledModel } from "@oh-my-pi/pi-catalog/models";
|
||||
|
||||
const COREWEAVE_ENV_KEYS = ["COREWEAVE_PROJECT", "WANDB_INFERENCE_PROJECT", "WANDB_ENTITY", "WANDB_PROJECT"] as const;
|
||||
const ORIGINAL_ENV = new Map(COREWEAVE_ENV_KEYS.map(key => [key, Bun.env[key]]));
|
||||
@@ -19,6 +22,22 @@ afterEach(() => {
|
||||
restoreCoreWeaveEnv();
|
||||
});
|
||||
|
||||
const context: Context = { messages: [{ role: "user", content: "hi", timestamp: 0 }] };
|
||||
|
||||
function chatSse(): Response {
|
||||
const chunk = (delta: unknown, finish: string | null) =>
|
||||
JSON.stringify({
|
||||
id: "x",
|
||||
object: "chat.completion.chunk",
|
||||
created: 0,
|
||||
choices: [{ index: 0, delta, finish_reason: finish }],
|
||||
});
|
||||
return new Response(`data: ${chunk({ content: "ok" }, null)}\n\ndata: ${chunk({}, "stop")}\n\ndata: [DONE]\n\n`, {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/event-stream" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("CoreWeave Serverless Inference project header", () => {
|
||||
const coreWeaveModel = {
|
||||
provider: "coreweave",
|
||||
@@ -66,4 +85,32 @@ describe("CoreWeave Serverless Inference project header", () => {
|
||||
expect(setup.headers["openai-project"]).toBe("explicit/team");
|
||||
expect(setup.headers["OpenAI-Project"]).toBeUndefined();
|
||||
});
|
||||
|
||||
test("uses COREWEAVE_PROJECT when an explicit blank project header is present", () => {
|
||||
Bun.env.COREWEAVE_PROJECT = "team/project";
|
||||
|
||||
const setup = resolveOpenAIRequestSetup(coreWeaveModel, {
|
||||
apiKey: "coreweave-key",
|
||||
extraHeaders: { "openai-project": " " },
|
||||
messages: [],
|
||||
});
|
||||
|
||||
expect(setup.headers["OpenAI-Project"]).toBe("team/project");
|
||||
});
|
||||
|
||||
test("sends OpenAI-Project on chat-completions requests", async () => {
|
||||
Bun.env.COREWEAVE_PROJECT = "team/project";
|
||||
const requestHeaders: Headers[] = [];
|
||||
const fetchMock: FetchImpl = vi.fn(async (_input: string | URL | Request, init?: RequestInit) => {
|
||||
requestHeaders.push(new Headers(init?.headers));
|
||||
return chatSse();
|
||||
});
|
||||
|
||||
await completeSimple(getBundledModel("coreweave", "zai-org/GLM-5.2"), context, {
|
||||
apiKey: "coreweave-key",
|
||||
fetch: fetchMock,
|
||||
});
|
||||
|
||||
expect(requestHeaders[0]?.get("OpenAI-Project")).toBe("team/project");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed CoreWeave Serverless Inference project-header detection so blank `OpenAI-Project` overrides do not block `COREWEAVE_PROJECT` fallback.
|
||||
|
||||
## [16.2.12] - 2026-07-01
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -38,5 +38,5 @@ export function coreWeaveProjectHeaders(env: CoreWeaveProjectEnv): Record<string
|
||||
|
||||
export function hasCoreWeaveProjectHeader(headers: Record<string, string>): boolean {
|
||||
const normalized = COREWEAVE_PROJECT_HEADER.toLowerCase();
|
||||
return Object.keys(headers).some(header => header.toLowerCase() === normalized);
|
||||
return Object.entries(headers).some(([header, value]) => header.toLowerCase() === normalized && value.trim() !== "");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user