From c64bf5fbdd5fa321658ff659a800d70c17e800b2 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 00:00:27 +0300 Subject: [PATCH 01/14] refactor(api): centralize service-tier primitives --- packages/types/src/model.ts | 15 ++- src/api/providers/__tests__/bedrock.spec.ts | 17 ++-- .../__tests__/openai-native-usage.spec.ts | 4 +- .../providers/__tests__/openai-native.spec.ts | 70 ++++++++++++- src/api/providers/bedrock.ts | 5 +- src/api/providers/openai-native.ts | 26 ++--- src/shared/cost.ts | 5 +- src/utils/__tests__/cost.spec.ts | 6 +- .../src/components/settings/ModelInfoView.tsx | 36 +++---- .../settings/__tests__/ModelInfoView.spec.tsx | 97 +++++++++++++++++++ .../components/settings/providers/OpenAI.tsx | 16 +-- .../providers/__tests__/OpenAI.spec.tsx | 92 ++++++++++++++++++ 12 files changed, 330 insertions(+), 59 deletions(-) create mode 100644 webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx create mode 100644 webview-ui/src/components/settings/providers/__tests__/OpenAI.spec.tsx diff --git a/packages/types/src/model.ts b/packages/types/src/model.ts index 257a30f3e7..2936d76b69 100644 --- a/packages/types/src/model.ts +++ b/packages/types/src/model.ts @@ -54,12 +54,21 @@ export const verbosityLevelsSchema = z.enum(verbosityLevels) export type VerbosityLevel = z.infer +/** Serialized service tier field used in provider request payloads and responses. */ +export const SERVICE_TIER_KEY = "service_tier" + /** - * Service tiers (OpenAI Responses API) + * Service tiers for the public OpenAI Responses API. */ -export const serviceTiers = ["default", "flex", "priority"] as const +export enum OpenAiServiceTier { + Default = "default", + Flex = "flex", + Priority = "priority", +} + +export const serviceTiers = Object.values(OpenAiServiceTier) as [`${OpenAiServiceTier}`, ...`${OpenAiServiceTier}`[]] export const serviceTierSchema = z.enum(serviceTiers) -export type ServiceTier = z.infer +export type ServiceTier = `${OpenAiServiceTier}` /** * ModelParameter diff --git a/src/api/providers/__tests__/bedrock.spec.ts b/src/api/providers/__tests__/bedrock.spec.ts index 71c2d1cd0c..b025f33f02 100644 --- a/src/api/providers/__tests__/bedrock.spec.ts +++ b/src/api/providers/__tests__/bedrock.spec.ts @@ -58,6 +58,7 @@ import { BEDROCK_1M_CONTEXT_MODEL_IDS, BEDROCK_SERVICE_TIER_MODEL_IDS, bedrockModels, + SERVICE_TIER_KEY, ApiProviderError, } from "@roo-code/types" @@ -1233,10 +1234,10 @@ describe("AwsBedrockHandler", () => { const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any // service_tier should be at the top level of the payload - expect(commandArg.service_tier).toBe("PRIORITY") + expect(commandArg[SERVICE_TIER_KEY]).toBe("PRIORITY") // service_tier should NOT be in additionalModelRequestFields if (commandArg.additionalModelRequestFields) { - expect(commandArg.additionalModelRequestFields.service_tier).toBeUndefined() + expect(commandArg.additionalModelRequestFields[SERVICE_TIER_KEY]).toBeUndefined() } }) @@ -1263,10 +1264,10 @@ describe("AwsBedrockHandler", () => { const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any // service_tier should be at the top level of the payload - expect(commandArg.service_tier).toBe("FLEX") + expect(commandArg[SERVICE_TIER_KEY]).toBe("FLEX") // service_tier should NOT be in additionalModelRequestFields if (commandArg.additionalModelRequestFields) { - expect(commandArg.additionalModelRequestFields.service_tier).toBeUndefined() + expect(commandArg.additionalModelRequestFields[SERVICE_TIER_KEY]).toBeUndefined() } }) @@ -1294,9 +1295,9 @@ describe("AwsBedrockHandler", () => { const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any // Service tier should NOT be included for unsupported models (at top level or in additionalModelRequestFields) - expect(commandArg.service_tier).toBeUndefined() + expect(commandArg[SERVICE_TIER_KEY]).toBeUndefined() if (commandArg.additionalModelRequestFields) { - expect(commandArg.additionalModelRequestFields.service_tier).toBeUndefined() + expect(commandArg.additionalModelRequestFields[SERVICE_TIER_KEY]).toBeUndefined() } }) @@ -1323,9 +1324,9 @@ describe("AwsBedrockHandler", () => { const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any // Service tier should NOT be included when not specified (at top level or in additionalModelRequestFields) - expect(commandArg.service_tier).toBeUndefined() + expect(commandArg[SERVICE_TIER_KEY]).toBeUndefined() if (commandArg.additionalModelRequestFields) { - expect(commandArg.additionalModelRequestFields.service_tier).toBeUndefined() + expect(commandArg.additionalModelRequestFields[SERVICE_TIER_KEY]).toBeUndefined() } }) }) diff --git a/src/api/providers/__tests__/openai-native-usage.spec.ts b/src/api/providers/__tests__/openai-native-usage.spec.ts index a266642e7a..184f04ec80 100644 --- a/src/api/providers/__tests__/openai-native-usage.spec.ts +++ b/src/api/providers/__tests__/openai-native-usage.spec.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from "vitest" import { OpenAiNativeHandler } from "../openai-native" -import { openAiNativeModels } from "@roo-code/types" +import { OpenAiServiceTier, openAiNativeModels } from "@roo-code/types" describe("OpenAiNativeHandler - normalizeUsage", () => { let handler: OpenAiNativeHandler @@ -468,7 +468,7 @@ describe("OpenAiNativeHandler - normalizeUsage", () => { it("should not apply GPT-5.4 long-context pricing to priority tier", () => { handler = new OpenAiNativeHandler({ openAiNativeApiKey: "test-key", - openAiNativeServiceTier: "priority", + openAiNativeServiceTier: OpenAiServiceTier.Priority, }) const usage = { diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index 1acb4101be..63c2e4e396 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -13,7 +13,7 @@ vitest.mock("@roo-code/telemetry", () => ({ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -import { ApiProviderError } from "@roo-code/types" +import { ApiProviderError, OpenAiServiceTier, SERVICE_TIER_KEY } from "@roo-code/types" import { OpenAiNativeHandler } from "../openai-native" import { ApiHandlerOptions } from "../../../shared/api" @@ -122,6 +122,29 @@ describe("OpenAiNativeHandler", () => { }) describe("createMessage", () => { + it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Priority])( + "should include the selected %s service tier", + async (serviceTier) => { + mockResponsesCreate.mockResolvedValue({ + async *[Symbol.asyncIterator]() {}, + }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: serviceTier, + }) + + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + void chunk + } + + expect(mockResponsesCreate).toHaveBeenCalledWith( + expect.objectContaining({ [SERVICE_TIER_KEY]: serviceTier }), + expect.any(Object), + ) + }, + ) + it("should handle streaming responses via Responses API", async () => { // Mock fetch for Responses API fallback const mockFetch = vitest.fn().mockResolvedValue({ @@ -221,6 +244,28 @@ describe("OpenAiNativeHandler", () => { ) }) + it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Priority])( + "should include the selected %s service tier", + async (serviceTier) => { + mockResponsesCreate.mockResolvedValue({ output: [] }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: serviceTier, + }) + + await handler.completePrompt("Test prompt") + + expect(mockResponsesCreate).toHaveBeenCalledWith( + expect.objectContaining({ + stream: false, + [SERVICE_TIER_KEY]: serviceTier, + }), + expect.any(Object), + ) + }, + ) + it("should handle SDK errors in completePrompt", async () => { // Mock SDK to throw an error mockResponsesCreate.mockRejectedValue(new Error("API Error")) @@ -332,12 +377,33 @@ describe("OpenAiNativeHandler", () => { expect(modelInfo.info.longContextPricing).toBeUndefined() expect(modelInfo.info.tiers).toEqual([ expect.objectContaining({ - name: "flex", + name: OpenAiServiceTier.Flex, outputPrice: 0.625, }), ]) }) + it("should retain standard pricing for an explicitly selected default tier", () => { + const defaultTierHandler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.4", + openAiNativeServiceTier: OpenAiServiceTier.Default, + }) + const model = defaultTierHandler.getModel() + const normalizeUsage = Reflect.get(defaultTierHandler, "normalizeUsage") + + const result = Reflect.apply(normalizeUsage, defaultTierHandler, [ + { + input_tokens: 100_000, + output_tokens: 1_000, + cache_read_input_tokens: 20_000, + }, + model, + ]) as { totalCost: number } + + expect(result.totalCost).toBeCloseTo(0.22, 6) + }) + it("should return GPT-5.3 Chat model info when selected", () => { const chatHandler = new OpenAiNativeHandler({ ...mockOptions, diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index 645ad8d354..0d39e843c4 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -33,6 +33,7 @@ import { BEDROCK_GLOBAL_INFERENCE_MODEL_IDS, BEDROCK_SERVICE_TIER_MODEL_IDS, BEDROCK_SERVICE_TIER_PRICING, + SERVICE_TIER_KEY, ApiProviderError, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -99,7 +100,7 @@ interface BedrockPayload { // AWS Bedrock service tiers (STANDARD, FLEX, PRIORITY) are specified at the top level // https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html type BedrockPayloadWithServiceTier = BedrockPayload & { - service_tier?: BedrockServiceTier + [SERVICE_TIER_KEY]?: BedrockServiceTier } // Define specific types for content block events to avoid 'as any' usage @@ -553,7 +554,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH ...(thinkingEnabled && { anthropic_version: "bedrock-2023-05-31" }), toolConfig, // Add service_tier as a top-level parameter (not inside additionalModelRequestFields) - ...(useServiceTier && { service_tier: this.options.awsBedrockServiceTier }), + ...(useServiceTier && { [SERVICE_TIER_KEY]: this.options.awsBedrockServiceTier }), } // Create AbortController with 10 minute timeout diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index a1ce2d89d0..e3ca443b44 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -13,6 +13,8 @@ import { type ReasoningEffort, type VerbosityLevel, type ReasoningEffortExtended, + OpenAiServiceTier, + SERVICE_TIER_KEY, type ServiceTier, ApiProviderError, } from "@roo-code/types" @@ -318,7 +320,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio max_output_tokens?: number store?: boolean instructions?: string - service_tier?: ServiceTier + [SERVICE_TIER_KEY]?: ServiceTier include?: string[] /** Prompt cache retention policy: "in_memory" (default) or "24h" for extended caching */ prompt_cache_retention?: "in_memory" | "24h" @@ -369,8 +371,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio ...(model.maxTokens ? { max_output_tokens: model.maxTokens } : {}), // Include tier when selected and supported by the model, or when explicitly "default" ...(requestedTier && - (requestedTier === "default" || allowedTierNames.has(requestedTier)) && { - service_tier: requestedTier, + (requestedTier === OpenAiServiceTier.Default || allowedTierNames.has(requestedTier)) && { + [SERVICE_TIER_KEY]: requestedTier, }), // Enable extended prompt cache retention for models that support it. // This uses the OpenAI Responses API `prompt_cache_retention` parameter. @@ -705,8 +707,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio const parsed = JSON.parse(data) // Capture resolved service tier if present - if (parsed.response?.service_tier) { - this.lastServiceTier = parsed.response.service_tier as ServiceTier + if (parsed.response?.[SERVICE_TIER_KEY]) { + this.lastServiceTier = parsed.response[SERVICE_TIER_KEY] as ServiceTier } // Capture complete output array (includes reasoning items with encrypted_content) if (parsed.response?.output && Array.isArray(parsed.response.output)) { @@ -1016,8 +1018,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } } else if (parsed.type === "response.completed" || parsed.type === "response.done") { // Capture resolved service tier if present - if (parsed.response?.service_tier) { - this.lastServiceTier = parsed.response.service_tier as ServiceTier + if (parsed.response?.[SERVICE_TIER_KEY]) { + this.lastServiceTier = parsed.response[SERVICE_TIER_KEY] as ServiceTier } // Capture top-level response id if (parsed.response?.id) { @@ -1146,8 +1148,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio */ private async *processEvent(event: any, model: OpenAiNativeModel): ApiStream { // Capture resolved service tier when available - if (event?.response?.service_tier) { - this.lastServiceTier = event.response.service_tier as ServiceTier + if (event?.response?.[SERVICE_TIER_KEY]) { + this.lastServiceTier = event.response[SERVICE_TIER_KEY] as ServiceTier } // Capture complete output array (includes reasoning items with encrypted_content) if (event?.response?.output && Array.isArray(event.response.output)) { @@ -1418,7 +1420,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio * If no tier or no overrides exist, the original ModelInfo is returned. */ private applyServiceTierPricing(info: ModelInfo, tier?: ServiceTier): ModelInfo { - if (!tier || tier === "default") return info + if (!tier || tier === OpenAiServiceTier.Default) return info // Find the tier with matching name in the tiers array const tierInfo = info.tiers?.find((t) => t.name === tier) @@ -1512,8 +1514,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio // Include service tier if selected and supported const requestedTier = (this.options.openAiNativeServiceTier as ServiceTier | undefined) || undefined const allowedTierNames = new Set(model.info.tiers?.map((t) => t.name).filter(Boolean) || []) - if (requestedTier && (requestedTier === "default" || allowedTierNames.has(requestedTier))) { - requestBody.service_tier = requestedTier + if (requestedTier && (requestedTier === OpenAiServiceTier.Default || allowedTierNames.has(requestedTier))) { + requestBody[SERVICE_TIER_KEY] = requestedTier } // Add reasoning if supported diff --git a/src/shared/cost.ts b/src/shared/cost.ts index 8954904fda..754a2d84df 100644 --- a/src/shared/cost.ts +++ b/src/shared/cost.ts @@ -1,5 +1,4 @@ -import type { ModelInfo } from "@roo-code/types" -import type { ServiceTier } from "@roo-code/types" +import { OpenAiServiceTier, type ModelInfo, type ServiceTier } from "@roo-code/types" export interface ApiCostResult { totalInputTokens: number @@ -13,7 +12,7 @@ function applyLongContextPricing(modelInfo: ModelInfo, totalInputTokens: number, return modelInfo } - const effectiveServiceTier = serviceTier ?? "default" + const effectiveServiceTier = serviceTier ?? OpenAiServiceTier.Default if (pricing.appliesToServiceTiers && !pricing.appliesToServiceTiers.includes(effectiveServiceTier)) { return modelInfo } diff --git a/src/utils/__tests__/cost.spec.ts b/src/utils/__tests__/cost.spec.ts index 6f0b594c8d..b369a0405b 100644 --- a/src/utils/__tests__/cost.spec.ts +++ b/src/utils/__tests__/cost.spec.ts @@ -1,6 +1,6 @@ // npx vitest utils/__tests__/cost.spec.ts -import type { ModelInfo } from "@roo-code/types" +import { OpenAiServiceTier, type ModelInfo } from "@roo-code/types" import { calculateApiCostAnthropic, calculateApiCostOpenAI } from "../../shared/cost" @@ -283,7 +283,7 @@ describe("Cost Utility", () => { thresholdTokens: 272_000, inputPriceMultiplier: 2, outputPriceMultiplier: 1.5, - appliesToServiceTiers: ["default", "flex"], + appliesToServiceTiers: [OpenAiServiceTier.Default, OpenAiServiceTier.Flex], }, } @@ -293,7 +293,7 @@ describe("Cost Utility", () => { 1_000, undefined, 100_000, - "priority", + OpenAiServiceTier.Priority, ) // Input cost: (5.0 / 1_000_000) * (300000 - 100000) = 1.0 diff --git a/webview-ui/src/components/settings/ModelInfoView.tsx b/webview-ui/src/components/settings/ModelInfoView.tsx index e043f68f83..bbf9fa39b2 100644 --- a/webview-ui/src/components/settings/ModelInfoView.tsx +++ b/webview-ui/src/components/settings/ModelInfoView.tsx @@ -1,6 +1,6 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import type { ModelInfo } from "@roo-code/types" +import { OpenAiServiceTier, providerIdentifiers, type ModelInfo } from "@roo-code/types" import { formatPrice } from "@src/utils/formatPrice" import { cn } from "@src/lib/utils" @@ -29,8 +29,10 @@ export const ModelInfoView = ({ // Show tiered pricing table for OpenAI Native when model supports non-standard tiers const allowedTierNames = - modelInfo?.tiers?.filter((t) => t.name === "flex" || t.name === "priority")?.map((t) => t.name) ?? [] - const shouldShowTierPricingTable = apiProvider === "openai-native" && allowedTierNames.length > 0 + modelInfo?.tiers + ?.filter((t) => t.name === OpenAiServiceTier.Flex || t.name === OpenAiServiceTier.Priority) + ?.map((t) => t.name) ?? [] + const shouldShowTierPricingTable = apiProvider === providerIdentifiers.openaiNative && allowedTierNames.length > 0 const fmt = (n?: number) => (typeof n === "number" ? `${formatPrice(n)}` : "—") const baseInfoItems = [ @@ -144,48 +146,48 @@ export const ModelInfoView = ({ {fmt(modelInfo?.outputPrice)} {fmt(modelInfo?.cacheReadsPrice)} - {allowedTierNames.includes("flex") && ( + {allowedTierNames.includes(OpenAiServiceTier.Flex) && ( {t("settings:serviceTier.flex")} {fmt( - modelInfo?.tiers?.find((t) => t.name === "flex")?.inputPrice ?? - modelInfo?.inputPrice, + modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Flex) + ?.inputPrice ?? modelInfo?.inputPrice, )} {fmt( - modelInfo?.tiers?.find((t) => t.name === "flex")?.outputPrice ?? - modelInfo?.outputPrice, + modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Flex) + ?.outputPrice ?? modelInfo?.outputPrice, )} {fmt( - modelInfo?.tiers?.find((t) => t.name === "flex")?.cacheReadsPrice ?? - modelInfo?.cacheReadsPrice, + modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Flex) + ?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice, )} )} - {allowedTierNames.includes("priority") && ( + {allowedTierNames.includes(OpenAiServiceTier.Priority) && ( {t("settings:serviceTier.priority")} {fmt( - modelInfo?.tiers?.find((t) => t.name === "priority")?.inputPrice ?? - modelInfo?.inputPrice, + modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Priority) + ?.inputPrice ?? modelInfo?.inputPrice, )} {fmt( - modelInfo?.tiers?.find((t) => t.name === "priority")?.outputPrice ?? - modelInfo?.outputPrice, + modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Priority) + ?.outputPrice ?? modelInfo?.outputPrice, )} {fmt( - modelInfo?.tiers?.find((t) => t.name === "priority")?.cacheReadsPrice ?? - modelInfo?.cacheReadsPrice, + modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Priority) + ?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice, )} diff --git a/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx b/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx new file mode 100644 index 0000000000..2da94ce028 --- /dev/null +++ b/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx @@ -0,0 +1,97 @@ +import { OpenAiServiceTier, providerIdentifiers, type ModelInfo } from "@roo-code/types" + +import { render, screen, within } from "@/utils/test-utils" + +import { ModelInfoView } from "../ModelInfoView" + +vi.mock("@src/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => + ({ + "settings:serviceTier.pricingTableTitle": "Service tier pricing", + "settings:serviceTier.columns.tier": "Tier", + "settings:serviceTier.columns.input": "Input", + "settings:serviceTier.columns.output": "Output", + "settings:serviceTier.columns.cacheReads": "Cache reads", + "settings:serviceTier.standard": "Standard", + "settings:serviceTier.flex": "Flex", + "settings:serviceTier.priority": "Priority", + })[key] ?? key, + }), +})) + +const baseModelInfo: ModelInfo = { + contextWindow: 128_000, + supportsPromptCache: true, + inputPrice: 10, + outputPrice: 20, + cacheReadsPrice: 3, +} + +const defaultProps = { + selectedModelId: "gpt-test", + isDescriptionExpanded: false, + setIsDescriptionExpanded: vi.fn(), +} + +const getPricingRowValues = (tier: string) => { + const row = screen.getByRole("cell", { name: tier }).closest("tr") + expect(row).not.toBeNull() + return within(row!) + .getAllByRole("cell") + .map((cell) => cell.textContent) +} + +describe("ModelInfoView service tier pricing", () => { + it("shows OpenAI Native tier prices with per-field fallback to Standard pricing", () => { + const modelInfo: ModelInfo = { + ...baseModelInfo, + tiers: [ + { name: OpenAiServiceTier.Default, contextWindow: 128_000 }, + { + name: OpenAiServiceTier.Flex, + contextWindow: 128_000, + inputPrice: 4, + cacheReadsPrice: 1, + }, + { + name: OpenAiServiceTier.Priority, + contextWindow: 128_000, + outputPrice: 40, + }, + ], + } + + render() + + expect(screen.getByText("Service tier pricing")).toBeInTheDocument() + expect(getPricingRowValues("Standard")).toEqual(["Standard", "$10.00", "$20.00", "$3.00"]) + expect(getPricingRowValues("Flex")).toEqual(["Flex", "$4.00", "$20.00", "$1.00"]) + expect(getPricingRowValues("Priority")).toEqual(["Priority", "$10.00", "$40.00", "$3.00"]) + }) + + it("only shows the tier pricing table for OpenAI Native models with a non-standard tier", () => { + const tieredModelInfo: ModelInfo = { + ...baseModelInfo, + tiers: [{ name: OpenAiServiceTier.Flex, contextWindow: 128_000 }], + } + const { rerender } = render( + , + ) + + expect(screen.queryByText("Service tier pricing")).not.toBeInTheDocument() + + rerender( + , + ) + + expect(screen.queryByText("Service tier pricing")).not.toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/components/settings/providers/OpenAI.tsx b/webview-ui/src/components/settings/providers/OpenAI.tsx index 96fd6c89be..460019e75a 100644 --- a/webview-ui/src/components/settings/providers/OpenAI.tsx +++ b/webview-ui/src/components/settings/providers/OpenAI.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react" import { Checkbox } from "vscrui" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import type { ModelInfo, ProviderSettings } from "@roo-code/types" +import { OpenAiServiceTier, type ModelInfo, type ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" @@ -78,7 +78,7 @@ export const OpenAI = ({ apiConfiguration, setApiConfigurationField, selectedMod {(() => { const allowedTiers = (selectedModelInfo?.tiers?.map((t) => t.name).filter(Boolean) || []).filter( - (t) => t === "flex" || t === "priority", + (t) => t === OpenAiServiceTier.Flex || t === OpenAiServiceTier.Priority, ) if (allowedTiers.length === 0) return null @@ -92,7 +92,7 @@ export const OpenAI = ({ apiConfiguration, setApiConfigurationField, selectedMod diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAI.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAI.spec.tsx new file mode 100644 index 0000000000..239c76932f --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAI.spec.tsx @@ -0,0 +1,92 @@ +import React from "react" + +import { OpenAiServiceTier, providerIdentifiers, type ModelInfo, type ProviderSettings } from "@roo-code/types" + +import { fireEvent, render, screen } from "@/utils/test-utils" + +import { OpenAI } from "../OpenAI" + +vi.mock("@src/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ t: (key: string) => key }), +})) + +vi.mock("vscrui", () => ({ + Checkbox: ({ children }: { children: React.ReactNode }) =>
{children}
, +})) + +vi.mock("@src/components/ui", () => ({ + Select: ({ children, value, onValueChange }: any) => ( + + ), + SelectContent: ({ children }: any) => <>{children}, + SelectItem: ({ children, value }: any) => , + SelectTrigger: () => null, + SelectValue: () => null, + StandardTooltip: ({ children, content }: any) => {children}, +})) + +const baseModelInfo: ModelInfo = { + contextWindow: 128_000, + supportsPromptCache: true, +} + +describe("OpenAI service tier selector", () => { + it("shows supported service tiers and persists the selected tier", () => { + const setApiConfigurationField = vi.fn() + const selectedModelInfo: ModelInfo = { + ...baseModelInfo, + tiers: [ + { name: OpenAiServiceTier.Default, contextWindow: 128_000 }, + { contextWindow: 128_000 }, + { name: OpenAiServiceTier.Flex, contextWindow: 128_000 }, + { name: OpenAiServiceTier.Priority, contextWindow: 128_000 }, + ], + } + const apiConfiguration: ProviderSettings = { + apiProvider: providerIdentifiers.openaiNative, + openAiNativeApiKey: "test-api-key", + } + + render( + , + ) + + const selector = screen.getByRole("combobox", { name: "Service tier" }) + expect(selector).toHaveValue(OpenAiServiceTier.Default) + expect(screen.getAllByRole("option").map((option) => option.textContent)).toEqual([ + "Standard", + "Flex", + "Priority", + ]) + + fireEvent.change(selector, { target: { value: OpenAiServiceTier.Flex } }) + expect(setApiConfigurationField).toHaveBeenLastCalledWith("openAiNativeServiceTier", OpenAiServiceTier.Flex) + + fireEvent.change(selector, { target: { value: OpenAiServiceTier.Priority } }) + expect(setApiConfigurationField).toHaveBeenLastCalledWith("openAiNativeServiceTier", OpenAiServiceTier.Priority) + }) + + it("hides the selector when the model only exposes the default tier", () => { + render( + , + ) + + expect(screen.queryByTestId("openai-service-tier")).not.toBeInTheDocument() + }) +}) From 4ca1971e1451ea52b829b2557b2551d0abdb11f7 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 00:34:32 +0300 Subject: [PATCH 02/14] test(openai-native): cover omitted completion tiers --- .../providers/__tests__/openai-native.spec.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index 63c2e4e396..7fa112a6ab 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -266,6 +266,31 @@ describe("OpenAiNativeHandler", () => { }, ) + it("should omit the service tier when none is configured", async () => { + mockResponsesCreate.mockResolvedValue({ output: [] }) + + await handler.completePrompt("Test prompt") + + const [request] = mockResponsesCreate.mock.calls[0] + expect(request.stream).toBe(false) + expect(request).not.toHaveProperty(SERVICE_TIER_KEY) + }) + + it("should omit a configured service tier that the model does not support", async () => { + mockResponsesCreate.mockResolvedValue({ output: [] }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-luna", + openAiNativeServiceTier: OpenAiServiceTier.Priority, + }) + + await handler.completePrompt("Test prompt") + + const [request] = mockResponsesCreate.mock.calls[0] + expect(request.stream).toBe(false) + expect(request).not.toHaveProperty(SERVICE_TIER_KEY) + }) + it("should handle SDK errors in completePrompt", async () => { // Mock SDK to throw an error mockResponsesCreate.mockRejectedValue(new Error("API Error")) From 9cdcbee9582c1f47170f680bb2c8bd3b4750d892 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 00:56:39 +0300 Subject: [PATCH 03/14] test(openai-native): cover resolved streaming tiers --- .../providers/__tests__/openai-native.spec.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index 7fa112a6ab..bf1e642fa6 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -145,6 +145,78 @@ describe("OpenAiNativeHandler", () => { }, ) + it("prices SDK stream usage using the service tier resolved by OpenAI", async () => { + mockResponsesCreate.mockResolvedValue({ + async *[Symbol.asyncIterator]() { + yield { + type: "response.done", + response: { + [SERVICE_TIER_KEY]: OpenAiServiceTier.Priority, + usage: { input_tokens: 100, output_tokens: 20 }, + }, + } + }, + }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: OpenAiServiceTier.Default, + }) + + const chunks = [] + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + chunks.push(chunk) + } + + expect(chunks).toContainEqual( + expect.objectContaining({ + type: "usage", + inputTokens: 100, + outputTokens: 20, + totalCost: 0.00275, + }), + ) + }) + + it("requests the configured tier but prices manual SSE fallback usage using OpenAI's resolved tier", async () => { + mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) + const mockFetch = vitest.fn().mockResolvedValue({ + ok: true, + body: new ReadableStream({ + start(controller) { + controller.enqueue( + new TextEncoder().encode( + `data: ${JSON.stringify({ + type: "response.done", + response: { + [SERVICE_TIER_KEY]: OpenAiServiceTier.Priority, + usage: { input_tokens: 100, output_tokens: 20 }, + }, + })}\n\n`, + ), + ) + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() + }, + }), + }) + global.fetch = mockFetch as typeof fetch + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: OpenAiServiceTier.Default, + }) + + const chunks = [] + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + chunks.push(chunk) + } + + const [, request] = mockFetch.mock.calls[0] + expect(JSON.parse(request.body)).toMatchObject({ [SERVICE_TIER_KEY]: OpenAiServiceTier.Default }) + expect(chunks).toContainEqual(expect.objectContaining({ type: "usage", totalCost: 0.00275 })) + }) + it("should handle streaming responses via Responses API", async () => { // Mock fetch for Responses API fallback const mockFetch = vitest.fn().mockResolvedValue({ From 224824a60986a007f6afd6c57439ce78a692a233 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 01:22:06 +0300 Subject: [PATCH 04/14] test(api): cover service tier fallbacks --- .../providers/__tests__/openai-native.spec.ts | 22 +++++++++++++++++++ .../settings/__tests__/ModelInfoView.spec.tsx | 12 ++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index bf1e642fa6..677dce16b1 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -501,6 +501,28 @@ describe("OpenAiNativeHandler", () => { expect(result.totalCost).toBeCloseTo(0.22, 6) }) + it("should retain standard pricing when no service tier is selected", () => { + const model = handler.getModel() + const applyServiceTierPricing = Reflect.get(handler, "applyServiceTierPricing") + + const result = Reflect.apply(applyServiceTierPricing, handler, [model.info]) + + expect(result).toBe(model.info) + }) + + it("should retain standard pricing when the resolved service tier has no pricing entry", () => { + const model = handler.getModel() + const infoWithoutTiers = { ...model.info, tiers: undefined } + const applyServiceTierPricing = Reflect.get(handler, "applyServiceTierPricing") + + const result = Reflect.apply(applyServiceTierPricing, handler, [ + infoWithoutTiers, + OpenAiServiceTier.Priority, + ]) + + expect(result).toBe(infoWithoutTiers) + }) + it("should return GPT-5.3 Chat model info when selected", () => { const chatHandler = new OpenAiNativeHandler({ ...mockOptions, diff --git a/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx b/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx index 2da94ce028..749a5877fd 100644 --- a/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx @@ -94,4 +94,16 @@ describe("ModelInfoView service tier pricing", () => { expect(screen.queryByText("Service tier pricing")).not.toBeInTheDocument() }) + + it("does not show the tier pricing table when model info has no tiers", () => { + render( + , + ) + + expect(screen.queryByText("Service tier pricing")).not.toBeInTheDocument() + }) }) From 9d31fe78fe49f68067f8121860397caf78aa061f Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 01:56:17 +0300 Subject: [PATCH 05/14] test(api): cover remaining service tier branches --- .../providers/__tests__/openai-native.spec.ts | 42 +++++++++++++++++++ .../settings/__tests__/ModelInfoView.spec.tsx | 20 +++++++++ 2 files changed, 62 insertions(+) diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index 677dce16b1..e44db57e80 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -217,6 +217,48 @@ describe("OpenAiNativeHandler", () => { expect(chunks).toContainEqual(expect.objectContaining({ type: "usage", totalCost: 0.00275 })) }) + it("captures the resolved tier from a manual SSE completion event", async () => { + mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) + const mockFetch = vitest.fn().mockResolvedValue({ + ok: true, + body: new ReadableStream({ + start(controller) { + controller.enqueue( + new TextEncoder().encode( + `data: ${JSON.stringify({ + type: "response.completed", + response: { [SERVICE_TIER_KEY]: OpenAiServiceTier.Priority }, + })}\n\n`, + ), + ) + controller.enqueue( + new TextEncoder().encode( + `data: ${JSON.stringify({ + type: "response.usage", + usage: { input_tokens: 100, output_tokens: 20 }, + })}\n\n`, + ), + ) + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() + }, + }), + }) + global.fetch = mockFetch as typeof fetch + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: OpenAiServiceTier.Default, + }) + + const chunks = [] + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + chunks.push(chunk) + } + + expect(chunks).toContainEqual(expect.objectContaining({ type: "usage", totalCost: 0.00275 })) + }) + it("should handle streaming responses via Responses API", async () => { // Mock fetch for Responses API fallback const mockFetch = vitest.fn().mockResolvedValue({ diff --git a/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx b/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx index 749a5877fd..938bc8bff7 100644 --- a/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/ModelInfoView.spec.tsx @@ -106,4 +106,24 @@ describe("ModelInfoView service tier pricing", () => { expect(screen.queryByText("Service tier pricing")).not.toBeInTheDocument() }) + + it("shows unavailable prices when neither the tier nor Standard defines them", () => { + render( + , + ) + + expect(getPricingRowValues("Flex")).toEqual(["Flex", "—", "—", "—"]) + expect(getPricingRowValues("Priority")).toEqual(["Priority", "—", "—", "—"]) + }) }) From 69374133bfae6e60c946c0f27f13aa1a9332f62b Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 02:08:15 +0300 Subject: [PATCH 06/14] refactor(api): remove duplicate tier capture --- src/api/providers/openai-native.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index e3ca443b44..fc14952f1d 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -1017,10 +1017,6 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio ) } } else if (parsed.type === "response.completed" || parsed.type === "response.done") { - // Capture resolved service tier if present - if (parsed.response?.[SERVICE_TIER_KEY]) { - this.lastServiceTier = parsed.response[SERVICE_TIER_KEY] as ServiceTier - } // Capture top-level response id if (parsed.response?.id) { this.lastResponseId = parsed.response.id as string From aac9e970d2ca8823084b25022be43b51274de48f Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 03:05:26 +0300 Subject: [PATCH 07/14] test(openai-native): cover flex service tier --- src/api/providers/__tests__/openai-native.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index e44db57e80..db30f12edd 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -122,7 +122,7 @@ describe("OpenAiNativeHandler", () => { }) describe("createMessage", () => { - it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Priority])( + it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Flex, OpenAiServiceTier.Priority])( "should include the selected %s service tier", async (serviceTier) => { mockResponsesCreate.mockResolvedValue({ @@ -358,7 +358,7 @@ describe("OpenAiNativeHandler", () => { ) }) - it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Priority])( + it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Flex, OpenAiServiceTier.Priority])( "should include the selected %s service tier", async (serviceTier) => { mockResponsesCreate.mockResolvedValue({ output: [] }) From 007a4bdc6a1a83fd133ca6c89037d8bde2cf47f4 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 03:47:37 +0300 Subject: [PATCH 08/14] test(openai-native): expand service tier coverage --- .../providers/__tests__/openai-native.spec.ts | 298 ++++++++++-------- 1 file changed, 163 insertions(+), 135 deletions(-) diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index db30f12edd..29f90741ac 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -13,7 +13,7 @@ vitest.mock("@roo-code/telemetry", () => ({ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -import { ApiProviderError, OpenAiServiceTier, SERVICE_TIER_KEY } from "@roo-code/types" +import { ApiProviderError, OpenAiServiceTier, SERVICE_TIER_KEY, serviceTiers } from "@roo-code/types" import { OpenAiNativeHandler } from "../openai-native" import { ApiHandlerOptions } from "../../../shared/api" @@ -22,6 +22,24 @@ import { Package } from "../../../shared/package" // Mock OpenAI client - now everything uses Responses API const mockResponsesCreate = vitest.fn() +const serviceTierPricingCases = [ + { + requestedTier: OpenAiServiceTier.Default, + resolvedTier: OpenAiServiceTier.Priority, + expectedCost: 0.00275, + }, + { + requestedTier: OpenAiServiceTier.Priority, + resolvedTier: OpenAiServiceTier.Flex, + expectedCost: 0.00055, + }, + { + requestedTier: OpenAiServiceTier.Flex, + resolvedTier: OpenAiServiceTier.Default, + expectedCost: 0.0011, + }, +] + vitest.mock("openai", () => { return { __esModule: true, @@ -122,142 +140,155 @@ describe("OpenAiNativeHandler", () => { }) describe("createMessage", () => { - it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Flex, OpenAiServiceTier.Priority])( - "should include the selected %s service tier", - async (serviceTier) => { + it.each(serviceTiers)("should include the selected %s service tier", async (serviceTier) => { + mockResponsesCreate.mockResolvedValue({ + async *[Symbol.asyncIterator]() {}, + }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: serviceTier, + }) + + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + void chunk + } + + expect(mockResponsesCreate).toHaveBeenCalledWith( + expect.objectContaining({ [SERVICE_TIER_KEY]: serviceTier }), + expect.any(Object), + ) + }) + + it.each(serviceTierPricingCases)( + "prices SDK stream usage using resolved $resolvedTier tier instead of requested $requestedTier tier", + async ({ requestedTier, resolvedTier, expectedCost }) => { mockResponsesCreate.mockResolvedValue({ - async *[Symbol.asyncIterator]() {}, + async *[Symbol.asyncIterator]() { + yield { + type: "response.done", + response: { + [SERVICE_TIER_KEY]: resolvedTier, + usage: { input_tokens: 100, output_tokens: 20 }, + }, + } + }, }) handler = new OpenAiNativeHandler({ ...mockOptions, apiModelId: "gpt-5.6-sol", - openAiNativeServiceTier: serviceTier, + openAiNativeServiceTier: requestedTier, }) + const chunks = [] for await (const chunk of handler.createMessage(systemPrompt, messages)) { - void chunk + chunks.push(chunk) } - expect(mockResponsesCreate).toHaveBeenCalledWith( - expect.objectContaining({ [SERVICE_TIER_KEY]: serviceTier }), - expect.any(Object), + expect(chunks).toContainEqual( + expect.objectContaining({ + type: "usage", + inputTokens: 100, + outputTokens: 20, + totalCost: expectedCost, + }), ) }, ) - it("prices SDK stream usage using the service tier resolved by OpenAI", async () => { - mockResponsesCreate.mockResolvedValue({ - async *[Symbol.asyncIterator]() { - yield { - type: "response.done", - response: { - [SERVICE_TIER_KEY]: OpenAiServiceTier.Priority, - usage: { input_tokens: 100, output_tokens: 20 }, + it.each(serviceTierPricingCases)( + "requests $requestedTier but prices manual SSE fallback usage using OpenAI's resolved $resolvedTier tier", + async ({ requestedTier, resolvedTier, expectedCost }) => { + mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) + const mockFetch = vitest.fn().mockResolvedValue({ + ok: true, + body: new ReadableStream({ + start(controller) { + controller.enqueue( + new TextEncoder().encode( + `data: ${JSON.stringify({ + type: "response.done", + response: { + [SERVICE_TIER_KEY]: resolvedTier, + usage: { input_tokens: 100, output_tokens: 20 }, + }, + })}\n\n`, + ), + ) + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() }, - } - }, - }) - handler = new OpenAiNativeHandler({ - ...mockOptions, - apiModelId: "gpt-5.6-sol", - openAiNativeServiceTier: OpenAiServiceTier.Default, - }) + }), + }) + global.fetch = mockFetch as typeof fetch + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: requestedTier, + }) - const chunks = [] - for await (const chunk of handler.createMessage(systemPrompt, messages)) { - chunks.push(chunk) - } + const chunks = [] + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + chunks.push(chunk) + } - expect(chunks).toContainEqual( - expect.objectContaining({ - type: "usage", - inputTokens: 100, - outputTokens: 20, - totalCost: 0.00275, - }), - ) - }) + const [, request] = mockFetch.mock.calls[0] + expect(JSON.parse(request.body)).toMatchObject({ [SERVICE_TIER_KEY]: requestedTier }) + expect(chunks).toContainEqual(expect.objectContaining({ type: "usage", totalCost: expectedCost })) + }, + ) - it("requests the configured tier but prices manual SSE fallback usage using OpenAI's resolved tier", async () => { - mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) - const mockFetch = vitest.fn().mockResolvedValue({ - ok: true, - body: new ReadableStream({ - start(controller) { - controller.enqueue( - new TextEncoder().encode( - `data: ${JSON.stringify({ - type: "response.done", - response: { - [SERVICE_TIER_KEY]: OpenAiServiceTier.Priority, + it.each(serviceTierPricingCases)( + "captures resolved $resolvedTier tier from a manual SSE completion event when $requestedTier was requested", + async ({ requestedTier, resolvedTier, expectedCost }) => { + mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) + const mockFetch = vitest.fn().mockResolvedValue({ + ok: true, + body: new ReadableStream({ + start(controller) { + controller.enqueue( + new TextEncoder().encode( + `data: ${JSON.stringify({ + type: "response.completed", + response: { [SERVICE_TIER_KEY]: resolvedTier }, + })}\n\n`, + ), + ) + controller.enqueue( + new TextEncoder().encode( + `data: ${JSON.stringify({ + type: "response.usage", usage: { input_tokens: 100, output_tokens: 20 }, - }, - })}\n\n`, - ), - ) - controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) - controller.close() - }, - }), - }) - global.fetch = mockFetch as typeof fetch - handler = new OpenAiNativeHandler({ - ...mockOptions, - apiModelId: "gpt-5.6-sol", - openAiNativeServiceTier: OpenAiServiceTier.Default, - }) - - const chunks = [] - for await (const chunk of handler.createMessage(systemPrompt, messages)) { - chunks.push(chunk) - } - - const [, request] = mockFetch.mock.calls[0] - expect(JSON.parse(request.body)).toMatchObject({ [SERVICE_TIER_KEY]: OpenAiServiceTier.Default }) - expect(chunks).toContainEqual(expect.objectContaining({ type: "usage", totalCost: 0.00275 })) - }) - - it("captures the resolved tier from a manual SSE completion event", async () => { - mockResponsesCreate.mockRejectedValue(new Error("SDK not available")) - const mockFetch = vitest.fn().mockResolvedValue({ - ok: true, - body: new ReadableStream({ - start(controller) { - controller.enqueue( - new TextEncoder().encode( - `data: ${JSON.stringify({ - type: "response.completed", - response: { [SERVICE_TIER_KEY]: OpenAiServiceTier.Priority }, - })}\n\n`, - ), - ) - controller.enqueue( - new TextEncoder().encode( - `data: ${JSON.stringify({ - type: "response.usage", - usage: { input_tokens: 100, output_tokens: 20 }, - })}\n\n`, - ), - ) - controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) - controller.close() - }, - }), - }) - global.fetch = mockFetch as typeof fetch - handler = new OpenAiNativeHandler({ - ...mockOptions, - apiModelId: "gpt-5.6-sol", - openAiNativeServiceTier: OpenAiServiceTier.Default, - }) + })}\n\n`, + ), + ) + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() + }, + }), + }) + global.fetch = mockFetch as typeof fetch + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: requestedTier, + }) - const chunks = [] - for await (const chunk of handler.createMessage(systemPrompt, messages)) { - chunks.push(chunk) - } + const chunks = [] + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + chunks.push(chunk) + } - expect(chunks).toContainEqual(expect.objectContaining({ type: "usage", totalCost: 0.00275 })) - }) + expect(chunks).toContainEqual( + expect.objectContaining({ + type: "usage", + inputTokens: 100, + outputTokens: 20, + totalCost: expectedCost, + }), + ) + }, + ) it("should handle streaming responses via Responses API", async () => { // Mock fetch for Responses API fallback @@ -358,27 +389,24 @@ describe("OpenAiNativeHandler", () => { ) }) - it.each([OpenAiServiceTier.Default, OpenAiServiceTier.Flex, OpenAiServiceTier.Priority])( - "should include the selected %s service tier", - async (serviceTier) => { - mockResponsesCreate.mockResolvedValue({ output: [] }) - handler = new OpenAiNativeHandler({ - ...mockOptions, - apiModelId: "gpt-5.6-sol", - openAiNativeServiceTier: serviceTier, - }) + it.each(serviceTiers)("should include the selected %s service tier", async (serviceTier) => { + mockResponsesCreate.mockResolvedValue({ output: [] }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: "gpt-5.6-sol", + openAiNativeServiceTier: serviceTier, + }) - await handler.completePrompt("Test prompt") + await handler.completePrompt("Test prompt") - expect(mockResponsesCreate).toHaveBeenCalledWith( - expect.objectContaining({ - stream: false, - [SERVICE_TIER_KEY]: serviceTier, - }), - expect.any(Object), - ) - }, - ) + expect(mockResponsesCreate).toHaveBeenCalledWith( + expect.objectContaining({ + stream: false, + [SERVICE_TIER_KEY]: serviceTier, + }), + expect.any(Object), + ) + }) it("should omit the service tier when none is configured", async () => { mockResponsesCreate.mockResolvedValue({ output: [] }) From 66ae5a991445b328bd867f14c608b8062a6e1dce Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 05:50:56 +0300 Subject: [PATCH 09/14] refactor: address service tier review feedback --- src/api/providers/openai-native.ts | 22 ++++-- .../src/components/settings/ModelInfoView.tsx | 74 ++++++++----------- 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index fc14952f1d..19d529acf9 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -336,8 +336,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } // Validate requested tier against model support; if not supported, omit. - const requestedTier = (this.options.openAiNativeServiceTier as ServiceTier | undefined) || undefined const allowedTierNames = new Set(model.info.tiers?.map((t) => t.name).filter(Boolean) || []) + const serviceTier = this.getAllowedServiceTier(this.options.openAiNativeServiceTier, allowedTierNames) // Decide whether to enable extended prompt cache retention for this request const promptCacheRetention = this.getPromptCacheRetention(model) @@ -370,10 +370,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio // Use the per-request reserved output computed by Roo (params.maxTokens from getModelParams). ...(model.maxTokens ? { max_output_tokens: model.maxTokens } : {}), // Include tier when selected and supported by the model, or when explicitly "default" - ...(requestedTier && - (requestedTier === OpenAiServiceTier.Default || allowedTierNames.has(requestedTier)) && { - [SERVICE_TIER_KEY]: requestedTier, - }), + ...(serviceTier && { [SERVICE_TIER_KEY]: serviceTier }), // Enable extended prompt cache retention for models that support it. // This uses the OpenAI Responses API `prompt_cache_retention` parameter. ...(promptCacheRetention ? { prompt_cache_retention: promptCacheRetention } : {}), @@ -1431,6 +1428,15 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } } + private getAllowedServiceTier( + requestedTier: ServiceTier | undefined, + allowedTierNames: ReadonlySet, + ): ServiceTier | undefined { + return requestedTier === OpenAiServiceTier.Default || (requestedTier && allowedTierNames.has(requestedTier)) + ? requestedTier + : undefined + } + // Removed isResponsesApiModel method as ALL models now use the Responses API override getModel() { @@ -1508,10 +1514,10 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } // Include service tier if selected and supported - const requestedTier = (this.options.openAiNativeServiceTier as ServiceTier | undefined) || undefined const allowedTierNames = new Set(model.info.tiers?.map((t) => t.name).filter(Boolean) || []) - if (requestedTier && (requestedTier === OpenAiServiceTier.Default || allowedTierNames.has(requestedTier))) { - requestBody[SERVICE_TIER_KEY] = requestedTier + const serviceTier = this.getAllowedServiceTier(this.options.openAiNativeServiceTier, allowedTierNames) + if (serviceTier) { + requestBody[SERVICE_TIER_KEY] = serviceTier } // Add reasoning if supported diff --git a/webview-ui/src/components/settings/ModelInfoView.tsx b/webview-ui/src/components/settings/ModelInfoView.tsx index bbf9fa39b2..6c82c0a632 100644 --- a/webview-ui/src/components/settings/ModelInfoView.tsx +++ b/webview-ui/src/components/settings/ModelInfoView.tsx @@ -17,6 +17,26 @@ type ModelInfoViewProps = { hidePricing?: boolean } +type TierPricingRowProps = { + tier: OpenAiServiceTier + label: string + modelInfo?: ModelInfo +} + +const TierPricingRow = ({ tier, label, modelInfo }: TierPricingRowProps) => { + const tierInfo = modelInfo?.tiers?.find(({ name }) => name === tier) + const fmt = (price?: number) => (typeof price === "number" ? formatPrice(price) : "—") + + return ( + + {label} + {fmt(tierInfo?.inputPrice ?? modelInfo?.inputPrice)} + {fmt(tierInfo?.outputPrice ?? modelInfo?.outputPrice)} + {fmt(tierInfo?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice)} + + ) +} + export const ModelInfoView = ({ apiProvider, selectedModelId, @@ -33,7 +53,7 @@ export const ModelInfoView = ({ ?.filter((t) => t.name === OpenAiServiceTier.Flex || t.name === OpenAiServiceTier.Priority) ?.map((t) => t.name) ?? [] const shouldShowTierPricingTable = apiProvider === providerIdentifiers.openaiNative && allowedTierNames.length > 0 - const fmt = (n?: number) => (typeof n === "number" ? `${formatPrice(n)}` : "—") + const fmt = (n?: number) => (typeof n === "number" ? formatPrice(n) : "—") const baseInfoItems = [ typeof modelInfo?.contextWindow === "number" && modelInfo.contextWindow > 0 && ( @@ -147,50 +167,18 @@ export const ModelInfoView = ({ {fmt(modelInfo?.cacheReadsPrice)} {allowedTierNames.includes(OpenAiServiceTier.Flex) && ( - - {t("settings:serviceTier.flex")} - - {fmt( - modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Flex) - ?.inputPrice ?? modelInfo?.inputPrice, - )} - - - {fmt( - modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Flex) - ?.outputPrice ?? modelInfo?.outputPrice, - )} - - - {fmt( - modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Flex) - ?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice, - )} - - + )} {allowedTierNames.includes(OpenAiServiceTier.Priority) && ( - - {t("settings:serviceTier.priority")} - - {fmt( - modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Priority) - ?.inputPrice ?? modelInfo?.inputPrice, - )} - - - {fmt( - modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Priority) - ?.outputPrice ?? modelInfo?.outputPrice, - )} - - - {fmt( - modelInfo?.tiers?.find((t) => t.name === OpenAiServiceTier.Priority) - ?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice, - )} - - + )} From 5f0179c9941663cb34e2b1224984800e34fa6c12 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Wed, 29 Jul 2026 18:21:25 +0300 Subject: [PATCH 10/14] refactor(api): address service tier review feedback --- packages/types/src/model.ts | 14 +-- .../providers/__tests__/openai-native.spec.ts | 97 +++++++++++-------- src/api/providers/openai-native.ts | 14 ++- .../src/components/settings/ModelInfoView.tsx | 4 +- 4 files changed, 69 insertions(+), 60 deletions(-) diff --git a/packages/types/src/model.ts b/packages/types/src/model.ts index 2936d76b69..28a32eecb4 100644 --- a/packages/types/src/model.ts +++ b/packages/types/src/model.ts @@ -60,15 +60,15 @@ export const SERVICE_TIER_KEY = "service_tier" /** * Service tiers for the public OpenAI Responses API. */ -export enum OpenAiServiceTier { - Default = "default", - Flex = "flex", - Priority = "priority", -} +export const OpenAiServiceTier = { + Default: "default", + Flex: "flex", + Priority: "priority", +} as const -export const serviceTiers = Object.values(OpenAiServiceTier) as [`${OpenAiServiceTier}`, ...`${OpenAiServiceTier}`[]] +export const serviceTiers = [OpenAiServiceTier.Default, OpenAiServiceTier.Flex, OpenAiServiceTier.Priority] as const export const serviceTierSchema = z.enum(serviceTiers) -export type ServiceTier = `${OpenAiServiceTier}` +export type ServiceTier = z.infer /** * ModelParameter diff --git a/src/api/providers/__tests__/openai-native.spec.ts b/src/api/providers/__tests__/openai-native.spec.ts index 29f90741ac..cba0779191 100644 --- a/src/api/providers/__tests__/openai-native.spec.ts +++ b/src/api/providers/__tests__/openai-native.spec.ts @@ -196,6 +196,60 @@ describe("OpenAiNativeHandler", () => { }, ) + it.each([ + { + name: "an explicitly selected default tier", + modelId: "gpt-5.4" as const, + requestedTier: OpenAiServiceTier.Default, + resolvedTier: undefined, + expectedCost: 0.22, + }, + { + name: "no selected service tier", + modelId: "gpt-5.4" as const, + requestedTier: undefined, + resolvedTier: undefined, + expectedCost: 0.22, + }, + { + name: "a resolved service tier without a pricing entry", + modelId: "gpt-5.6-luna" as const, + requestedTier: OpenAiServiceTier.Default, + resolvedTier: OpenAiServiceTier.Priority, + expectedCost: 0.088, + }, + ])("retains standard pricing for $name", async ({ modelId, requestedTier, resolvedTier, expectedCost }) => { + mockResponsesCreate.mockResolvedValue({ + async *[Symbol.asyncIterator]() { + yield { + type: "response.done", + response: { + ...(resolvedTier ? { [SERVICE_TIER_KEY]: resolvedTier } : {}), + usage: { + input_tokens: 100_000, + output_tokens: 1_000, + cache_read_input_tokens: 20_000, + }, + }, + } + }, + }) + handler = new OpenAiNativeHandler({ + ...mockOptions, + apiModelId: modelId, + openAiNativeServiceTier: requestedTier, + }) + + const chunks = [] + for await (const chunk of handler.createMessage(systemPrompt, messages)) { + chunks.push(chunk) + } + + const usageChunk = chunks.find((chunk) => chunk.type === "usage") + expect(usageChunk).toBeDefined() + expect(usageChunk?.totalCost).toBeCloseTo(expectedCost, 6) + }) + it.each(serviceTierPricingCases)( "requests $requestedTier but prices manual SSE fallback usage using OpenAI's resolved $resolvedTier tier", async ({ requestedTier, resolvedTier, expectedCost }) => { @@ -550,49 +604,6 @@ describe("OpenAiNativeHandler", () => { ]) }) - it("should retain standard pricing for an explicitly selected default tier", () => { - const defaultTierHandler = new OpenAiNativeHandler({ - ...mockOptions, - apiModelId: "gpt-5.4", - openAiNativeServiceTier: OpenAiServiceTier.Default, - }) - const model = defaultTierHandler.getModel() - const normalizeUsage = Reflect.get(defaultTierHandler, "normalizeUsage") - - const result = Reflect.apply(normalizeUsage, defaultTierHandler, [ - { - input_tokens: 100_000, - output_tokens: 1_000, - cache_read_input_tokens: 20_000, - }, - model, - ]) as { totalCost: number } - - expect(result.totalCost).toBeCloseTo(0.22, 6) - }) - - it("should retain standard pricing when no service tier is selected", () => { - const model = handler.getModel() - const applyServiceTierPricing = Reflect.get(handler, "applyServiceTierPricing") - - const result = Reflect.apply(applyServiceTierPricing, handler, [model.info]) - - expect(result).toBe(model.info) - }) - - it("should retain standard pricing when the resolved service tier has no pricing entry", () => { - const model = handler.getModel() - const infoWithoutTiers = { ...model.info, tiers: undefined } - const applyServiceTierPricing = Reflect.get(handler, "applyServiceTierPricing") - - const result = Reflect.apply(applyServiceTierPricing, handler, [ - infoWithoutTiers, - OpenAiServiceTier.Priority, - ]) - - expect(result).toBe(infoWithoutTiers) - }) - it("should return GPT-5.3 Chat model info when selected", () => { const chatHandler = new OpenAiNativeHandler({ ...mockOptions, diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index 19d529acf9..8dffe03dcc 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -336,8 +336,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } // Validate requested tier against model support; if not supported, omit. - const allowedTierNames = new Set(model.info.tiers?.map((t) => t.name).filter(Boolean) || []) - const serviceTier = this.getAllowedServiceTier(this.options.openAiNativeServiceTier, allowedTierNames) + const serviceTier = this.getAllowedServiceTier(model) // Decide whether to enable extended prompt cache retention for this request const promptCacheRetention = this.getPromptCacheRetention(model) @@ -1428,10 +1427,10 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } } - private getAllowedServiceTier( - requestedTier: ServiceTier | undefined, - allowedTierNames: ReadonlySet, - ): ServiceTier | undefined { + private getAllowedServiceTier(model: OpenAiNativeModel): ServiceTier | undefined { + const requestedTier = this.options.openAiNativeServiceTier + const allowedTierNames = new Set(model.info.tiers?.map(({ name }) => name).filter(Boolean)) + return requestedTier === OpenAiServiceTier.Default || (requestedTier && allowedTierNames.has(requestedTier)) ? requestedTier : undefined @@ -1514,8 +1513,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio } // Include service tier if selected and supported - const allowedTierNames = new Set(model.info.tiers?.map((t) => t.name).filter(Boolean) || []) - const serviceTier = this.getAllowedServiceTier(this.options.openAiNativeServiceTier, allowedTierNames) + const serviceTier = this.getAllowedServiceTier(model) if (serviceTier) { requestBody[SERVICE_TIER_KEY] = serviceTier } diff --git a/webview-ui/src/components/settings/ModelInfoView.tsx b/webview-ui/src/components/settings/ModelInfoView.tsx index 6c82c0a632..ba24fe3fbd 100644 --- a/webview-ui/src/components/settings/ModelInfoView.tsx +++ b/webview-ui/src/components/settings/ModelInfoView.tsx @@ -1,6 +1,6 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { OpenAiServiceTier, providerIdentifiers, type ModelInfo } from "@roo-code/types" +import { OpenAiServiceTier, providerIdentifiers, type ModelInfo, type ServiceTier } from "@roo-code/types" import { formatPrice } from "@src/utils/formatPrice" import { cn } from "@src/lib/utils" @@ -18,7 +18,7 @@ type ModelInfoViewProps = { } type TierPricingRowProps = { - tier: OpenAiServiceTier + tier: ServiceTier label: string modelInfo?: ModelInfo } From a92aed6673bd015d56609519b6c802d39f114c11 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Thu, 30 Jul 2026 13:02:21 +0300 Subject: [PATCH 11/14] test(webview): add model info visual snapshot --- .../ModelInfoView.visual.fixture.tsx | 67 ++++++++++++++++++ .../__tests__/ModelInfoView.visual.tsx | 15 ++++ .../model-info-service-tier-pricing-dark.png | Bin 0 -> 16915 bytes 3 files changed, 82 insertions(+) create mode 100644 webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx create mode 100644 webview-ui/src/components/settings/__tests__/ModelInfoView.visual.tsx create mode 100644 webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png diff --git a/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx b/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx new file mode 100644 index 0000000000..9463222c94 --- /dev/null +++ b/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx @@ -0,0 +1,67 @@ +import React from "react" + +import { OpenAiServiceTier, providerIdentifiers, type ModelInfo } from "@roo-code/types" + +import { TranslationContext } from "../../../i18n/TranslationContext" +import { ModelInfoView } from "../ModelInfoView" + +const modelInfo: ModelInfo = { + contextWindow: 400_000, + maxTokens: 128_000, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 2, + outputPrice: 8, + cacheReadsPrice: 0.5, + tiers: [ + { + name: OpenAiServiceTier.Flex, + contextWindow: 400_000, + inputPrice: 1, + outputPrice: 4, + cacheReadsPrice: 0.25, + }, + { + name: OpenAiServiceTier.Priority, + contextWindow: 400_000, + inputPrice: 3.5, + outputPrice: 14, + cacheReadsPrice: 0.875, + }, + ], +} + +const translations: Record = { + "settings:modelInfo.contextWindow": "Context window:", + "settings:modelInfo.maxOutput": "Max output", + "settings:modelInfo.supportsImages": "Supports images", + "settings:modelInfo.noImages": "Does not support images", + "settings:modelInfo.supportsPromptCache": "Supports prompt caching", + "settings:modelInfo.noPromptCache": "Does not support prompt caching", + "settings:serviceTier.pricingTableTitle": "Service tier pricing (per 1M tokens)", + "settings:serviceTier.columns.tier": "Tier", + "settings:serviceTier.columns.input": "Input", + "settings:serviceTier.columns.output": "Output", + "settings:serviceTier.columns.cacheReads": "Cache reads", + "settings:serviceTier.standard": "Standard", + "settings:serviceTier.flex": "Flex", + "settings:serviceTier.priority": "Priority", +} + +export const ModelInfoViewFixture = () => ( + translations[key] ?? key, + i18n: null as unknown as typeof import("../../../i18n/setup").default, + }}> +
+ {}} + /> +
+
+) diff --git a/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.tsx b/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.tsx new file mode 100644 index 0000000000..0b5074e761 --- /dev/null +++ b/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.tsx @@ -0,0 +1,15 @@ +import React from "react" + +import { expect, test } from "../../../../playwright/coverage-fixture" +import { ModelInfoViewFixture } from "./ModelInfoView.visual.fixture" + +test("renders OpenAI service tier pricing in the VS Code dark theme", async ({ mount }) => { + const component = await mount() + + await component.evaluate(async () => { + await document.fonts.ready + await new Promise((resolve) => requestAnimationFrame(() => resolve())) + }) + + await expect(component).toHaveScreenshot("model-info-service-tier-pricing-dark.png") +}) diff --git a/webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png b/webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..624ce5eb2e4545b849fbb8428c36582e58fe1b52 GIT binary patch literal 16915 zcmZ|1WmuN$)-H^+bV_%3cQ+_01_%gBN|#7?i;~g}5(-F3BdK&FQqn110#bXh*8A>b z|Md##zG%ZA}$y3~~$v1O#j~RmF!02zNH%>lAcE__vLO^jic3JOnjG zxkv8l+ZlJ8G#*p@sAa3_F5y>B35%;xOrfW*DibrKR=AsDelN}gJ%`ckjIpZUbS;K2 zm^rxfwB@4oWM?nEYDns|&NX^&{dB8lNZjj7^WW*gqiNrOcVnq)?vv7IewPPbA0H$k$}noCixRUxnrwW-^MX0%%GmGfWZw5; zhuTT^>fedPn_aBe_VeG2?q_=K*55%w&A(8a+Th? ztPdW<&ih4EOQj0FI$VyHQT|kxz@~eT=ehTE^{Xe0cWfO0D1_ml_@WDrS6OOtkRyz8 z>Q}BjHTE&TAIGdQ{ps=N(>X7Po9nA6lOOLWYE8Rv>KoouNpL@JI*Zqmj=*R5vA@{y z`B5>y-}Pmq*sxznq7*r+LCtm)nE+>q{6RLYnah2oe;uU;wcLhv zCR07`eTiJ3pMJ@tQwYtFc$1rJ))Tjy@n$XnUv>1s2lnroUGJ$lo|KTLP`T@r{E{u9etNZiAZs@q1H93?(r$e4fV}@NCjImq*9_Wk!v4Y=)e2 zsWS<*a)|s+Zh11Xo{Y$J&dqE|awMU#v*oU6fx!g>cfz)?u?p<@k+r>V7w<6(3FP0t zIgMQ;Sg&)lq+@jdcl_!omQ)$H>R{3|ny!+fC(vi|$Q}o-2zr9A#d2xw9KCRrTnUr4 zbKk$yJzSKhpC0wp%bZL1Yp$1T5ao|;{Ivhw;IyM?kFm*-%Z8tTLsv5rmPpk+2bUCX z_e7bIS5F+%Wg1+p@6C$par_%Y8ph0G8Im6So+3oN7X74x*!;S^J(cT&8GXp)>ECP1 z7k6fA*%*I@kW6YOazbIaOx8(^z~FdV#z?X(TWMO*t{S{}Ow92_uV$l7hjNRqUA_RB zST&`D&@-?r;vOE_C+_=!DiSg`mo9Pk=ssfw8oUxrQ3&WVX7lM*P!qj5yyOqw-s8$m z|8ZLsqPVPZBaus24RVQ8+J492muU38BJMzWboQm?dU`p(dgtd;8#-gbekl2DvEZxGecT8>LIu1BIoh)iI@c_F94MR_cTUdU z(=e{C_#EP6gsycTa;7nuShA@o(6XF}-ZKiJ5$NQi=`k~B9IPKUh!vXiKGNWzZ!N5M zS!YI@t+AshWM;FV7(DUa2oX-kPR_t~{wd}4H<_ycG3`h)nT+iC3$27ZqaOKpHTf(D zDa*(wLVLx&Y_GXke0eoifZjW&^-0z(jtvt}(CpCPxo9k*O2Z~2`n}@cR&4^_+K8ye%{v~6lO}KViYAgK(!0j9j5Dy!e>xE6GKsx z3L7uckGJb_5kvkOK)cv)CSWvFCR$4?(AQ-e5FhhrF$`2^79 zAI=f;^PmaD$tiGbBZ=kyL{^>M{@cs(C0{dRx{_uoQP8lg2-@$%!Tl`kk<$pK>L=8T z3wmbu+`&h^I^H2f3r-lFvlauXLbU(7Vuwm3BN?7;)pLZE3o-h#*{pg z^5}hdl$C;*P?Z55TEhp4HobsxQq9`!{tV7Eir(u^lUtj*z^AgoVk z7z57`38a1qHM|y*!>_bx3iJ((d>Q%@svR|*x3j~qGNX-O?mlq z@4|S3)toXSFV>SK;|IksgScJPk&vz2o3W6ghxmk6zT|mx>LjoAIBSiF{j4fh%po>~ zuF9{Z*iS#M+Md5(R7W zn~zs=MqNmOEsoj6?>F-wWv0l=jBD+wLd=T26`zN12l)14tCq=JY**FT&5Yi3AIuV= zaz_=h7ILh3VkbYg_>p{9eY({anNd@_*7lj-&DALvNle<9`!o5l;i$!qFq?)_?OEMU z$5i!L!Gm~#BX`sa+cS@#4pYlQRP3$TKosYat^ggxKf(9Yc?8LMhsYKTl3KelV-br5 zUH5!wUkdgVZxRYd0>mLOy2VPXns|5Bxz(xPjZ&#vtD`xiU13#WZO~V`r@mk#iu8&i z{eyEB9mI!0=0v+vq|Hi{IQCibKx+{itFro2R-#Ds9J1P5s7E?J@i<4DRUYg9EI>Z| zm*~Ih#`4t&Ii7Gvi|oGadY=JpdLCX!&Xe=#3N9g9Y4f{fqw&E>kxAWv^1 z*G#R$JRA|vm}x0_yu{g9apC7p?p%OnCM%YqHm$Vn--5-h2`;ClP5^YQkBWvvu_;zw zbe5O%x;@Acf4$oK0h5Hg$>Yc(x@()>=A8yQrJ$8|3cvZ`N{=o;j@Mfg9(7o#FHlTP#v~-V zeQwtP@W_yV1IPh{`B6QkQm^7=;?CN^pU+=vtVVKGqNz2~gumMQo)&3vmP@?;14xCz z`H21Tr!$@}P|%_-p-orY{CdB?a1a-JQfn^k(eM%W;tp+k9lk=1!@Opu)%`^$g2|)x zp+1$-=1eKC!4!e8*(T+8+eUviL^O!Vzyw-E({ zD8|k#@_Dmo1)ux=f_e&nTRsvRPPOfrrD~Pa@()VP#Y9eh{(VzIru&bIA2NBonMcsP z><(3XwcHsY;JTsh!4um~$f6k@aCa4UDZW;DGqK8Y$aDV{EFY{M`&;5!*oHAJnGYnV zzkBac9IXw++dW*f%<`Go>^bRXhz@UA2*+oL`ud>R^W;Y~jf~Gf*SfimouNvf3sNj5 z)fid!^;Z2V3nz&>xBHc5J@R3M4h4XA;I2B6BSg`OTEhkqGOM#b{*(Z#B7e_MLzCP1 z8zlyB@Zn(}kKg%f(%L}tujgJn06SS?x93}A%rY2MaQfUt14?N8E}hKo1^*7gBoW_I ziVxA#c}6a5i>_boFi-uw$~H@^L(w+8J|m1tB;iq+p%^3iAo>NZQ}_oJ+AabvQVvnO ze1zUgyr{@p88a2397|xEe`5g_2;8KceH@ex;8~*(C zcqANPIezNd+q#@+z})xk`?OL^9O2|zuoS%jN&pN)%Hxo+hfOTQd`7kHQw&ZNuk9o+ zu8*+sH*d?q^njBElF@TO77gwPOZM;fYgVLR{~?;365M* zlIp_u!wHPKTlGx(R2*zh>b=8v0OEdC^QI&;8oD(9!Q){Te!<{$H{#CGPI8@@ii6WYx5;bP=#3Lvu zkD*-l5*R056`|7i0GTUPPoeiMAN&NDA!vaXQ!LIWz9+IbYVu*VmCldr{UlN?Oi9jq zu~=1(&9Oq3F9}xo8Cnso|EmRbS5$w3+O(4MMA5|k&d<28QD^pq^L~ext3)eCa>0E4 zdS`Qe?lAv@et{u9IepWrfJ$}jYK)(mFCxRXipTh8oKIz0PLxx_yp6pNz7sIr@6J@z zzw3<0$ABb5qIqn-(pmgwZ%$*dtZWKHX=CYTGI3F0Dm_d^pgM5nmPCaIIW_3xVr96( zS)ZTrPg*2Du)h~0%zqbEjDKDEiL~htEDsMepl^@9v!UYx`Mf8LOr1DOA#CfPHkrsG z=(atHzfggESJ0l^f;!^hZXiZ}f2brHVhu1yZ(6C=1rw-vc!rl}2RiFaG?wIe4-?Pu z;Z`Q6PRR>e{ZUNJagP4su4i#^Dr=ehdN^C|cjFtDs8ZvWOrp$5HS$3_atXJRF#0_w zmxwQ_+=}1K`w~4q11Y0_gjduYsO|&t5VgW+4E-cQgU%u)-L&f z)b%+xrlls$RZ{-!8t%N;3xYk8wU%z-2|~inXr`WC84`=t{qGad#E*LJXbO*dS*Mh0 z2aCT%tD_;hH;X&B{u0Ihu?J^9-fYF2q3Oh{Dm zB43AxSJucMAkd2cbL#L>c~5zel|Ex6Q97f5E9H85<^%)*S{*C>VBPZfx1cHP83E3MO@eN$&3gSxai8){u407A4sg6( z5O=EKhsfh&FYqjDrV4Jh1|XeoPc08j=P!!6ta<;Pvdl%Lx{^!Dg z$o%Ema&!`>e!JVJN5$P;BDRwz_vq_nXiXT^lX>x$EaZv5c0q5CL_=BB`e4}Pj!)bO z6$%Z9^6a3CM!>vRu}sQtS}^+cJT|$I^)b+9!#c-+9XOAbGg>lN$Lm>sw>|Y~nIV4F zCLoITJ@}=;IHo>{y{03e?#CcP0j*Q=#ZPO+AZAa1#tQX0{WOH+Ijx7t_)r&sLg#zq z$$2W#y@!0z)Jy^s)MZS6MAbX5vKxPs+*#R66|&i#tseuC2=41+Xvq4NW>ylKjd56H z{Kvb$y{0P7AKv9!i2~GO9;Abk4rKBlRwp)_E2ywEBk0wiyJeifKbx zGV}1mK-!7?X|1QHmnLk-{nhPG1nZ%L2CVcxN4~Grj zzUNOWPz^aFx%r#FGY+SF^I8LnnD6CnbG~A!y!?TcMNy=ahq_MBDK`Lpw-|10<-_lh z#8-mKaJF};Z_?6(8{s*zE@TaS^Ai&Gu|lo*G==P!nJk}$;>bJVUUuF$+1E+(cNq7JiGqXra>l@js&qRU4nYjsDO@|rlW|>D8t-FY0%pAKKdJxaY*KA^&1Wmm zGh**pOL$7rh@t#?dDSe+7=H)KS3Su)9abU?5yk1x!BNB+Nw37ulcA2VWO+YiKA4>d zW=tss6|!hl$Yw&Y5BtJU&l#{KJ)uF3Z9A%N!Q22X${MTwMqJ7d>z|)0oHmAY(DmnV zXP^9Hw6S;;a6F`+{7&%m=ATt7bS zGXVIbH63BNeJ+u5t#+?9{#YW`Xy?dp`hC^lcQA0HKi`hEst0<_d zb#!MTM8oPPcIbV}P9o7lbUIiKZaa~#IQa3NX|6wS+T#-YUVfnF-BRzpe&l=VFy;QG zOux?Y#WU6+%(d>=JgB5m6#WsCG)CRwXF4;tFSV}>FBOz!=bqH4LD#?~yZd7hzq01! z#^w=fr62A7b0flu-O-Pjk=P?lTA7kjyy;Q>*VH9cA^%x^huNc#eX6Zc{LJQh1Xbgi zu_ONY_|EfNF+NC)%0a>q)r|!StqKbNQdUrzSTP(BI#i=ODsW8yFB=^G*mv7~I(3DrJQi=iA51w+wl3^*n=Rxsfk(95#C57nZ19u}yxasLD{ z?dXgh`T+O?_Hg9@`O5s;$ZL4L_3V`DJD3Cj{)yi?ML+8%y_GWNR`EQcz(adE;Ay@6 z88RUkv+{h?UqK9PLBPEpHMhr(aVD4q*?-J;aM$hm&F75Q+mo^4AoIT_Z(W6peh94b z(UL74*BdtzJv~y1U!d?%xdDcmq4wO!Vb(~!Z7k2eS{p8auD)XDORn|;PTzwBRyhXP zfdLQjFS@w$ra+#JrVuHcOiF_81>_2J=seS&fe8L#+xLFBDl!)tDHJt6l zj5P(>7<@_*V2&WGOT#@&=o>YMYMJW|8oOYq6qi9m?8iQ4urz>N<$i}{n~bkY``P}i z);>eb#bUB|>RBzSw&ufhK%(8L_(54mz1EiKD&^E|sJ%ggVr=V`w(k(>nd5$bG4_Ma z_EyT>8Iia20*z6(+(eE$*wZZFKE^9z(ib;;h8~`$__o^WakMrfiF0g~L#QpYxCkag`kS-;Ma+Ag z*)chHbCxQxm9Yr$Hy5Gwjj~&ZhQF|0wm9iw$x@96D9yO_Sxk=ZqN)Qrwx%ef=*KEP zsJ5Z_)!u+HZ?{1Ik|UAZ#m0ZkS2*VApW#mbg?!OG=e@-MpzdX$zIO3FvX^V~22k7bs8K@o5L^{#Er?>{86 z8L;&6CqAAe%BuHJQ4;QPV|CK30BiVOS3r&XnmOkvH^5>1SEjs zm8J*>f+?H8p0d@I8T-Dn@I;&TAzS=-CVaO-gze$ z{h<8PuPkn0RgLN2I#A6RkBlecEO4P4K0fHp6B|@!D*TYZE>+5;6@ihc;4zoMl8QkR zIzF=Yc?1U(cr)t&eGE;@|F9ppys-bXc-9P0M#5#ZHsMb@xJ4+zC#^V{UFMO7rubYHrgfR~~fwdVij71y26 z9)wz2sBF11uABT&S<(46Fhu_&c>NE8?lA78`g%RBvxV)Wl~G^{re=x0z&eHC>BcrV z3W8szRp^r0d3mHNcAD{w;A+R#G|!h|ApMgZQ#*(9d9gF7;KJy?24w@ zlZs#5ejx&@@V>7$m&ot>+!8F%TTPh+-H(#D_v-8397O}{E*vTe*NvFh(HRAx$4!Bq zC?lcRGo?0T+8K@?5Qw4?nY#t5NkkGNH_UYa0-PbK^33WS%wuJZD7;Dxf{poOJK`!tweSP2|!#xdS) z@m36lI;?Pfcrg@1rvQ(mhGguy`4KL3U!WzWCjAiIywBXqupQic@mov*8ZoqOut;nz zZab-BjfSGB3KcKE7unsiQTr+BUr^ZlV;RtrY5)&`X4h@7tx3f8Q1SB9Wi@-uobZ}< z5q8u&+FjqPe<@Y7-+a=l9J`bGO!JYcTU2^Q0Ip~INA}}IBd{qKe|IPZEYvHNl#d;Cq1K%D)#$uJNsmp({}&U`QjD#J zM(kYg@vQrR{ao$IG+@ul=UzYC%_0AuiIN6^!iPB_z zRiFnxF) zuoH9I+Ich$?4tXQ+#khH1GA3lj!>t5EE}nRsiU9?k02ewoHpNr){SlO+ zce>d|-70IufQW3aT1sosX45O1=P6uWdfn))vTXjkmOWql&92H7GlzrRDJ&QRN`Ks^ zJX#dzIvO5lvV`QVLzg}E^>Vt??*l9uR*ROxdO6yP;gc)U8%Sna6Z6;8KwZpT)#s3X z1P+BTN+`NpR2$U1npqAcDL;}u={PLvSt(`3dcs)YwlfV*?=6AU-|!oz3f8TOA!-%Lf*6(7c7JsaJ znBnzaCkvHhw0726H+n~B z{99KNkNju3jK=MLoH7&zRa_AuJ4Ww+e&tf$toa?`j(s3$49g8WR2sQXYm6apUnO;J}i~ zg*pimoljY2#nyUKqEl2VTwk82{NfMU`t_wyc-(UI#UC;WH{0M)zyEnIkTjQEnr(h@ zTWD*T98G;f@RGpeJ*JDKO}Loqn(DZ2?k2;=V^DcV{@u#IX9#^SBOpyn+-Q-{7MfLT z`;B_R?B&;UANrBGk5xrlU#xXBFZ=83Q}i3ppLn&l=+$~GqXln zfRfrwB`WDD>d12lA6KPWs3p!$84>8g48R>Z;O6}Hf*o~y^P)cJNyCpTPD^=h#-+v} z)UpnXh>LhoFAZ|D1k|||NEsZ=e`4L-X^O!3T^dPmk{T=@Q{+kYxvonI;0VJWb^p-<}vNG&Wlo~(= zM*K;M-b7F!=m4NPj5M{KY0-h+2L?^tdSj3R6hBy12Q2k#{Os*9)LN1R0FL>1^^fNo zUvI57ledCk0R$=)0y3b3wDLpzg26FgE#3nB2@rNRCM)uYUzdjW)CfTtU36o} zoA3jR1SPEA^Mu{_9VuVCDDyvLuZbd^Whh#p>3_{FVmh`2?v^|d^wr^a!&lHcUUY<_ zRCGH)>cC~S_XM;dQRBvzq3j3H@YHDL8Xu<-NBZH?tIQhI+7m}NK!Bx<*)hPp#jtCB zuGy%!IvwE^4z;4L=FgTPx{MZZ4HgR$x2EtGy?1xr0x78P7Hjwd3-~QnC z@NnrZ&qFP!U0hyvr7DmBgKSfx2=0_!tN$I?NDjlgeIO^Gt#zU@0f+%MDn|4+kl0pT3|omJ2`d` zFF3L4IU16|wE=)b_OF0a-cnD2j%y0kFpwc3LlzY*PcYC0_&`lK12PNKUu|3#cv>zi zeXl&r+M5_Uo+6`8H^DE1#G~o~WUyIIZ;2gVd>@`({GuMrmukNsQDoaS&?rFha-I`^ z{RdC!HUb8#!TqedyHbTBJz-zDB}3XLH9m6HHWZ6&!xCrFExbP4-%m{N8z6^cg5RoN zeV-42sX7C`!~w@>Zq;23 zWYb4VjI^hWY}`*uCbzz{Xo^`DrcKOt zHBOi}opz|AE6!p7!DKuY4vqA!8kox|*R6n7i1dnD2icS9uOnA_l9illqvIlCY{aL@ zQn()JZxivP>y`O73u0{@dCbJ9*FD|04FP%qrzTt}%G%$j)p*BBbb2SYuMX*s$Gus( zK<50T<1wxHOa<}79WWTz<`ozm9Wexrmymln#p=!v6uyFr7^t9TL57yiTgO%7ywZ(- zjQ3E9R6|`p+jj$(-fi0QE!x{Gw7KtgO+fA`nNGs}2RYpVraBCVD@kRB6i>B^E}btVM7#e3X_szurC^zc`{HVgJ&#{;k=ODe;F-42CjJv5;h=D=pm6d+8VGo$RyV}P z(CS3*s89)D;ogiATkZ9(nSKZkyW*1L6V^qQ>o}Z_%)@*P-ES6IP(|GdYhCkBPBZLm z749=449h4otIpTij!|#AnRc-pYgCFxw5+;n#=ygQz%8Slj=YKT^c)0ukB1 z&q8^NItX8?GkE;{lI_%IZ++ody6=%+2rhe6{(cB(F{g0BTt-Jzvk&il zG4=c@^@+Qr1S>W##Zsdk&Z&OTtGllHIr1pt*e{IkPuJK%wbo}2C`Rrzm1THas}^d1 z_4{aP(0U5B6pPAi5#52er@wZC6kqzIseQ`07V3^SA{uRod4F-t=LF$-0 z!T!m?!Aj#jkUT}i$&Xfdcp~+fl?b2yPehuU{Afdxv2RKi{kZ>Sq;n*-^>3b-Q@WEl zG5K%aM2U39p={Zt)q7B^Q!_%MNPou{=JD#eb-(b8v4|IFw~3+{_7lVKY5ga$w`L&d z!$_-(`xJw?T^F5MIa!$^z^-RDh0WYE(KU^=J(KYhlzll4(9iu19xT|9;+^C{o{yg* zg8R-%o=$nRd81fd?R`-q$B(b=^xU}BpJ?)LDbGeds>>yL?jCRz`zuz?d9D8(1n-L! zmX+~sEFpn9xq>j4diu3zcjH+FR(>Bx==&%5p&E*|A=RT2e<%Hhw1mC>Qr6$~eVrod z*lS{rC-V%sv#5P&SRV`pG1gOeGFuL5eY$g>w$Oh{&iOId(Au+Y1JTY4#yE%bm}02ny&O3L?=tan9&5O%H(&?w2dF(VX&+-- zXszEEv6?TMMAl5*$w_qgj|cvA}nNu5!A}#v&|~RvoSt$uAqHR zdY7D6=)AHizoWPr`x%YL3V!Y5H}AY{WiMi>vZ)muVhRaZXOtb*qs&bS=`xrQR^eK82|WcI9^0z%w1&hXT7X?@QHhxp+cQ~$&L|{ zL?7YnxdE5rR^y{4xvSWhcD4V0Q#SnG{(dv%M~$JR${;yYNlnx$r}`N#Mr=xjA71&- zZ_vlLr}F6i<#WQF|9@;ibiSqc#2krd!Lw*!hz&=ghBFTU78^nH!+!&*>)Ug+4f*NA zZiXk1@*s7+G)sgGd5L63HG(5PXx5O4O6`&^zYWU1YI4!SH3O*%g#5Wx!#xOq>`-Ar z66k7BV$XeVh|Maf33-q3ctv1Ha#?D1>j^cT=gA-9Gks*}*FODs)_#scxi2k-ik z8aQ5X0^@*bL;Vr78up*BuRE1Q#Z)CQbE865V?R^_b^yMpebjHx%|*aBI?I6?@b7B1jJTv|69C@v7N%9 zY2?#m4!t_~Lx=5!2`wOXK>lKlbE(_9U$djVUEFOuS^Twi!$(k3z|Q*XzOi4wo=L%L zA`ANS#t%`aB~nX*TIZYYBp!%?{SNm4By5m}9c`CHw9cTGnv+?Zd zXd>dQM>d~8Y8tu9%l*zou`ZftNZwRvHo#3ToL`PUfJ@m}*kelHfDr=HDS#F(zzcmm zE&bBom$0k<{_Z@G^gkOdWV?LZ^S@ev7H&?}4S)}@Ak{nopsf$2S!ybx=crMFdg4oP z4xnfUa#n!I>{qY9B<0P4QUhsfyuT1V`U%P_IIo*P5F*NjE8fR#ofPqg-@c($xZ@IY zh@e9%xTiVr5i9xoBxLq*RYA*ak51nv$ovG&7qUKJrb>iAq*MXw#q0C$7`{pOz3kc7 zNCa9Qj&`39?2qX)jGNpm_%!dw(knqKJ(hE!7oK(~Ln5gi{8ircu!No$IYt}Buwdy| zkc59^Pu}Zq0$Y#A!HH!QXCE(c4aTn5-)%9Uf7ROxnyFwZMvLI8J%vmb*G#$-+?Z|@ zh;gx88G=LDB#ij0O0J{E{&(b`N|$xbKM6^FGG_}&9&OQMP7ghvhFpT(T}6$E8S8IP zWMJo(CsK!zJW@J)_!h)07arBbyONaB-e-et_$bj^a)2Cw&(*sR; z*6jJZ^NzIq!x-isDsQwmG4Acg-36*YA(Ca5pux{QRc3x6+Tq{b22%(O0ZQ+)@j`N- z`cZ1mg&}Z-BxX>#aNf0r+`m{x_~2avMpmPSV-N_@N%8IjZoosE^V}+hBwo|m(%Vo< zcrUGQ>XHjl+J8VWu-*vi z*!7Y=Mw{Bz%E_&&asNC~IhVnsyS&G9fhd@yk|QkFX@y`A;VgDu!Dxp@rX-ot$<^t+3N@2%&367F@Sqr4#zEng zpU~U8F_#ondoraOixF``wtCzag$@>&KN!?G(iwIPt}OqE4kV;&iV=$cmm%tesAkj~ zJHDvmgfEwV%Vy5Ep~O$|Di`O^P(BB*g1}FI{rmF$a2m#6pkBPW7uQWFmj1=+2K1+; zPrb&LVlxkYVZCFc%(?9y9BL=v!~P70-{Clt3 zOJ`+BJ(AIA>Vp9Qs0z@RXe7QDps6pc6xMe9@3QmtxD9+_O1$z~vutsgm#$gVK6{#Z z&a`YHkNW}Xy40|~^5#BJ#cOa3&?|DI9*_?P@S?Nv26wZmGWt+s<|t(J)5uS0HxW6b zvoQubKnr?7gX~(VqehBfpym~ZO#vE2MVM_joz=!+^$FxLtbdhZkLc1p#n^m1&qjg8 zlP%WgdyT7{*tJ}A52Zylmfl-QH7H}zruRHiex&Ucbq4JPLuPkseRcZtxK-zvO`Ufj zq;truUd!Qqf?UFBhy|W1`;!t@t-iXS7O8S9;C|}=qihTFK(tr+pQBarq5^EdY)M&b zb!$GlP%N$?CmI>w^ppl*p}7>$D}&io@{!Pq*b~ynu_21WY)KqexKalXzOxFNjD4T4auI|BU}`Mb>8$edpDPDAeK4n$KxHkHf`X0JX-mfPY56V}4_479Ul13&_ScBmEQm=VtGI}2Z~ zw{}Jlx)|g*G$>JHxE^i7v*v_M9<~5#@=A_Z6sbFRI^7rvIzIaR<=bH*5&c#tmrDJ) zQ(rCglI%=?4Q>9^g>T`rj*%AM{UChTU3}aB$~HiDaQn}v(S`X7+Vg*dbV+}z&=ySb z+)rSQH8)6-h35%m05@3g+{p_4<=cDJapn|n^OmMII1$|@&ma_sirp0b z-ye9rC5q*>vC`Y-0;m-WP4choru}sFCS<`njYZ2qyd%mbfYAr?UgkR`#x2({@>b%7 zB_dMYXFw07vuvfkjg%e#kYLw^)9>&9bk1lA6LY!S?jSqa6BuAKm|^lo?!+*W*L}zUT67^=QRp2|B=T&smvC z)i>nOC?D22E`nfaaEju4S=Wr84!d1Q^9FKDVqdaS{NZ`I@Cbk3D)Z@Ly7yobn!&^rz{*wIoj7$pJ1CaND&g}fC`&k_v0g$l7&?DAb+I9({>Y0e_TQskSNg^3B9 z9D&$ZMHj7!ksCo%i|`Ib!|)Ol#a{Y10G1WJgB8YCuVGBgERX~66Tp#pJ_#g`WE(K} z;U9ZkAr)eh<598PITZ2OFykT#l9tnm`AHpqCF! zwt`TOTLFQ*84nW(^@rlp+(IJ09PWqt;RhpbE~{=w%Y!f=1NgBofz6aU_MYSOzhPI+ z#;@ur!gjN->i#d``2*#hgxk;^A}=srn4fNU@O^c{F%RHxp6@J7veI9oq{C2vdYZ6? zp(w=Lu|?YOsRut^9>P%}bNo61S?=$b)0O9lno+|6(=u@50CQk^2-!_1w1cStK^b$X z7K0ygxFCw!G02FT$gfiAIwWO_kkA%eFwD zhZ~lErML>!itpc|?91fj(eYA)q%HorCOEjk3&l?Hy73i}T&VgKj6yKLFc}a{<9n7v z=K1UMQwK-`Kv)k3k1|A$<0o;*_=Ef3KnKDjI~IrhcrO5wbT(aP{#CFE51qgu$NKq^ zU?rr&vf2K{Y|ke;5%E1^rEBjasJ}%L~sOeVCZS>+}zY~S(Yy5=XP+0E_^?V;%;utyU~vi1W?yWxP6ro zsDrIDgsBpDhGB}q?4j6b1MFdTuxgyw@(*n1eK6@FbJ-qi)<`)_a2IT{E%G5E!Q=&Otm$K@|vU+dk0>VQp8mWy2KJhNy|*|7H``r`8Z zD2f6vmh>Oqq}lhoQ({mH69^aU^esj-p-?_UAId&oJu+pM3$4_75G#r$BI7K(OZpurk z%V=xL@Rpl+>QWLyH$bP8T()qIn|A~wEL(}l`X<^>Hqh$JO3fi3%sK5l_;VnkcUR)8 zsn)*3z^8+X%g{u+N`+%y@IFO(y#nsQYiVMY0=B^DG>+OdnpzQ3@fh^C3L^1$uuznv zsdr{?*CpuhvIWMScXNt{MC^IL^ZuaQAjy*`U^sa^jwfD5g}*=``S$M%xP=NP&s*hC z%3vlbLF4aj5`1l|pI}q^PFCb|PX8}-_R3oUTa3__&d=7;9D3PX^K(ICOE_0p?$vnl z`AMl6nn!~63ekUWgoErsWu`bS$y&q8$R6u|?r_@Y>;rz(rB4y8&PJCXIn10q5$SXS zF|4%UK9q+lb+y-G)fqJ_~NC*itVNF#L!j|wI1T`g1#WH!5!2bixY~{iL literal 0 HcmV?d00001 From cda1af26a25e61cdf5098085274cbd9673c172e0 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Thu, 30 Jul 2026 14:13:28 +0300 Subject: [PATCH 12/14] fix(webview): isolate visual test dependencies --- packages/types/package.json | 8 ++++++++ webview-ui/playwright-ct.config.ts | 7 ++++++- webview-ui/playwright/TranslationContext.ts | 13 +++++++++++++ .../settings/ModelDescriptionMarkdown.tsx | 2 +- .../src/components/settings/ModelInfoView.tsx | 3 ++- .../__tests__/ModelInfoView.visual.fixture.tsx | 5 +++-- 6 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 webview-ui/playwright/TranslationContext.ts diff --git a/packages/types/package.json b/packages/types/package.json index c4cbd4454c..98cf9f373a 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -11,6 +11,14 @@ "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } + }, + "./model": { + "types": "./src/model.ts", + "import": "./src/model.ts" + }, + "./provider-identifiers": { + "types": "./src/provider-identifiers.ts", + "import": "./src/provider-identifiers.ts" } }, "scripts": { diff --git a/webview-ui/playwright-ct.config.ts b/webview-ui/playwright-ct.config.ts index 8b236781b2..3eb0abac7b 100644 --- a/webview-ui/playwright-ct.config.ts +++ b/webview-ui/playwright-ct.config.ts @@ -58,10 +58,15 @@ export default defineConfig({ ], resolve: { alias: { + "@src/i18n/TranslationContext": path.resolve(dirname, "./playwright/TranslationContext.ts"), "@": path.resolve(dirname, "./src"), "@src": path.resolve(dirname, "./src"), "@roo": path.resolve(dirname, "../src/shared"), - vscode: path.resolve(dirname, "./src/__mocks__/vscode.ts"), + "@vscode/webview-ui-toolkit/react": path.resolve( + dirname, + "./src/__mocks__/@vscode/webview-ui-toolkit/react.tsx", + ), + vscode: path.resolve(dirname, "../src/__mocks__/vscode.js"), }, }, define: { diff --git a/webview-ui/playwright/TranslationContext.ts b/webview-ui/playwright/TranslationContext.ts new file mode 100644 index 0000000000..b2024e29d9 --- /dev/null +++ b/webview-ui/playwright/TranslationContext.ts @@ -0,0 +1,13 @@ +import { createContext, useContext } from "react" + +type TranslationContextValue = { + t: (key: string, options?: Record) => string + i18n: unknown +} + +export const TranslationContext = createContext({ + t: (key: string) => key, + i18n: null, +}) + +export const useAppTranslation = () => useContext(TranslationContext) diff --git a/webview-ui/src/components/settings/ModelDescriptionMarkdown.tsx b/webview-ui/src/components/settings/ModelDescriptionMarkdown.tsx index b04ab1163e..b00a36d1ac 100644 --- a/webview-ui/src/components/settings/ModelDescriptionMarkdown.tsx +++ b/webview-ui/src/components/settings/ModelDescriptionMarkdown.tsx @@ -3,7 +3,7 @@ import { memo, useEffect, useRef, useState } from "react" import { useRemark } from "react-remark" import { cn } from "@/lib/utils" -import { Collapsible, CollapsibleTrigger } from "@/components/ui" +import { Collapsible, CollapsibleTrigger } from "@/components/ui/collapsible" import { StyledMarkdown } from "./styles" diff --git a/webview-ui/src/components/settings/ModelInfoView.tsx b/webview-ui/src/components/settings/ModelInfoView.tsx index ba24fe3fbd..fff55eda55 100644 --- a/webview-ui/src/components/settings/ModelInfoView.tsx +++ b/webview-ui/src/components/settings/ModelInfoView.tsx @@ -1,6 +1,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { OpenAiServiceTier, providerIdentifiers, type ModelInfo, type ServiceTier } from "@roo-code/types" +import { OpenAiServiceTier, type ModelInfo, type ServiceTier } from "@roo-code/types/model" +import { providerIdentifiers } from "@roo-code/types/provider-identifiers" import { formatPrice } from "@src/utils/formatPrice" import { cn } from "@src/lib/utils" diff --git a/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx b/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx index 9463222c94..b007d4be69 100644 --- a/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx +++ b/webview-ui/src/components/settings/__tests__/ModelInfoView.visual.fixture.tsx @@ -1,8 +1,9 @@ import React from "react" -import { OpenAiServiceTier, providerIdentifiers, type ModelInfo } from "@roo-code/types" +import { OpenAiServiceTier, type ModelInfo } from "@roo-code/types/model" +import { providerIdentifiers } from "@roo-code/types/provider-identifiers" -import { TranslationContext } from "../../../i18n/TranslationContext" +import { TranslationContext } from "@src/i18n/TranslationContext" import { ModelInfoView } from "../ModelInfoView" const modelInfo: ModelInfo = { From c0c751349b5f1671b2e1d0a2b9f45cf12dd5b44f Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Thu, 30 Jul 2026 14:22:26 +0300 Subject: [PATCH 13/14] test(webview): update service tier visual baseline --- .../model-info-service-tier-pricing-dark.png | Bin 16915 -> 17534 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png b/webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png index 624ce5eb2e4545b849fbb8428c36582e58fe1b52..757f59f11e5a971f72ff2e41ad3720aa02f9b328 100644 GIT binary patch literal 17534 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 From f80107e5b1acf88d912ca8dc6d63203b4455afab Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Thu, 30 Jul 2026 13:06:38 +0000 Subject: [PATCH 14/14] fix(webview): add @source directive so Tailwind utilities generate in CT builds --- .../model-info-service-tier-pricing-dark.png | Bin 17534 -> 18087 bytes .../__screenshots__/zoo-hero-dark.png | Bin 20809 -> 2009 bytes webview-ui/src/index.css | 2 ++ 3 files changed, 2 insertions(+) diff --git a/webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png b/webview-ui/src/components/settings/__tests__/__screenshots__/model-info-service-tier-pricing-dark.png index 757f59f11e5a971f72ff2e41ad3720aa02f9b328..0a3bee53510b9ab9282bd0e1c910a2b2e58f0a66 100644 GIT binary patch literal 18087 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 literal 17534 zcmZs@Wl$Vj+b$fO;2KPji&h^<0a~obaHF`# z@3_zzw(m!8JODVlIqQX|E^^uT9zuO|i1V zi;$vuBABT)N-x!Br`1j%6s5cI3sB!Py!6AVED>8TT*xSZ#!wg7Ve&yr}*rx zxlpOSbf_3_6=4hLdV#zdZ<_>tLSy<*PLEcP4)%+;)H|*kyrD~~4d^ezt|t}v2XzS) zk5z7Mm;(HxoKz63W?!a-jEt%rpDYa7Z}Z!M#aMx7Bv`>BergaA1Xk<|5!nM&Rjyz2 z($A!*YSmQB@jzAb#8WpbNYi1P#Yle)#U!ItoYsFhA=7Oo^A{)jJ8a9BrzA#p%yp~r z!_bB!pAGsIta`Sp(!#{C5G$t2X0O$Jxfy(h#w_ZavY31O0&wP{KhUXHW5S})C;`c~ z(igwvSP}<|teXf8_W21B)VFB>xIKHr0q^=bQ&cCoUzVx1W7CTSq%d(0c_S7bkuA0B zR!7X*WF&QdkqO$ZJNh{e$+`-m_a2vgJ^cgn6Qz{korVK^DO@zsGWwfXF_yc=#62Xp z4W)VNeeU$uX!Ow>Qx=KZQYY4YR%K?jVJNBMWl#o^&)WxSgtOb#u^EbSH#5C18fCqX zVmJ||wCCp2*hm@8szgBMutZk%CtQ3Y2WfA@c`UdSuIc8ivI$Ha08Hs1+d``*R7$+f zZ{fI`P2bl)&R;V>WLeJY_}rM4aGV1M33*QvS{Tsv;fFoVE=mhd(qeUVJx8>X3E#S> zTPV!zVz~64m+O|YoK@)*i4t?>Y&UDf_JO@7g{YG;s?9afuaWaV!H` zWc7({w2Tao1zU8`PfWg@Oh#E3(^!}}$7I`RIUp;f?-xa2aTP<4t71to+*g;QC@@S| z3=&TN;5lqxY}mRWX6_z>qlOIrM9e*SqcZEq%F3KUvo)KC>OMX^(xS~&7eNSdwzyi6obUO zx$*bMVvW+gPoa}x?dps@PW^llV%N%DXwfoDZC&<#PFKA%3!kJKhn!gxuo{?U#Y`At z(ti|iqnGl0?IcB(aqVfy$9M{`oUTBD{AnA?H4|Qf`W#8eM8kK|UhnHRtVT zVlvk4ytcJ$nxq5XF&|bALTkf29Ly|pooBU295*(o2qHH|vlC1#>^A+F5{9Z0sNsp` zq0Z=GDom`4Z*vZmvJ2(#gs-5H)r+R!$cPgNGkmfW+%vrX%<%!dBy$WT%dXe=OM{lV zdMnK1Y*YI2KdebqGJIp<8!OJR?RG?VZJm(KJLm~k*`tUX!Ec-qET z?428#Jnu2r>RyE8z3MFki8`lC(3P$nZ9YE-XZC3ATI@fGVgQRN{LzUjHZgoMEk338q?Iim;vCABve z*1X81h4>h*Ng*SlkgXCuIiC%Iz-BbCTVJYohd67dT8wvEtG=JT1MMO?P>X@214pmL z;w<6QSU54gime{J^D!%8CoynElaBT1-;1yxM z*;o3RSSD2*Qtyr%^%zY;FiJqRt=-B(OUMhq2afo7Xy^cLa61s|P=wdnl~+`ia?D_& zA#F9wA?GVJ(!MammEQ7!I>AXoLJ4c5rMfe4&f!Ih7U{6 zYn_@9E)#QLaQz8kBpz(l1j`@lO48{zw0EaJ>Ryw3T0G z!*7S!TG*S?N%Mnm(Gi4E4&wu)O%mKqHU)4Az3WwlA0h8J2ZG>lkyiC%qLtrucn($v zoQP;)i+9Xd)nUKe7-5uP-+MJecF$Ap33ir zbs$OFB`I{B{=B^$9j%mV%7#znk{x47cQ2a&b9qzE`L2Hf5kSQG7U~L5AP5-Hdga^Zx0JLrfM92RuIdGL)9le z{6sb!{zE+|UV8hH)R&rb6-MUr?q`DGRjfT|6i0-Dp1c8h0v%y}`3BxhEDTX8udgV5 z7%a}>5+p}#{G|KFo!({4NDkI>WyWpJX-e!<((Sv~xl^wST ziE*Oe&6)A+3CRlaYy#I327^d8R-EP$dM(A4vI#&>h`5l!k8PhI?4+DDD-dgc)DTv8 zjxJQ-Oxn_4MOguTDkx-Z37~(Fc+Ghju=|IsX#O4h#WX?<=TKauuDhX|{3AA=t3`2_ zzLT>d+S(8!N~Vw>iur0I;r+YO7~VpzP1|nns%4%=%8}Jy%&pvM{ZsUUifs(-ss;hp zLuBNaM%aGN#!U(h-eW2ydta_uBMB4pLGBmi)RX~9ImpnSxPVS45jzhzdatnbF+I;z z{b63s3hgd+hw%pfg;m)H+%7Bae#wn-99t=k*<2)DSrYLy*6cW-WnD3^fMJ&e?Zi24 zyoQQ;qV~iuLe7*Uw}+ujCnvm>e9l=3P;)bEJwaL9-pjljq^ zu}h-Cn^s!|3fdUR92;w@x^?)0rSU}4s-#5tMsn;EoOp;B4pOwJnw}Ee-9m^og7_V1 z-byf>st6WvgoX@mF(ds+`=4Wf{6A<5>Az?z7>OGaj5%0^dP~jk-4yJMFzko=PfDbI zfhv2tJUP$KI+A}pWTo_F7R~d_3Nahas8g;KsfY3{zfI?*vS+DL^I%V>5I9KJQvp`a zR7uS_)EtQKLrq^!6zcVu9dTB>dVm=F<_%Ys2EG%1Vct^UQ>XUqfwbt8>VHF+RvRAY#Z+f2HBGWD0Xw4lX{U|UXNFxG8^2i$z8 zGWyawUYr$o1YiJExE3g1h{TIS2+TAYt}`|G|7=br1)nCW0{W#zaJZpXoQ_zCxF5>T zE9}T-SYMgI0*x-0J_Zt}FgjUl&O{4H+-_g3tDqYRSAMZ=FX>IUYG zJf~(VvofAy&$i%<%~Jkt>|isg!?f7dm`$<_vHA&&UgQOK>^y5>#ZBA8F=JTJEyr$HjiRCfRaD7jSO+ z?2t!q)_VKi3e5|<{vl`!%~>0}klT;_z`Wn|RU|`7v0dtZ$g5Rhi*T!28YQ68Z{0>K z_ftwQUF~=w7;CtEEHfy!9a^7@s}jb4`$!_6tnFGZUEyuhecnu7tUcgav^Q-N#~l-G zFJ4^iNKt3J{5wZ)`J!oZyyR?p%5KfMF=%dX*yO{#t9zSy(lO~RZoG|v{WHg-fR7W` zq}gtWt}3|r9_4nw$W6i(znrXE^C)JSo+kQ@`Qw@qNBCuts{jc6mjAm9+5`zIaif{) z+m&Qro%z-Nz_FL%RC2#06vEJYzEgsO3I(;XUu=R^I8j<3QKy<#U@yXsx#25@P)yP9;7bz&GFN5=QZ7OS1r`Z#XIh*GQ8{nCXftq@F@UW8EY2^WhMe zgzJ$uBmGII{=1Fh5FsXP*3igza{s$ed3VSFI0d{ zNbAN2HII6IP3|wqC797ywtSF^?Pp9!e9m{|iA>eR&nR)Bf3m`r3H`{lK$owewAd*e zQRlFYI6uY)hZQ{>Z~rTvt5A7tJ7ZPC;ozyZqZM5Tqnz4s(?6T7i`-6FVQ#WH1msKa zt{{CuuNy(MrP*k6mHmEj-)XVRzTAWT*fqixSB-j^`{`o&#W~56CeTD=K`ANR^zV!# ztS{X_O36Zn?3($Y>ZvM?dppY=PPK!#ct$Xe3*S_Z<7ZyXn+O5pCu4qMydAYTRIwQ+ z;e~rU{uD9Oq>5680Hvxi-nWsK^6ksyHl4*k(39|!- z3!Mm1ODVG%dT2IyHLaKdStLHFhpcBN78^3;AcZyI8A_uaj=N*PE$L_Jthx?8Ug!(^PfaP9he{JD4%o|^Zl?lW7 z`1EQIQG@7rajU_QSF5gjrS5TGe*mf`3l5OU2q)HN@Aq$z=4bmYTwBl_b=Acty;*ry zF=nM!?zjxeyTSxNYn5`>V%#U{4ZaHxYLvqoM`pl$@yS@D0j0UV>RBJv-xX2raFd$V z7bTpNoXDSWsfy)wtq%7$nMFv6t9vo}mzGNQ!$z6EbkF){*e0#m@BW%|cy8$Esf7QY z|AMzf_;7kp>e_L?>rnu73mG0LgZ8d)QD@LtU{L(~)oNCKn_uM4em9Z7N=oSg@Oo|> z+tnKQHF_<4^{BroZ7Vs2L*g&i4q>w$pDcR>c%600oCaYv8zo0o>yreM$01Kyn)f=E z4UgP?MT>I6l1_Y)6f0u)W|h(;03-A{k%$*bk$vm9uVATy8zn^=0<2YB;7Svr{d*j7 zCI^2`1cmqC9{Zo(8pq8JmM}JJQs<;?P{6aLw#@rWcs~ymTA*-u+7Z{2{a$`0}u%HR@HZi~jm?Zx!fqmDL>xfPUH#)=Pbl zKghp!>z;W&3+?_O*!2A96n;^=KG=HkEq;MphKr1QlM?QH*z0CpG%{=X*v^}b417ud z00Zc@e=d+D{r;zCw#@hfB)eMbVJ+tFK=}Y;+Zd=rYsb7%Wk6RcMGaXv8$m6j{yPho zR^XTt%9!Bx{;qwop@Kq6on%a-s~@i$O++z`DJQ?`!jW6ZS`E->64CxiPZf#zTYjei zSEG*5P2$H8^P9<`uK;jmsOarh>Z1Fp^Z!zNL$np4hZZ<_z|Y~5FTHj4Vc+iz1JTzG zbRzmDOxV#5VMCd*-T0%WNAz%$1KMG3&-xmKRRgGzP7ROwhFlVKd68VNzX0?hi-ooE z{56Owog|*T(cuTA%sVRvnj_+E$KzyW8}+Mz!;_ZxL-yebtg{#$@liLkI#4Z6M=^? zuJb?g1iV!p70T44vyIR3Ou?%L-*N&nnCX5E zAKZ?zp!|Lb?@L$`rioVd7rRajc|FAiBPbj)H4WIeiukf@{e2pwgChy>z+{38_Pc8( zG2tn+oIDEZ{9YD0`u0$N%&e#O*X|lZ4=fO0Q$c+sSpoQ3P$U8LPyiM5;GWlY> zg|FU4O&9t!tApR>Xrdn07bP7BOZ;TMV_MC7ZY%i%n@9SR|I*GG@WpaH8$J4air9Z{ z^5JeZ#@1iG)Y+^QSIbZzi%-Ho{|-yYq>P7=b337+wM_9{G9I?lBUz5V&3ESFJQ_VO z#?wY@EF3N(=5WG|1t*VxPNGw}FfDGJ-&F855?G3y?3aqXeA;}%`QwU~JDP@ocd--d zPSp_p`S!MNy_-I8qcat%ZMXlf3rmKYj;P92^E!n}3o0cK2V4i#H+2&UP=|xJc!?Y| z;WnObHP1{cCq4zbdHki=#R?#14XOq{v*Gm_bKNmd-#|h=_~hU$KUsAn#(CVi#+a;K zHys`$I8VBi2wn6yD86LwHO?gE(sRBfbHg%vVlt&rMkUTI*hq_V7v2 zYWy$px?fQyJM5;A3A})pyN4JY69bMJESBFW!#D`YJ{jN7^(|8j_XyqMd10>=J-|Hl zK=(NdYOOLm?@TKP7=l^-Pj9=#HSU*2KB_~6d&g81^onGyAr~W+2$nv9IZ4IF*^`)v z0wtum=&D#R`niEH+d|x8c&kMXQHtRF=6x8@n*(jcw{%@k1v8n;g*E$^?w4@#J>dxr zJ2Dv+EK3mnYWw-J2XdA0(V(uJvu1Uf-8VIPxugP)b^}M@t>c(nGs5Y(WS+`3UdM%R z5;B@F#x0^X!;|5+{gh+Vu_B~GOe6wOD0qzXbgWgn{sF94FUaGFe)K%IE#>f*RkNaN zum2Qy2#T{d?8g?H5NV{i%b9B_bSEJ_vI;O^>cY!6@6~ge6m^n50!YSuqEC$|_9&rw z|F=Gazk-K`bQu;Y!1({ASgwj>|8|LkY;iCz;fEdLjsBN{rMQ8iIk=m!sEe@?dMYSq znUiQ?{ls6Y)^W&=u>$1@*^%6HR(*KatE94Bh3!a%#8J&%UvV=nCq%a1O;wfeJL#El zELktHX{BM)ICifvQPYB@^jGvlOq%q8Vn7LJ#^eE3hFjgQ!RN6?`CgVYGs7|Jb(3#j z+<0q$cU|q8dJEtGw5J`tu!PB_`s4J4XVSv8o#Cl~y7w=t+^zwdmne9W{qW6lOPZun zg7LJ3n@rmD%P;p2#loTl%5U%}5iMD`%WlrBGxE~C4czCRHE1(hnGKAZ@+mU|cE1)T zQqoQfBUOEwbJ6Q$9i)7Pk#+q_a8S zjk0~&)nw;BnoCd21!OG@RCs-!z_N=WuSz}1)ou8J&b`gU>^l|)adyR;=OT)+^4`ND z!j|6t5qLTIQ%aeI&aA9^nlyL=ox^rHZd z&yv(3ZLA5g+oG!^-~pfJ(aT11Rcz}au21d{onlOy6`hiyR+~V4GN-r+(q=S7Y0tS> zzH_8=Npr(MbXAODIV$qab(@XkrDk7QZVn6y;iX;!|Api*TORRqM7F^MuQw}J?I^De zQM@I~(f+-MR#O37G;Ugb!G$x!ZJ}{Pj~+>a70V^c_{RJ?l@uk4o4Tvwk3x({Ub+

