diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index 4436227623..3f322e7125 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -144,10 +144,13 @@ export async function installRootRedirect(server: HttpServer, traceUrls: string[ response.setHeader('Location', urlPath); if (process.env.OPENAI_API_KEY) - response.setHeader('Set-Cookie', `openai_api_key=${process.env.OPENAI_API_KEY}`); - + response.appendHeader('Set-Cookie', `openai_api_key=${process.env.OPENAI_API_KEY}`); + if (process.env.OPENAI_BASE_URL) + response.appendHeader('Set-Cookie', `openai_base_url=${process.env.OPENAI_BASE_URL}`); if (process.env.ANTHROPIC_API_KEY) - response.setHeader('Set-Cookie', `anthropic_api_key=${process.env.ANTHROPIC_API_KEY}`); + response.appendHeader('Set-Cookie', `anthropic_api_key=${process.env.ANTHROPIC_API_KEY}`); + if (process.env.ANTHROPIC_BASE_URL) + response.appendHeader('Set-Cookie', `anthropic_base_url=${process.env.ANTHROPIC_BASE_URL}`); response.end(); return true; diff --git a/packages/trace-viewer/src/ui/llm.tsx b/packages/trace-viewer/src/ui/llm.tsx index 29d607e2d2..1532ed4bc8 100644 --- a/packages/trace-viewer/src/ui/llm.tsx +++ b/packages/trace-viewer/src/ui/llm.tsx @@ -221,15 +221,15 @@ export class Conversation { const llmContext = React.createContext(undefined); export function LLMProvider({ children }: React.PropsWithChildren<{}>) { - const cookies = useCookies(); + const cookiePairs = useCookies(); const chat = React.useMemo(() => { - for (const [name, value] of cookies) { - if (name === 'openai_api_key') - return new LLMChat(new OpenAI(value)); - if (name === 'anthropic_api_key') - return new LLMChat(new Anthropic(value)) - } - }, [cookies]); + const cookies = Object.fromEntries(cookiePairs); + console.log({ cookies }) + if (cookies.openai_api_key) + return new LLMChat(new OpenAI(cookies.openai_api_key, cookies.openai_base_url)); + if (cookies.anthropic_api_key) + return new LLMChat(new Anthropic(cookies.anthropic_api_key, cookies.anthropic_base_url)); + }, [cookiePairs]); return {children}; }; diff --git a/packages/web/src/uiUtils.ts b/packages/web/src/uiUtils.ts index 57f32720cc..b5f7aeffa1 100644 --- a/packages/web/src/uiUtils.ts +++ b/packages/web/src/uiUtils.ts @@ -250,7 +250,7 @@ export function useFlash(): [boolean, EffectCallback] { } export function useCookies() { - return document.cookie.split(";").filter(v => v.includes("=")).map(kv => { + return document.cookie.split("; ").filter(v => v.includes("=")).map(kv => { const separator = kv.indexOf("="); return [kv.substring(0, separator), kv.substring(separator + 1)]; }) diff --git a/tests/playwright-test/ui-mode-llm.spec.ts b/tests/playwright-test/ui-mode-llm.spec.ts index dc264da40b..69e318dd69 100644 --- a/tests/playwright-test/ui-mode-llm.spec.ts +++ b/tests/playwright-test/ui-mode-llm.spec.ts @@ -21,7 +21,23 @@ test.describe.configure({ mode: 'parallel', retries }); test.beforeAll(() => process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS = '1'); test.afterAll(() => delete process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS); -test('openai', async ({ runUITest }) => { +test('openai', async ({ runUITest, server }) => { + server.setRoute('/v1/chat/completions', async (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Headers', '*'); + if (req.method === 'OPTIONS') + return res.end(); + + expect(req.headers.authorization).toBe('Bearer fake-key'); + expect((await req.postBody).toString()).toContain(`- button \\"Submit\\"`); + const event = { + object: 'chat.completion.chunk', + choices: [{ delta: { content: 'This is a mock response' } }] + }; + res.setHeader('Content-Type', 'text/event-stream'); + res.write(`data: ${JSON.stringify(event)}\n\n`); + }); + const { page } = await runUITest({ 'a.test.ts': ` import { test, expect } from '@playwright/test'; @@ -31,19 +47,8 @@ test('openai', async ({ runUITest }) => { }); `, }, { - OPENAI_API_KEY: 'fake-key' - }); - - await page.context().route('https://api.openai.com/**', async (route, request) => { - expect(await request.headerValue('authorization')).toBe('Bearer fake-key'); - expect(request.postData()).toContain(`- button \\"Submit\\"`); - const event = { - object: 'chat.completion.chunk', - choices: [{ delta: { content: 'This is a mock response' } }] - }; - await route.fulfill({ - body: `\n\ndata: ${JSON.stringify(event)}\n\n` - }); + OPENAI_API_KEY: 'fake-key', + OPENAI_BASE_URL: server.PREFIX, }); await page.getByTitle('Run all').click();