From d624552b6274eae8049fde6ce20954174e99275b Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:28:00 +0000 Subject: [PATCH] fix: Ignore empty `BRAINTRUST_API_URL` and `BRAINTRUST_PROXY_URL` environment variables --- .changeset/empty-url-env-vars.md | 5 +++ js/src/logger.test.ts | 76 ++++++++++++++++++++++++++++++++ js/src/logger.ts | 6 +-- 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 .changeset/empty-url-env-vars.md diff --git a/.changeset/empty-url-env-vars.md b/.changeset/empty-url-env-vars.md new file mode 100644 index 000000000..636c4bd07 --- /dev/null +++ b/.changeset/empty-url-env-vars.md @@ -0,0 +1,5 @@ +--- +"braintrust": patch +--- + +fix: Ignore empty `BRAINTRUST_API_URL` and `BRAINTRUST_PROXY_URL` environment variables and use the organization URLs instead diff --git a/js/src/logger.test.ts b/js/src/logger.test.ts index dd35b6111..94d0b73d0 100644 --- a/js/src/logger.test.ts +++ b/js/src/logger.test.ts @@ -12,6 +12,7 @@ import { FailedHTTPResponse, loadPrompt, loadParameters, + loginToState, wrapTraced, currentSpan, withParent, @@ -3209,6 +3210,81 @@ test("simulateLoginForTests and simulateLogoutForTests", async () => { } }); +describe("login with empty URL environment variables", () => { + const orgInfo = { + id: "org-id", + name: "org-name", + api_url: "https://api.test", + proxy_url: "https://proxy.test", + }; + + afterEach(() => { + vi.unstubAllEnvs(); + _exportsForTestingOnly.simulateLogoutForTests(); + }); + + test("loginToState uses the organization URLs when BRAINTRUST_API_URL and BRAINTRUST_PROXY_URL are empty", async () => { + vi.stubEnv("BRAINTRUST_API_URL", ""); + vi.stubEnv("BRAINTRUST_PROXY_URL", ""); + const fetch = vi.fn(async () => + Response.json({ org_info: [orgInfo] }), + ); + + const state = await loginToState({ + apiKey: "test-credential", + appUrl: "https://app.test", + fetch, + }); + + expect(state.apiUrl).toBe("https://api.test"); + expect(state.proxyUrl).toBe("https://proxy.test"); + }); + + test("loadPrompt with an explicit API key uses the organization API URL when BRAINTRUST_API_URL is empty", async () => { + vi.stubEnv("BRAINTRUST_API_URL", ""); + const fetch = vi + .fn() + .mockResolvedValueOnce(Response.json({ org_info: [orgInfo] })) + .mockResolvedValueOnce( + Response.json({ + objects: [ + { + id: "11111111-1111-4111-8111-111111111111", + _xact_id: "v1", + project_id: "22222222-2222-4222-8222-222222222222", + log_id: "p", + org_id: "33333333-3333-4333-8333-333333333333", + name: "Saved prompt", + slug: "saved-prompt", + prompt_data: { + prompt: { + type: "chat", + messages: [{ role: "user", content: "Hello" }], + }, + options: { model: "gpt-5-mini" }, + }, + }, + ], + }), + ); + + await loadPrompt({ + projectName: "test-project", + slug: "saved-prompt", + apiKey: "test-credential", + appUrl: "https://app.test", + fetch, + noTrace: true, + }); + + expect(fetch).toHaveBeenNthCalledWith( + 2, + expect.stringMatching(/^https:\/\/api\.test\/v1\/prompt\?/), + expect.anything(), + ); + }); +}); + describe("HTTPConnection POST retries", () => { afterEach(() => { vi.restoreAllMocks(); diff --git a/js/src/logger.ts b/js/src/logger.ts index 3a5be45ca..b11f945c8 100644 --- a/js/src/logger.ts +++ b/js/src/logger.ts @@ -5853,7 +5853,7 @@ async function loginToLoaderRequestState({ ); const org = selectLoginOrg(info.org_info, orgName); orgId = org.id; - apiUrl = iso.getEnv("BRAINTRUST_API_URL") ?? org.api_url; + apiUrl = iso.getEnv("BRAINTRUST_API_URL") || org.api_url; if (!apiUrl) { throw new Error( orgName @@ -7221,8 +7221,8 @@ function _saveOrgInfo( const org = selectLoginOrg(orgInfo, orgName); state.orgId = org.id; state.orgName = org.name; - state.apiUrl = iso.getEnv("BRAINTRUST_API_URL") ?? org.api_url; - state.proxyUrl = iso.getEnv("BRAINTRUST_PROXY_URL") ?? org.proxy_url; + state.apiUrl = iso.getEnv("BRAINTRUST_API_URL") || org.api_url; + state.proxyUrl = iso.getEnv("BRAINTRUST_PROXY_URL") || org.proxy_url; state.gitMetadataSettings = org.git_metadata || undefined; }