Merge PR #4154: fix(coreweave): harden project header setup (@lance0)

This commit is contained in:
can1357
2026-07-01 21:53:19 +02:00
7 changed files with 82 additions and 11 deletions
+3
View File
@@ -16,6 +16,9 @@
### Fixed
- Reworded the GPT-5 Responses no-reasoning fallback developer item so it no longer says `# Juice: 0 !important` or implies a zero tool/execution budget. ([#4151](https://github.com/can1357/oh-my-pi/issues/4151))
### Changed
- Clarified CoreWeave Serverless Inference login instructions to persist `COREWEAVE_PROJECT` in the user's shell startup file.
## [16.2.12] - 2026-07-01
@@ -17,6 +17,7 @@ import {
COREWEAVE_PROJECT_HEADER,
coreWeaveProjectHeaders,
hasCoreWeaveProjectHeader,
removeBlankCoreWeaveProjectHeaders,
} from "@oh-my-pi/pi-catalog/wire/coreweave";
import { parseGitHubCopilotApiKey } from "@oh-my-pi/pi-catalog/wire/github-copilot";
import {
@@ -161,6 +162,7 @@ function resolveSakanaRequestBaseUrl(): string | undefined {
}
function applyCoreWeaveProjectHeader(headers: Record<string, string>): void {
removeBlankCoreWeaveProjectHeaders(headers);
if (hasCoreWeaveProjectHeader(headers)) {
return;
}
+4 -3
View File
@@ -5,14 +5,15 @@ import { createApiKeyLogin } from "./api-key-login";
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";
const PROJECT_PERSIST_INSTRUCTIONS =
"add export COREWEAVE_PROJECT=<team>/<project> to your shell startup file (for example ~/.zshrc, ~/.bashrc, or your shell's profile/rc file)";
const PROJECT_SETUP_INSTRUCTIONS = `Create or select a CoreWeave Serverless Inference project, ${PROJECT_PERSIST_INSTRUCTIONS} 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, ${PROJECT_PERSIST_INSTRUCTIONS}.`,
);
}
return headers;
+10 -6
View File
@@ -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,34 @@ 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");
expect(setup.headers["openai-project"]).toBeUndefined();
});
test("sends one OpenAI-Project header on chat-completions requests when a blank override is present", 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,
headers: { "openai-project": " " },
});
expect(requestHeaders[0]?.get("OpenAI-Project")).toBe("team/project");
});
});
+3
View File
@@ -17,6 +17,9 @@
### Fixed
- Updated the Responses compat flag docs to describe the no-reasoning fallback without the old `# Juice: 0 !important` wording. ([#4151](https://github.com/can1357/oh-my-pi/issues/4151))
### 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
+10 -1
View File
@@ -38,5 +38,14 @@ 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() !== "");
}
export function removeBlankCoreWeaveProjectHeaders(headers: Record<string, string>): void {
const normalized = COREWEAVE_PROJECT_HEADER.toLowerCase();
for (const [header, value] of Object.entries(headers)) {
if (header.toLowerCase() === normalized && value.trim() === "") {
delete headers[header];
}
}
}