-
Notifications
You must be signed in to change notification settings - Fork 212
feat(webview): add OpenAI Codex speed selector #1076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
69e5851
1977341
3558eed
0c7b0f9
4ca5f3e
bcb5e4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import React from "react" | ||
|
|
||
| import { | ||
| OpenAiCodexServiceTier, | ||
| type OpenAiCodexServiceTier as OpenAiCodexServiceTierValue, | ||
| } from "@roo-code/types/model" | ||
|
|
||
| import { useAppTranslation } from "@src/i18n/TranslationContext" | ||
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@src/components/ui" | ||
|
|
||
| interface OpenAICodexSpeedSelectorProps { | ||
| value?: OpenAiCodexServiceTierValue | ||
| onValueChange: (value: OpenAiCodexServiceTierValue) => void | ||
| } | ||
|
|
||
| export const OpenAICodexSpeedSelector: React.FC<OpenAICodexSpeedSelectorProps> = ({ value, onValueChange }) => { | ||
| const { t } = useAppTranslation() | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-1" data-testid="openai-codex-service-tier"> | ||
| <div className="flex items-center gap-1"> | ||
| <label className="block font-medium">{t("settings:openAiCodexSpeed.label")}</label> | ||
| <StandardTooltip content={t("settings:openAiCodexSpeed.tooltip")}> | ||
| <i className="codicon codicon-info text-vscode-descriptionForeground text-xs" /> | ||
| </StandardTooltip> | ||
| </div> | ||
| <Select value={value ?? OpenAiCodexServiceTier.Default} onValueChange={onValueChange}> | ||
| <SelectTrigger className="w-full"> | ||
| <SelectValue placeholder={t("settings:common.select")} /> | ||
| </SelectTrigger> | ||
| <SelectContent> | ||
| <SelectItem value={OpenAiCodexServiceTier.Default}> | ||
| {t("settings:openAiCodexSpeed.standard")} | ||
| </SelectItem> | ||
| <SelectItem value={OpenAiCodexServiceTier.Priority}> | ||
| {t("settings:openAiCodexSpeed.fast")} | ||
| </SelectItem> | ||
| </SelectContent> | ||
| </Select> | ||
| </div> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import React from "react" | ||
|
|
||
| import { | ||
| OPEN_AI_CODEX_SERVICE_TIER_KEY, | ||
| OpenAiCodexServiceTier, | ||
| providerIdentifiers, | ||
| type ProviderSettings, | ||
| } from "@roo-code/types" | ||
|
|
||
| import { fireEvent, render, screen } from "@/utils/test-utils" | ||
| import { vscode } from "@src/utils/vscode" | ||
|
|
||
| import { OpenAICodex } from "../OpenAICodex" | ||
|
|
||
| vi.mock("@src/i18n/TranslationContext", () => ({ | ||
| useAppTranslation: () => ({ | ||
| t: (key: string) => | ||
| ({ | ||
| "settings:openAiCodexSpeed.label": "Speed", | ||
| "settings:openAiCodexSpeed.tooltip": | ||
| "Fast uses Codex priority processing for about 1.5x speed and consumes more subscription quota.", | ||
| "settings:openAiCodexSpeed.standard": "Standard", | ||
| "settings:openAiCodexSpeed.fast": "Fast (1.5x speed, increased usage)", | ||
| })[key] ?? key, | ||
| }), | ||
| })) | ||
|
|
||
| vi.mock("@src/components/ui", () => ({ | ||
| Button: ({ children, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) => ( | ||
| <button {...props}>{children}</button> | ||
| ), | ||
| Select: ({ children, value, onValueChange }: any) => ( | ||
| <select aria-label="Speed" value={value} onChange={(event) => onValueChange(event.target.value)}> | ||
| {children} | ||
| </select> | ||
| ), | ||
| SelectContent: ({ children }: any) => <>{children}</>, | ||
| SelectItem: ({ children, value }: any) => <option value={value}>{children}</option>, | ||
| SelectTrigger: ({ children }: any) => <>{children}</>, | ||
| SelectValue: () => null, | ||
| StandardTooltip: ({ children, content }: any) => <span title={content}>{children}</span>, | ||
| })) | ||
|
|
||
| vi.mock("../../ModelPicker", () => ({ | ||
| ModelPicker: () => <div data-testid="model-picker" />, | ||
| })) | ||
|
|
||
| vi.mock("../OpenAICodexRateLimitDashboard", () => ({ | ||
| OpenAICodexRateLimitDashboard: () => null, | ||
| })) | ||
|
|
||
| vi.mock("@src/utils/vscode", () => ({ | ||
| vscode: { postMessage: vi.fn() }, | ||
| })) | ||
|
|
||
| describe("OpenAICodex speed selector", () => { | ||
| const renderSelector = (apiConfiguration: ProviderSettings, setApiConfigurationField = vi.fn()) => { | ||
| render(<OpenAICodex apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />) | ||
| return { setApiConfigurationField, selector: screen.getByRole("combobox", { name: "Speed" }) } | ||
| } | ||
|
|
||
| it("defaults to Standard and clearly explains the Fast quota trade-off", () => { | ||
| const { selector } = renderSelector({ apiProvider: providerIdentifiers.openaiCodex }) | ||
|
|
||
| expect(selector).toHaveValue(OpenAiCodexServiceTier.Default) | ||
| expect(screen.getByRole("option", { name: "Standard" })).toBeInTheDocument() | ||
| expect(screen.getByRole("option", { name: "Fast (1.5x speed, increased usage)" })).toBeInTheDocument() | ||
| expect( | ||
| screen.getByTitle( | ||
| "Fast uses Codex priority processing for about 1.5x speed and consumes more subscription quota.", | ||
| ), | ||
| ).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it("selects Fast from a saved preference and persists changes through the settings callback", () => { | ||
| const { selector, setApiConfigurationField } = renderSelector({ | ||
| apiProvider: providerIdentifiers.openaiCodex, | ||
| [OPEN_AI_CODEX_SERVICE_TIER_KEY]: OpenAiCodexServiceTier.Priority, | ||
| }) | ||
| const postMessage = vi.mocked(vscode.postMessage) | ||
|
|
||
| expect(selector).toHaveValue(OpenAiCodexServiceTier.Priority) | ||
|
|
||
| fireEvent.change(selector, { target: { value: OpenAiCodexServiceTier.Default } }) | ||
| expect(setApiConfigurationField).toHaveBeenLastCalledWith( | ||
| OPEN_AI_CODEX_SERVICE_TIER_KEY, | ||
| OpenAiCodexServiceTier.Default, | ||
| ) | ||
|
|
||
| fireEvent.change(selector, { target: { value: OpenAiCodexServiceTier.Priority } }) | ||
| expect(setApiConfigurationField).toHaveBeenLastCalledWith( | ||
| OPEN_AI_CODEX_SERVICE_TIER_KEY, | ||
| OpenAiCodexServiceTier.Priority, | ||
| ) | ||
| expect(postMessage).not.toHaveBeenCalled() | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /* v8 ignore file -- Playwright component fixture is covered by the visual test. */ | ||
| import React from "react" | ||
|
|
||
| import { TranslationContext } from "@src/i18n/TranslationContext" | ||
| import { TooltipProvider } from "@src/components/ui/tooltip" | ||
| import { OpenAICodexSpeedSelector } from "../OpenAICodexSpeedSelector" | ||
|
|
||
| const translations: Record<string, string> = { | ||
| "settings:common.select": "Select", | ||
| "settings:openAiCodexSpeed.label": "Speed", | ||
| "settings:openAiCodexSpeed.tooltip": | ||
| "Fast uses Codex priority processing for about 1.5x speed and consumes more subscription quota.", | ||
| "settings:openAiCodexSpeed.standard": "Standard", | ||
| "settings:openAiCodexSpeed.fast": "Fast (1.5x speed, increased usage)", | ||
| } | ||
|
|
||
| export const OpenAICodexFixture = () => ( | ||
| <TranslationContext.Provider | ||
| value={{ | ||
| t: (key) => translations[key] ?? key, | ||
| i18n: null as unknown as typeof import("../../../../i18n/setup").default, | ||
| }}> | ||
| <TooltipProvider> | ||
| <div className="w-[480px] bg-vscode-editor-background p-4 text-vscode-foreground"> | ||
| <OpenAICodexSpeedSelector onValueChange={() => {}} /> | ||
| </div> | ||
| </TooltipProvider> | ||
| </TranslationContext.Provider> | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import React from "react" | ||
|
|
||
| import { expect, test } from "../../../../../playwright/coverage-fixture" | ||
| import { OpenAICodexFixture } from "./OpenAICodex.visual.fixture" | ||
|
|
||
| test("renders the OpenAI Codex speed selector in the VS Code dark theme", async ({ mount }) => { | ||
| const component = await mount(<OpenAICodexFixture />) | ||
| const selector = component.getByTestId("openai-codex-service-tier") | ||
|
|
||
| await selector.evaluate(async () => { | ||
| await document.fonts.ready | ||
| await new Promise<void>((resolve) => requestAnimationFrame(() => resolve())) | ||
| }) | ||
|
|
||
| await expect(selector).toHaveScreenshot("openai-codex-speed-selector-dark.png") | ||
| }) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.