ga_`Ql^@q@1{LFD_Kr~9E4`hb^@?C{+_cyg;q###ku(Uz;{%1 zRH>PzM2|`?1`YfE$pHRY><2fOr(J^VxzDs{8Kc?ojz}kt|gWmYo83R#MmeeUU|ulao!`PbUiLT+AK zn<;h9)5w+S3DK7X0ZNT=)7RmyPR2oio#OXMg z6qN-$XGmJ7d8tL?)(!xYxe@KMKBqHxGN2o2!=-hP>IcGso)Rie?^#<){DbEeE|eo} zhnb|ih6m^YiSto*i&p|fTf3NX$>vSkv4v__M}f?yv(DCOia*VRnV?lO8THi=IJ&$E z`-Uwr=ucl?%1-7`Djjf$O{K zq)M8NZ@SV#jV5vOF1b}VI-Z=%{Mc=EZs*7H&B43t`YF@yAlcY`kcgj%(4aJpmV@FD zJK`#vC#XZ^Z$TFqhX6$ulWZ$=%9=Kw-RRMMF#a4V&qd;!(FfXx+Q;rzS>^QNCvvqa zi}dLL$Lr(|;PXDBhlbhecUHPzw6ZI>0(r;s?Y4-9{4n#R<0rVlQG}KE!N8PfH{k;g zb7RS10_w4+o>#9}m9sw;{J6=jMV0H^Ko2gIVScK$OezI?gW zi*A2ZVnfNc|8f4jE@ttY36$ls;w&j>2}%|rL$FA47U^=H2GJ?lvxUn}2`0veW2zT}!1+^M4vFPfIX|QTo|=Q8f#` z=L805Ds}1ZpL?{@e5vqfpp@xd>yfYkA`;)6S+oSUv|?J#CNVtr`>-+-ovKUHUFhFo z6u|TFsvIO~m!FxlF^fBAW6M4**Xg0hx{ezsvm*2}A=vfiW0L47EX7dk`Gz>mOI+xDXufd#2 zK~Z4Jm~WA$odCC>=8p%p_k4uI_I3A`-we>KnT(iXRCT5ZXCwj;>g`lno1d|IaM~Q_ zXqiGNs?nlQ2dM4*9RQbq!~%?X&+ig`Da9(6STj-!UfL-$EaafRu#^2vfYduMrPo`c z>e29+{CQEy$gSu`YY9l4BD78l$DhNW&oG>dwAImUkf8Y)=ksig68s>wI*= z2-|+JTT2*>Yd=Fvgp0|?xyIwP9Y85*pP|&?%Q6fMk>R$ib(T>Jf9Z08)m#*xtKvqP zeFT}qZ~Dw`??C+Crd?bZM%-$5$N$#_%ogc-7#yI|Ni&oq#Q3}3H8M5NH=!0O)q{Ex zmq2BF{O&qSz@%ixwnPmr;3B|AWuByh;CU4KymQ+#407ndDaDohIvy|Lhr38k-F#w~ zp4dYmlvDb>VeDjbGlj~l&t4N zn-N+aIGk3^y8a z?Dy}*=F$*%Y73xIC|vhs?OFR4F;6KZqH-4Fm9ipl=qE+9Wz@S8zG!r7+DvZwJx#lb z7NqCg^j__rIAjO&1#)|3*x@-Yr!umwTU7=rP>St8D`uOT?WB5FIHsVIe1k@jYx#rz z%cshsZ4q;6iwnw}&=*d#=gIS+;RjVnnWvKh3KB*4%3L0l5HhQ0GN3n?!?rMtL58<} zH4~!nY+2HX)+j+)ZM16PdCG5DHG=W#J!ykE*3?InD0|4~$A%=S5LVuLWzAdYll3WO z*mtyu2=tvdNKc2FXr}~fuAC{r@gQY;yu;s&;_MUL0a#E{FNnQmra3Rxh>P3GbuIlY z8h*t%60fvOnTaY>Z>!fSeMji5g*_`nTtDa=S)tfdSqF}lR z_dgSRO?*Rh5zIj$Jj{H`4 z0QSZ)XYg8Ur|*p&xvs?<3DuM=QtX=+BEO6MV{t(v|K&zK;RKJDgU&Q6L4m&vk-Er) z`StGt#WZb%c9^Rlryz{M->Zy1Q+@)Rnp&p@Izy0{rY1LDIqK&=N`8SY4zL+Own&yGSQw>LTK~!X8I}&wt`4*{}7i+<%VhBsgGS@a$U;!`)ejpNp z$U6{`M$u={?^kdwdD4N%RX+=pZ)y)5(J^k`L0{l^ZgpjSDjB@DuXU^N18`7y7tKAH z#O~AqZNHn=$|qK&CN~>3G_{;6FzK{pKL$u-0B!==42R^m9%qjPxg_wxEpw!T|JCmj z{}-0|A6c*C2?KxKrX&qxiR-S~@ZG}wi9{XRPu2H#+}6u4tU(}mih=Dajb_9p{Vn4Z zMZG|=-JY5SHeD`lj7|dJ8}wlgL)>0`+Kz*J81GkXu88TQOP2i#qM(>yfcwj)t%-bt zq1GCqh5sHt3H@pEz^=8?#YA_+8zE1YU8$-0vwXQw^GpB+^ltynVZ@qeoJqG+IDB?Y z@nht`7_MmhUR7~7$j-8g@2kCA_{QfEam9&SM5~6Ttoa0Wel#-W$WuzJxOgF1c-K&i zv4yEgsaqFKnkb!aLAal)D04MzN1(~jq8vH~ zi#a`o&L<+3tBP>Yt~5S^e@ibEiU0ObHpm#pptmiNLKX^%?$hwAKKY0!^Blw z(sebx1|w|dkI|3ak{w?tMo*z5TP1b=bU4iybM<6`y`Ir<+0{l>GCkSE@n7k%3HJ7X znZ`R?m4GQ(aI_B+Z%$|**8!&xEzR?%*VFH}-LkLLAA9W{A{T#uU!iq03nZsD77X>v zfI~u&4B>iAFSuO@17xTjgK||>9*Loa(K|UE6bN83u)bu^qe762yM*>LA0>DB>(hQq zu&+_lfO!Z?l?V}Iqwi=sDs_czxTmClvdqwiBY^LYNBgNT|HeemAwjgpI2PbX_x^)Q zF;1m<1!17bc+!;>O+PPg^462}5i%KP_s>!jYu4S`#&y;iG58iQ-Q=3BZ^w653Iw<1 z1FZ};_IkR{i0EdrhW^0${c>+m^sMvK5@u80jG{~qW1TVNLnXeDwcAMAe*Tur$q#oh zgm`2kn^ebrZ1>tb`tjFMQBfpQ>rJw=;^S-oFp251sUPvLt4S9l;a+%;LXLo(tq-Zr z#aNifKVH}JHbl(beh(<}!m(})!8ymP*JZvy=3#f59YI437nOiIf_}@l!@qG}4O*Xz zY70cXEiTbbtS1khd*M^dzZX*w?|9F4aPV%^Dp$a>I(f|7Cu}Sm>c|=-`CPMRa8dVB zpLo~IWqky)s_+J-K4d9OT0-kg)p?N`bPdmeT2_deGB>EPOD9ka`6zUv&}09qca9^-ilAa63g!;?shkqUjfe8_-TKTr(&BVpule`tui*Ft^0!NaK+ z8I04Pw)!$QI-+Q~p?S9ad5z-=%VrxA1$cii<5Zf3Qmw$&?#n<@5-_@~`8v)lKNoQt zkieUx!!#;7s(PtT>lA;~!%zHU9(k0)A zXK@-d=cBZ%eax1Hsl2r`K$TSazODk2{1o+GC64NRZmOo(ppjQ!m8(#bxOeok8tnyy z$Ib65j|8Hz1R&wn36Ef1HMEI9eF+y_G3sXgBRnj<6LNYH-p<*Hc@oewm*iX!)o}nX zPHxzgLC)V&>IpY^Wm^h3p+Ac>c-r^;BpL<}iO9-|Z)bOJf(j1cb&WD{Vm& zw?Ce-fxz&3=OEk}L!tUC@Z7Tv)eoF%izjbt8;*tRfOpjHI4`^3L>iPui+ULH%v~%)o zP}bKqS9<4CYQxVy;7U~$&V3ineLG#Vk&qW`XtXuVNy_BYRt|#F6?PR1eu^j*Vn5Ab zKO1?1Ac$pSH5@q?8nbBKv5!OPB}coAeXwis_vOmYrNFR{qjHG!q39`XvejOe+J?v* zpLPyKPePwyr(%DJq_=AABd(MA>Y#M;oy~Po?zkdSgvV=`@T_MwLp|`HFZHWNP+P_?2_x z4x`q*)*2OwuJfA{#$HgVk3kG$Llc)9PHjT4QxbGN zZV%J=!QpYL)G#r?%eYaiMiN-Aj|j@(>2W0L)SoF+m5}94j_yS9c}&<`ic6j;6r`OF z7k;B73gQ($c(Y{rW!98h zBBd4*s9F}4_?o`GsRu&l8)gI)@Y;8|I$w$B197DK*KSZ-*_FMw@?oB~MK;Z}_BM^% z^sNQ|d?A2{O(L^u(r^eC^rAS#6n>@D@V`dzZJ{6YZSa0Y5E_H;Gg53*n1sI`jQLHN zqWX9z4LlwJBjio;2NCnrGa=ubd(Tf`t@ke^(Lb3Qu&B(o{vSEU=f6kd|Kxgp>`4Bv zum7vo!8Q2>Rmb&CoM>B zs(^*wizw9q#-*JchT^@Z)ri?*^E;Q?hV+=f84hs?Tx^Bux{7iKVhC7zj8s5Lvo}G! z$r0Dw*dUiKeD_m2fppYrY>8l{pxEUxuDbv=qM$4ps!Zm?C-{^-mPW<;K+akS;!X5} zq#yawBJq2F-~bIxh8;vn3bEnqiewmZSE)kBrT#?nLfFjbas=)gRV(n4{!_Z~_pd2W zzm3xL*dI6nV2!q-FnO9NZ!w=R-Vqk$ZRH-1JOroZT^bgo)Ju=ruDN5r(+OivDSm=I~0b_RQn1t;aGHLW^69xN@t zq4A!UA6mn1jGlO%7GW}kpIMbzJA>0jk~KnMHiK!Mx+H%!pt9~-N7#Dws$0UIJFaiIUV`~P$J<^YJ^lu>|^;&^z zs;34d1!159IQZtm{0I%TDC-%VZ15j{Hu`DG_2dyO{+%eE50-l83mDDx_(BI1s7~Nu zPww43nQxE%udKHiG@K!|GhU9P1cS`BCKU$HxmN~r>TFm5yx+d;Qi8<*M=aduFEnXQ zYp!KC>k3#!9E-xA+==eaSzdzRDMW{;C{ro`>ZC$P?SJgHZ~O%2mq73(h4v%CVgXiW zePz*wN+1 zi5ts59{g#)mQ%X^M{*6lw~|__(&vMeUu9FK%$dlCxd+F$-LS9`SoH_Ee+BCpoc?-` zi8T||#E~GZq1RTVMl>=H$_c|m(_EyuLX!chAaTDW!&ooophQ~g!jB=_)sdfDgnf_G z`sL!k?BF?VcEOXJGWImHe^s~69!0(_q&{f4=S@7HNyUnxS2e|4EszbtjIg&DY;W$m za#xs9S$^Wuz2Pt9DPJYHo8se%x@o7Bs}Y(0s+kQN(w zu)YmrXTopARjLKqkA(9^p~8fjP-&=aHrTJF_#K|r+FrZsRRpo~gP!3JMWnlI=Ugy&wXd^`_9De|Wmu1W+(#BJSX z8MpNa#fXhpr3~X^a++V5_SUgkA2&+~Z(17QYU7=s%e@CxPjbh@62r0^=N-Fse|?6t zMH*aZNA#dRj);9Sii;T|oBECzNF+~+%@?nPNg)~(-KA|Vq)eAZePX{1bk#pWmjfB8 zA%J6qs6r4wgTDXb*XwsZn%1kTRbXRq@2Z5s*`VkpPwF?LJqI=XY#)K;EkG%(8TeErd`AoA$ zkP7T8*N$`#4NBW_WHJ&h)!wnTe2F(g^69xkRFvX!6Q4m9rP+K22G$+?c5BDt9K9&= z%utwY;yrDOj77Wc2{l3SNG^v(tw*}h28COFB2E}u8AnXqq~dXv`=@#cKSCq^YqSeD z8d8iSx{R?FKn9ThiGxvn?}5do;%HVEldk_7Lh2ZmQ!U?5W!C8eI9vu zRTtqO$1aZKY08s0ge59NZfF^vh)k2ZCeEzNlOvnaKiga1LBLzQnfK3eYdINJR5Iq8z#?mc+x%Do`qW4& z2FboqKiI`wcW9KVFxT+EIX1m}G{3u(@h9GJlAixrDX?XK_vOLsOFfm;>>dWAoDt#mgWxZ!83J~bP}QwG_S^LYrC2AU?) zIp)bvVnR8jz|s7<&GXw$pKGq5@X7Rt2Mp9jAailQ2Yu9TzXK1z?^oeQSyQVxFdOy9 zE<%>6^KI**158$T^S@0!p8q!ru3{P>L(|FsPfER4gYu-Vt=S_dk#=vUJRr(FH8`4q z5i-~dS$YxsUz<(30=#kL|JrT;BTw<_?K|+jBXiMu`jnW3GImMoS+B zLEmX^4`nJxx^ASZt!}-&EAoQ=%*BaxFZ@^1V4FoN_XpL9i`g31D0p|BmRxOpvO@0P zv~j_PUbNsACum*D_t7Gdcq@ajtYq@?(zI@(9>02oSb~39X5v6n!=IFbVYyTW6MRt2 zrslbPqF&~6Z6!sTt7Rkmu;P771#Iq!oVN^11wQP33l7l%Z59rpgS-`@>Sm=qS6uS7 zJ}Xgg$Kd^!siNj#MGCc)qQ2j*rF(iz#9NMSOFrwJ-@-@sOKT4Sv8pa#myZ6%*MXcx zDdkkl%zrf^`c7Pn{3JZ%j){SpMgWfQ*8NuejNoP{R6!>_iXGgt)FD#}{TTKVZ%J@d znf+N6L)@)#I!{Iv1PLBcA*YLOA&VY<=jvf#<$Vjm!Od6aWd9}H`SSR#);EDE$;5W{ zZ07XC7Ecb!R)aJOGv&ngHKMHX1HXBq4D}c&Ev5UGPLag+vJu%OZ7sQ>TQV!;=Lp}8 z0;4mR=EjxT+B-w1-QcnQHKs00-9?}Exl9}1U}8_LpW}=hI`np)oExrb>{a{?D-=>j zDOA@uh1K{C``XGsv(0r7sHJJb&)WXAWV4nC9BoUqnZ%1(@Z!`7)OcG*R4(-GkoB4J zqSfTvK6Na696dI&277M93OXXWr61l5eM>PiNaV-mhMQ>GI8&@-WK%I}CVudca1EjO zww?Lh;9q3(PmJHq1%a|h6m0JY?+lS#rB-`Psc?E9(!5)9q>>)`Bg5KlJpB73o=*0Tt<$HGrmFZw#I9zonpKW5M19+;`T{bavigldXE3Q6q36DOt*0)?0Xk6m$&CB5wX*@>%DcghkB?Teju|v0t+!Y!nUqcajrG z+$iQtU+^KSEujGXs!Q(}xV%P8!3oAL7r8j!H$hPqivM^$-2Zqz6TzD6BZODupn~_{ z36Gw5pLOor)ccl?zO6W{-SJ#D-RA%=yOJ(T!3$0;?9m*ZBja24*5~|A)k!{MO>A8C?px6@coj7}oPg{RSy)2mz@e%2A_Jq}Cb#EjEFV-H%eAc(#)ILxi zSo+4RAiUm=M-q=Syp}5jySC2)H+1eag z3T(^f%|<~HC`{`Bfht3dRVw;4)rm;fjkWuh9AJghr9$G1mXt7au3!m5e0tO0&1sEX zy?1U&aPrJag<^Pg+3?ARmjIQ`;^N%GwCj3S?1Br@u4He01?njD5#J-Q*K;T#{mhm2v)dl^%r`=~M~ zbbj#en0YH_DGqmz99_Bk5)MrX6+wi%7J@M!f``hgQZW_VSn#av6lw^FTCqJdkx=Ci zCeMxPhIGMm_v#mM=Ihczlh@1RujaI$@AND1P!FG7V+5dvVox>2Ht!xYx@&I=pGRFe z5w>TbEJ%8y?DE-SkEfSA@G38AwhhZYZm%NE47G6Jc;l+J_1Q=SlRcG|4bi!F(!;JE zBzpiu8HK<+wg$auxD)oog&fRyp=S5~Lv3g2z&WM=hXMcZK*Il2NPTeKqbOTrbntje zK?LW(Hq~?av%y}v;x|nzaRH)-yX)5tZp^KsVTD0<3D)6ZVy}ODNMw2#q>Q7HhQ~Cv zQK-Vy@YLFg%bt84g`AkSQFw&i`Pv??DQG#TWylxo{=r!Z@8ET!zz zzDY2YvEwWgeik(km^=aLeh<+ZFr-e*se_DDbL0HnIWNGIdSd(;i;cBB_-V!>VmQkNAJbW!7Kjko310kn zW3&4S90a3GU69SfZslGJxXt%b5-`Xj9n)*q%Dio+uBJ09TdD(Xbhwkmu zoFFeQj;~2ENglfBWOb^EzJz0r>bS804=S3#UBz8;5dt{czb1Bddp1kU2`u%*ufCEW@{*dUhgPLTZ_b#*J}4BJc$$dLkLjsd)C#Lk=gzsR=q#&Fo#! ztBr-amXFJvP;8qgQ+MxNV|bD(0lgnakKWE z$ffa>X)Vhs0i)`^MT&hc0nTUrm#KBwUYK|3@W#yd@j-?v{{0G>1mYZF?$3Nf#vV=x z%sfN$+7=iL@mVHmpveFh#)N__$E0}WN5AOoe`=A7;1JQs&+2Oywmytx9~sq;-b;t- z-lYKEGvxdlYT9oMI?zm4B&JsaTxT|LyU9~jgG-kmlHHts?vV1D_sky~MtID$#<7q} z$uY&I8P}#DzKZyb(qSO8)gEg@0uQ#r;f?-C_5?6?kyboRI$}#p_|k4wrCw8>t>a$s z*m?LO$3%T)7(tY_Git>?kcj4uYMIg zk5BMh3W;lf+YQ(?gP+4Y`vqDT_dw}|j$13d#~~F4lt5bt(piFolM*eejY$8>o~yeDTK^yVW^cO#R>T=b?5okZyh+aYr%y79yvvD=At#G$MH=> zUk6$=$peNX!D-yCUZcqOzzG|!Tl<3Ou4UA^i)=QzMAgZ14~bcJ_InLVmx~x)eKIt@#xi@VP86L ztj8S-Z_8Ul#dt0f**$={f(9)yMn8JeYM0M*1#K7U{=pa9#KF&^QSYY5jnj!lcr^u~ zamRI+bA4oDkw>v_{|PQi+4^J(Vl0j|n@ntyg*#{v2*f?(ZTE5ejxnASoWKXs)o4

a0zvQ}ys;kl)Y_J}W;s^&PPeIKifzMw`K&0`$KYuYO{U@FWvfW{@{6tF z;HQGT8_@onQxQt%)Uh`Yt>^|-G9 z!VqeMl8Q>JKvE9~`$a?59gN+2D>N_Sp4w)gRg~wJGaF8wRBEM-j5oF0H|TMH-!s)% zd0***%fZ&?!$u#3HqPX#pMzpux&k7=mEilKx(21!IgR09AL9vaJD?fixS0000BTC000M$Nklp(0=jH(6l02&h}Hh!-?bS%N+&7$sE_W1=rsV}0?VJeio3Cu>qPz8M>^ zN@AZ(s-QLsHAZkT1*@Qm-6m3Z(F8Pxo5*eZv$K2VpEEO?T^Cq3ne#9l{&SswzVn^$ z{4;CH&o2-J!R>a7SC~$C6)hkdSgFUtO7S}7YbBMke(=KTd-|mPu(86B=p67$En+#& z)MKG`$>Ph)ES^@N+NXFm(3Wf_9t$u7@v>Cvdmgg-v>#eud4~|bM1A2gFsyjX#k_cr z^iH|ER(M&gFQJN#Wnd%uMrD{~{Q`My2GsZ%Jau3O7N6%8yb@Uu1T}RJR(slG4 zC0$3)QPOqv9Hq$C(F+#D7>x#D8-OQ&W@vS5|lv$I;+Jx6eZJN{c{RDJ=q5 zU$ht#6BF5r_ElElXeFXqtFOL_K7IPM(P+dn^zw=oE4FRhHZn5u`gPCz=4ink&^tpQ z>?-9s3qz#5PsxIaP7Xy!<6o=woA~&+jEoF+C$X`y8#it&DJi*k@BYNZq^cf*uqKY9 ziPwNyKpo>0voen6{7%OTRYxybvZT8DhsTfY&z?QwZYDW78DEowuA@WO(Md^(%a>bQ zTicEu`>w984(a02qgIFGpT536i^VcIIr;hX80b1WbRCU!f!knzf8W)sSF5V3K79Dl z-fr*j@5f4S-n?fphHU0q%0 z&YkP+?PZLN%>_LkO>kzSiju{Hd?=po3t4#k_U*X24Gs>zeEIVB?c2+iEj1X7adB~I zdHeP)BD%0}T}g>8E6Y+;R1^~vlbf518)JQaeRFd&)pKfNg&_s0qqXoJh>n(On*4Kg zRn-A>=-s=2IGs+^YP)vrK6&!w@bECkkC2W>M?YgjJbbTRyA}^V$kB@zFBZGNMX++^ z%KP`5A)?~Z99ss<(L}!r97mhvBgt@QFd(rAnVA;k2ea8cFfefG(xs7+VVli{dmgH{ zxVTsZHJ-einwkzDK8$Vg-^Rwq_V)Ignws(PaW*#BGlVtC@d&C2^@DLW{3=&~#Nb{; z)3I3|TcSojdi1En@$ZcrHzp<~hK2_5V144m_cb*?A~WDo@YAPH=xfYl>AQFDP|vlt zwl+34;gi5lP~@RK5;$72z-;PhLHT`jK|ww$wydlybYEd%q1X)RqNSz9<#HiW+_`hN zwAA+E#S4tpMMpFN2ezP?W9Xcdk|AyHYmFD)(Y)~#C?FJ8nj z1+oVgEn2i_&6+hfTM1^KKK(m0GsAATqtX~3pT@Fha&Ed(PMtc1Z1dp3gNW17N=E)t z9j(UE_;LhwMS1ydj7~g!`0(u6pFV#4D6WeO7tW`oq+lJ~`q2B`-Q6fWa1Ugf#CN`8 zDrLIg)YMcAeYCXv83`OMXmfNRI$DdPd5eItH+1oyJ$oDu$F*w>c!Fv_&P5y@A~!M#>U22DLFlQEn6ZSk7=0f{qU6o#FB0$G2?Rf)-T7SFZf(bh@aX7b_Ew%hU|$-*pJGu{3pb zbad3_&BZVs@O0MJ*1B9yLI18J1Rbpz>xhkwLv8o|{rl?bYCM>bXRdSfjB<2p>a?oE zcT_0s)K(TywhD@)!;Wh|I$A8B2^}3UN2jEuV%~0l44@jJ_D-6chg&;N2=h!w0|2+T z3t{;_+H6k4Xa*jmnI(9R*5lE@aHjdraMPyZj*gDe(NQ2egxf2)-9Uj~)!l=x+3+O! zej`~}In3NUL-7B7bbfxG#gh5_d6!~lpzCOuK^=|J(az4Not>R@W^T3bI&>Wkf$3$>UX!J7G^EyX=8AmhA r{{R30|Nkkiy{G^H00v1!K~w_(k1$vkp@mAG00000NkvXXu0mjflIZQT literal 20809 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%0A%^GVq=aM^v@2ZP>b5!`ICA983!VWb0i#%> z1C(T)4v;qp(rqcC)xdZ@x7Wd}prqtMnWiQ zF7#58eG7n}0dfXxgN-okxJ)1C)~L@}Usf{)+Bd0knrc+nGdDMq2A#AAL6GoLM^Ic` zd41_anr)md<9s=gkmnGisJVw22s#4M=)*b|EoaV*pHpDtS z@bPR+#jpiaXOYvcB9E4llbSkgIVQkVDy%BN%D}qi`GXgFMUcT(Ov&^KIS6cPTYba7 z6!1c_b-M?^kzm3s%8TE=ee)Y#k`@{LCMT_|8fpoKQwplX3pj**yIfrax-pc4cso^v zRunNuhb2mSaNOR;)wOv+AcSfLf1En(lL3JoE{ZoA{f?AYUAX=5XLlN+Exp4x<@dv#cq$V{w)M!~NkKwzlX8fCI60j5-f z%N|yhNoDwRu1gYQTSE)grPF4KsGg})n^T5f<1-f*I~X&HO^|xpl9Z&97eWE9HZ~Be zb(CYzM6)U=o$*aKMn{V-ZGrxqz$06kjgoWHfTps}1gp%GAO}4ZTkDLXU^JRIEc*l+?m3$4}?0P6h>3&~&>QuDzuE-YzMC5LI%QX|ZT*SCH`fdSU z1wZchu{-=&@)n*P5gCb7C%3-~3wPjiDddV2VeRsDLO`pakM!eeW}|Zp>T~9to|hpv zc2s~=QnSD4sy@j{rJd{u5pJf}?_8Q-y{h0?4%V2+6nl>!X!je6N`#X>VPOdIizAsh~QZ|3m?9sDdW50oZ#{R_hVPVas z!IcP{qA;rHc0DzcY$L~w8|R;0a{hc<_}{%u=Hfo8L`?tS4$x(ru)WUlma8}5*r@jT zWyOla@TdPh>jWej^kut|xQsx%!WiOu2An8+9D!hA+clku>wM5B&xGY)VhDGTa}|rl zII1S}J%PU1PiQ@n6LkdcZ{F6n3|#kBG65l7VxgLqh&TXqQVyZDJyj2*At9(>v;&QA zmLdO?_^}B40S#Av1Pu#_r$i3U{jrM)%%wtc8ypyg$7Ms-mX-;BeL9Dx0_=c{;1_P? zXTST_E)CX!p|fbE6@>Zz0(iaj)WREF*PUB1C@FS*#w0j^!1i3V+=r0rcA~y++-iLO z5E2aYw{;&vkGkNvQ4+pKK*s+U6@pCo@gyx;4ip9g{S=&{>jWf diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index b93603f5a6..3f6cb9c54b 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -18,6 +18,8 @@ @import "tailwindcss/theme.css" layer(theme); @import "./preflight.css" layer(base); @import "tailwindcss/utilities.css" layer(utilities); + +@source "../src"; @import "katex/dist/katex.min.css"; @plugin "tailwindcss-animate";