mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 05:16:50 +01:00
Fix Codex billing attribution env
This commit is contained in:
1 parent
bfe6369ef5
commit
30ce8e1bd6
2 files changed
+112
-2
No files matched your search
@@ -0,0 +1,109 @@
|
||||
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const {
|
||||
runAdapterExecutionTargetProcess,
|
||||
ensureAdapterExecutionTargetCommandResolvable,
|
||||
ensureAdapterExecutionTargetRuntimeCommandInstalled,
|
||||
resolveAdapterExecutionTargetCommandForLogs,
|
||||
} = vi.hoisted(() => ({
|
||||
runAdapterExecutionTargetProcess: vi.fn(async () => ({
|
||||
exitCode: 0,
|
||||
signal: null,
|
||||
timedOut: false,
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
pid: 123,
|
||||
startedAt: new Date().toISOString(),
|
||||
})),
|
||||
ensureAdapterExecutionTargetCommandResolvable: vi.fn(async () => undefined),
|
||||
ensureAdapterExecutionTargetRuntimeCommandInstalled: vi.fn(async () => undefined),
|
||||
resolveAdapterExecutionTargetCommandForLogs: vi.fn(async () => "/usr/bin/codex"),
|
||||
}));
|
||||
|
||||
vi.mock("@paperclipai/adapter-utils/execution-target", async () => {
|
||||
const actual = await vi.importActual<typeof import("@paperclipai/adapter-utils/execution-target")>(
|
||||
"@paperclipai/adapter-utils/execution-target",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
runAdapterExecutionTargetProcess,
|
||||
ensureAdapterExecutionTargetCommandResolvable,
|
||||
ensureAdapterExecutionTargetRuntimeCommandInstalled,
|
||||
resolveAdapterExecutionTargetCommandForLogs,
|
||||
};
|
||||
});
|
||||
|
||||
import { execute } from "./execute.js";
|
||||
|
||||
describe("codex execution billing attribution", () => {
|
||||
const cleanupDirs: string[] = [];
|
||||
const originalOpenRouterApiKey = process.env.OPENROUTER_API_KEY;
|
||||
|
||||
afterEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
if (originalOpenRouterApiKey == null) {
|
||||
delete process.env.OPENROUTER_API_KEY;
|
||||
} else {
|
||||
process.env.OPENROUTER_API_KEY = originalOpenRouterApiKey;
|
||||
}
|
||||
while (cleanupDirs.length > 0) {
|
||||
const dir = cleanupDirs.pop();
|
||||
if (!dir) continue;
|
||||
await rm(dir, { recursive: true, force: true }).catch(() => undefined);
|
||||
}
|
||||
});
|
||||
|
||||
it("attributes direct OpenAI API runs to OpenAI despite an ambient host OpenRouter key", async () => {
|
||||
process.env.OPENROUTER_API_KEY = "sk-or-host-ambient";
|
||||
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-codex-billing-"));
|
||||
cleanupDirs.push(rootDir);
|
||||
const workspaceDir = path.join(rootDir, "workspace");
|
||||
const codexHomeDir = path.join(rootDir, "codex-home");
|
||||
await mkdir(workspaceDir, { recursive: true });
|
||||
await mkdir(codexHomeDir, { recursive: true });
|
||||
await writeFile(path.join(codexHomeDir, "auth.json"), "{}", "utf8");
|
||||
|
||||
const result = await execute({
|
||||
runId: "run-direct-openai",
|
||||
agent: {
|
||||
id: "agent-1",
|
||||
companyId: "company-1",
|
||||
name: "CodexCoder",
|
||||
adapterType: "codex_local",
|
||||
adapterConfig: {},
|
||||
},
|
||||
runtime: {
|
||||
sessionId: null,
|
||||
sessionParams: null,
|
||||
sessionDisplayId: null,
|
||||
taskKey: null,
|
||||
},
|
||||
config: {
|
||||
command: "codex",
|
||||
env: {
|
||||
CODEX_HOME: codexHomeDir,
|
||||
OPENAI_API_KEY: "sk-direct-openai",
|
||||
},
|
||||
},
|
||||
context: {
|
||||
paperclipWorkspace: {
|
||||
cwd: workspaceDir,
|
||||
source: "project_primary",
|
||||
},
|
||||
},
|
||||
onLog: async () => {},
|
||||
});
|
||||
|
||||
expect(result.biller).toBe("openai");
|
||||
expect(result.billingType).toBe("api");
|
||||
expect(runAdapterExecutionTargetProcess).toHaveBeenCalledTimes(1);
|
||||
const call = runAdapterExecutionTargetProcess.mock.calls[0] as unknown as
|
||||
| [string, unknown, string, string[], { env: Record<string, string> }]
|
||||
| undefined;
|
||||
expect(call?.[4].env.OPENAI_API_KEY).toBe("sk-direct-openai");
|
||||
expect(call?.[4].env.OPENROUTER_API_KEY).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -499,12 +499,13 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
Object.assign(env, paperclipBridge.env);
|
||||
}
|
||||
}
|
||||
const billingEnv = { ...env };
|
||||
const effectiveEnv = Object.fromEntries(
|
||||
Object.entries({ ...process.env, ...env }).filter(
|
||||
(entry): entry is [string, string] => typeof entry[1] === "string",
|
||||
),
|
||||
);
|
||||
const billingType = resolveCodexBillingType(effectiveEnv);
|
||||
const billingType = resolveCodexBillingType(billingEnv);
|
||||
const runtimeEnv = Object.fromEntries(
|
||||
Object.entries(ensurePathInEnv(effectiveEnv)).filter(
|
||||
(entry): entry is [string, string] => typeof entry[1] === "string",
|
||||
@@ -809,7 +810,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
|
||||
sessionParams: resolvedSessionParams,
|
||||
sessionDisplayId: resolvedSessionId,
|
||||
provider: "openai",
|
||||
biller: resolveCodexBiller(effectiveEnv, billingType),
|
||||
biller: resolveCodexBiller(billingEnv, billingType),
|
||||
model,
|
||||
billingType,
|
||||
costUsd: null,
|
||||
|
||||
Reference in new issue
Block a user