From f87698c0d4afec1e4b621002acda0a13cfe2fc81 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Tue, 18 Aug 2026 14:54:50 -0700 Subject: [PATCH 1/8] feat(xai): enable Priority Processing on the API-key transport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase B2 of the FastWire umbrella (lidge-jun/opencodex#1886), closing the request in #1875. Fast now works end to end for xAI, and only where xAI documents it. Capability follows the transport. The registry gains a key-auth service-tier overlay applied only when a preset allows the key override and the captured effective auth transport is key-based; xAI declares Fast there and stays unclassified on OAuth, because Priority Processing is documented for the public api.x.ai endpoints and not for the Grok CLI subscription gateway. The overlay resolves inside the shared FastPolicyAuthority capture, so the catalog and the runtime cannot disagree — and the runtime only rewrites the base URL to that gateway when authMode is "oauth", exactly when the overlay withholds the capability, so Fast can never be injected into the unverified endpoint. The catalog stops telling every provider OpenAI's story. Fast tier copy is now per-provider, and xAI's says what xAI actually charges: priority processing at 2x token price, not "1.5x speed". Providers that declare nothing keep their current bytes. Pricing is declared rather than hardcoded to one vendor. The OpenAI-only provider gate becomes exact (provider, model) priority rules, so xAI gets its documented flat 2x while routed resellers sharing the grok slug inherit nothing. The long-context relationship is likewise a declaration: OpenAI publishes that Fast and long context are exclusive regimes, while xAI publishes neither a combined rate nor an exclusion — so a confirmed-priority request above 200k prices at the published long-context rate and is marked a known lower bound, surfaced in the dashboard as "≥$" rather than an invented stacked multiplier. Billing still follows the response echo, which matches xAI's rule that the priority rate applies only when the response confirms it. NOTE — beyond the Fast path: xAI's bundled cached-input price for grok-4.6 was $0.30 against an official $0.50, so every xai cost estimate (not just Fast) was low. A verified-override layer corrects it ahead of the bundled row, which the existing expected-price overlays sit behind and could not reach. The Fast multiplier applies on top of the base price, so shipping the premium without this correction would have compounded the error. Full suite at this commit: 13330 pass / 10 skip / 1 fail — the one failure is the pre-existing dev-side key-login-live-update regression, confirmed to reproduce on this branch's own base commit (bcc77c039) with none of these changes applied. Co-Authored-By: Claude Fable 5 --- .../docs/reference/configuration/providers.md | 20 ++- gui/src/i18n/de.ts | 1 + gui/src/i18n/en.ts | 1 + gui/src/i18n/fr.ts | 1 + gui/src/i18n/ja.ts | 1 + gui/src/i18n/ko.ts | 1 + gui/src/i18n/ru.ts | 1 + gui/src/i18n/tr.ts | 1 + gui/src/i18n/zh-TW.ts | 1 + gui/src/i18n/zh.ts | 1 + gui/src/pages/Logs.tsx | 35 +++-- gui/src/pages/logs-cost-format.ts | 11 ++ gui/tests/logs-cost-lower-bound.test.ts | 10 ++ src/codex/catalog/effort.ts | 2 +- src/codex/catalog/parsing.ts | 2 + src/codex/catalog/provider-fetch.ts | 25 +++- src/providers/fastwire.ts | 13 +- src/providers/registry.ts | 20 +++ src/providers/service-tier.ts | 35 +++-- src/server/management/shared.ts | 4 +- src/usage/cost.ts | 74 ++++++---- src/usage/expected-prices.ts | 85 ++++++++++-- tests/management-api-logs-metrics.test.ts | 25 ++++ tests/service-tier-capability.test.ts | 126 +++++++++++++++++- tests/usage-cost.test.ts | 125 +++++++++++++++++ 25 files changed, 545 insertions(+), 76 deletions(-) create mode 100644 gui/src/pages/logs-cost-format.ts create mode 100644 gui/tests/logs-cost-lower-bound.test.ts diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 2c1d2e3eef..d7c2294cd9 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -87,7 +87,7 @@ differing backup and rewrites known legacy namespaced selected ids to bare ids. | `modelMaxInputTokens?` | `Record` | Positive per-model max input limits used for catalog auto-compaction hints. | | `defaultMaxOutputTokens?` | `number` | Provider-wide `openai-chat` fallback when the client omits `max_output_tokens`. | | `modelMaxOutputTokens?` | `Record` | Positive per-model `openai-chat` fallback budgets; exact/pattern matches beat the provider default. | -| `modelCosts?` | `Record` | Per-model display prices (USD per 1M tokens), keyed by that provider's exact upstream model id — not a provider identifier or a routed `provider/model` label, e.g. `{ "deepseek-v4-flash": { "input": 0.14, "output": 0.28, "cacheRead": 0.0028, "cacheWrite": 0 } }`. Any model id is a valid key — custom providers may target any OpenAI-compatible endpoint through the `openai-chat` adapter, and local or internal provider ids work even when they are absent from the built-in catalogs. User-configured prices win over the built-in catalogs in the Logs `~$` and Usage estimates; historical entries are repriced from the current overlay, so editing a price can move past totals. The fallback order is user `modelCosts` → jawcode catalog → expected-price overlay → model-level vendor fallback, and an all-zero entry falls through to the next source in that sequence. Each rate must be a non-negative finite number at most 1,000,000 (USD per 1M tokens); out-of-range rows are rejected by the management boundary and dropped on load. Display-time estimation only: overlays never affect routing, account selection, quotas, or billing. | +| `modelCosts?` | `Record` | Per-model display prices (USD per 1M tokens), keyed by that provider's exact upstream model id — not a provider identifier or a routed `provider/model` label, e.g. `{ "deepseek-v4-flash": { "input": 0.14, "output": 0.28, "cacheRead": 0.0028, "cacheWrite": 0 } }`. Any model id is a valid key — custom providers may target any OpenAI-compatible endpoint through the `openai-chat` adapter, and local or internal provider ids work even when they are absent from the built-in catalogs. User-configured prices win over the built-in catalogs in the Logs `~$` and Usage estimates; historical entries are repriced from the current overlay, so editing a price can move past totals. The fallback order is user `modelCosts` → exact official correction → jawcode catalog → expected-price overlay → model-level vendor fallback, and an all-zero entry falls through to the next source in that sequence. Each rate must be a non-negative finite number at most 1,000,000 (USD per 1M tokens); out-of-range rows are rejected by the management boundary and dropped on load. Display-time estimation only: overlays never affect routing, account selection, quotas, or billing. | | `headers?` | `Record` | Extra upstream headers. Authorization, cookies, API-key headers, embedded newlines, and invalid names are rejected. | | `openRouterRouting?` | `OpenRouterProviderRouting` | Default OpenRouter `order`, `only`, and `allowFallbacks` preferences; valid only for canonical OpenRouter with `openai-chat`. | | `modelOpenRouterRouting?` | `Record` | Exact model-id overrides that replace the provider-wide OpenRouter preference. | @@ -151,6 +151,24 @@ contract; existing configurations see these migration deltas: Explicit capability `false` and Responses caller-tier forwarding retain their existing contracts. +### xAI Priority Processing + +The built-in `xai` preset advertises and injects Fast only when its effective transport uses +`authMode: "key"`. It sends `service_tier: "priority"` to xAI's public Chat Completions or +Responses API. `ocx login xai` uses the separate Grok CLI subscription gateway, so OAuth remains +unclassified: its catalog rows do not advertise Fast and the proxy does not inject a tier. + +xAI charges Priority Processing at 2× the standard token price for input, output, cached, and +reasoning tokens; cache discounts are applied before the multiplier. Cost estimates use that premium +only when xAI echoes `service_tier: "priority"` (or when an adapter explicitly records an assumed +priority outcome). An echoed `default` is a downgrade and stays at the standard price. + +For `grok-4.6`, the standard rate per 1M tokens is $2.00 input, $0.50 cached input, and $6.00 +output. A prompt of at least 200,000 tokens reprices the whole request at $4.00 / $1.00 / $12.00. +xAI has not published how that long-context band combines with Priority Processing. When a +long-context response confirms `priority`, the dashboard therefore shows the published long-context +cost with a `≥` marker and a lower-bound explanation; it never invents a stacked multiplier. + API-key providers may hold a literal key or an environment reference. OAuth providers use the credential store populated by `ocx login`; subscription-backed Claude Code launch behavior is configured under [`claudeCode.authMode`](/reference/configuration/server/#claude-code). diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 976d81c702..c6851ca73f 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -711,6 +711,7 @@ export const de: Record = { "logs.detail.estimate.cache_detail_missing": "Cache-Details fehlen; Eingabe ist als Obergrenze geschätzt.", "logs.detail.estimate.expected_price_overlay": "Ein verifizierter Expected-Listenpreis wurde verwendet.", "logs.detail.estimate.provider_cost_overlay": "Ein vom Anbieter konfiguriertes Preis-Overlay wurde verwendet.", + "logs.detail.estimate.priority_lower_bound": "xAI hat keinen kombinierten Preis für Priority und langen Kontext veröffentlicht; die angezeigten Kosten sind eine Untergrenze.", "logs.col.error": "Fehler", "logs.col.upstreamReason": "Upstream-Grund", "logs.col.duration": "Dauer", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 9ed230f6dc..3ce843e9ff 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -744,6 +744,7 @@ export const en = { "logs.detail.estimate.cache_detail_missing": "Cache details were unavailable; input is an upper-bound estimate.", "logs.detail.estimate.expected_price_overlay": "A verified expected list price was used.", "logs.detail.estimate.provider_cost_overlay": "A provider-configured price overlay was used.", + "logs.detail.estimate.priority_lower_bound": "xAI has not published a combined Priority and long-context price; the displayed cost is a lower bound.", "logs.col.error": "Error", "logs.col.upstreamReason": "Upstream reason", "logs.col.duration": "Duration", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index 20e9311cb9..10e2b9b281 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -725,6 +725,7 @@ export const fr: Record = { "logs.detail.estimate.cache_detail_missing": "Les détails du cache n’étaient pas disponibles ; l’entrée est une estimation de la limite supérieure.", "logs.detail.estimate.expected_price_overlay": "Un tarif catalogue attendu et vérifié a été utilisé.", "logs.detail.estimate.provider_cost_overlay": "Un remplacement de tarif configuré pour le fournisseur a été utilisé.", + "logs.detail.estimate.priority_lower_bound": "xAI n’a pas publié de tarif combinant Priority et contexte long ; le coût affiché est une borne inférieure.", "logs.col.error": "Erreur", "logs.col.upstreamReason": "Motif en amont", "logs.col.duration": "Durée", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index ce7f4d1642..a7c2d31212 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -687,6 +687,7 @@ export const ja: Record = { "logs.detail.estimate.cache_detail_missing": "キャッシュの詳細が利用できませんでした; 入力は上限の推定です。", "logs.detail.estimate.expected_price_overlay": "検証済みの予想定価が使用されました。", "logs.detail.estimate.provider_cost_overlay": "プロバイダー設定の価格オーバーレイが使用されました。", + "logs.detail.estimate.priority_lower_bound": "xAI は Priority と長いコンテキストの組み合わせ価格を公開していないため、表示額は下限です。", "logs.col.error": "エラー", "logs.col.upstreamReason": "上流の理由", "logs.col.duration": "所要時間", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 2909a498c1..95dc62227f 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -730,6 +730,7 @@ export const ko: Record = { "logs.detail.estimate.cache_detail_missing": "캐시 상세가 없어 입력 전액을 상한으로 추정했습니다.", "logs.detail.estimate.expected_price_overlay": "검증된 expected 정가를 사용했습니다.", "logs.detail.estimate.provider_cost_overlay": "프로바이더 구성 가격 오버레이를 사용했습니다.", + "logs.detail.estimate.priority_lower_bound": "xAI가 Priority와 긴 컨텍스트의 결합 가격을 공개하지 않아 표시 비용은 하한입니다.", "logs.col.error": "오류", "logs.col.upstreamReason": "업스트림 원인", "logs.col.duration": "소요 시간", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 6bf8dca072..bd4d59991c 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -728,6 +728,7 @@ export const ru: Record = { "logs.detail.estimate.cache_detail_missing": "Детализация кэша недоступна; входные токены оценены по верхней границе.", "logs.detail.estimate.expected_price_overlay": "Использована подтверждённая ожидаемая цена из прайс-листа.", "logs.detail.estimate.provider_cost_overlay": "Использован ценовой оверлей провайдера.", + "logs.detail.estimate.priority_lower_bound": "xAI не опубликовала совмещённую цену Priority и длинного контекста; показанная стоимость является нижней границей.", "logs.col.error": "Ошибка", "logs.col.upstreamReason": "Причина от провайдера", "logs.col.duration": "Длительность", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index 3abaaaef6c..d7ab493cb1 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -735,6 +735,7 @@ export const tr: Record = { "logs.detail.estimate.cache_detail_missing": "Önbellek detayları eksik.", "logs.detail.estimate.expected_price_overlay": "Doğrulanmış liste fiyatı kullanıldı.", "logs.detail.estimate.provider_cost_overlay": "Kullanıcı tarafından yapılandırılan bir sağlayıcı fiyat katmanı kullanıldı.", + "logs.detail.estimate.priority_lower_bound": "xAI, Priority ile uzun bağlamın birleşik fiyatını yayımlamadı; gösterilen maliyet alt sınırdır.", "logs.col.error": "Hata", "logs.col.upstreamReason": "Yukarı akış nedeni", "logs.col.duration": "Süre", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 869b863370..994a155ff9 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -1805,6 +1805,7 @@ export const zhTW: Record = { "logs.detail.attempt.recovery.emptyCompletion": "空白完成重試", "logs.detail.attempt.recovery.unknown": "未知的復原原因", "logs.detail.estimate.provider_cost_overlay": "已使用供應商設定的價格覆蓋。", + "logs.detail.estimate.priority_lower_bound": "xAI 尚未公布 Priority 與長上下文的組合價格;目前顯示的是費用下界。", "pws.cockpitImportDescription": "從此裝置匯入 Cockpit Tools Antigravity JSON 匯出檔。不會顯示檔案內容。", "pws.cockpitImportFileLabel": "Cockpit Tools Antigravity JSON 匯出檔", "pws.cockpitImportChooseFile": "選擇 JSON 檔案", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index de6897e351..666d9ca31b 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -723,6 +723,7 @@ export const zh: Record = { "logs.detail.estimate.cache_detail_missing": "缺少缓存明细;输入费用按上限估算。", "logs.detail.estimate.expected_price_overlay": "使用了已验证的 Expected 标价。", "logs.detail.estimate.provider_cost_overlay": "使用了用户配置的提供方价格覆盖。", + "logs.detail.estimate.priority_lower_bound": "xAI 尚未公布 Priority 与长上下文的组合价格;当前显示的是费用下界。", "logs.col.error": "错误", "logs.col.upstreamReason": "上游原因", "logs.col.duration": "耗时", diff --git a/gui/src/pages/Logs.tsx b/gui/src/pages/Logs.tsx index b7acbb4d02..afd722de2b 100644 --- a/gui/src/pages/Logs.tsx +++ b/gui/src/pages/Logs.tsx @@ -15,6 +15,7 @@ import Debug from "./Debug"; import type { LogsTab } from "./logs-tab-keydown"; import { logsTabKeyDown, readTabFromHash, selectLogsTab } from "./logs-tab-keydown"; +import { formatEstimatedUsdTotal } from "./logs-cost-format"; import { modelTitle } from "./logs-model-title"; import { speedLabel } from "./logs-speed-label"; import { cacheSplit, isCursorUsageProvider, tokensTitle } from "./logs-token-title"; @@ -53,7 +54,8 @@ type CostEstimateReason = | "usage_estimated" | "cache_detail_missing" | "expected_price_overlay" - | "provider_cost_overlay"; + | "provider_cost_overlay" + | "priority_lower_bound"; type TokPerSecondResult = | { kind: "value"; value: number; estimated: boolean } @@ -238,20 +240,16 @@ function formatTokPerSecond(result: TokPerSecondResult | undefined, localeTag?: } function formatEstimatedUsd(result: CostResult | undefined, localeTag?: string): string { - if (!result || result.kind === "unavailable" || !Number.isFinite(result.estimate.cost.total) || result.estimate.cost.total < 0) return "\u2014"; - const totalUsd = result.estimate.cost.total; - return `~$${new Intl.NumberFormat(localeTag, { - minimumFractionDigits: 4, - maximumFractionDigits: 4, - }).format(totalUsd)}`; + if (!result || result.kind === "unavailable") return "\u2014"; + return formatEstimatedUsdTotal( + result.estimate.cost.total, + result.estimateReasons.includes("priority_lower_bound"), + localeTag, + ); } -function formatEstimatedUsdValue(value: number, localeTag?: string): string { - if (!Number.isFinite(value) || value < 0) return "\u2014"; - return `~$${new Intl.NumberFormat(localeTag, { - minimumFractionDigits: 4, - maximumFractionDigits: 4, - }).format(value)}`; +function formatEstimatedUsdValue(value: number, localeTag?: string, lowerBound = false): string { + return formatEstimatedUsdTotal(value, lowerBound, localeTag); } /** Consecutive failed polls before a stale table is called out. Two seconds each, so ~6s. */ @@ -273,6 +271,7 @@ const ESTIMATE_REASON_KEYS = { cache_detail_missing: "logs.detail.estimate.cache_detail_missing", expected_price_overlay: "logs.detail.estimate.expected_price_overlay", provider_cost_overlay: "logs.detail.estimate.provider_cost_overlay", + priority_lower_bound: "logs.detail.estimate.priority_lower_bound", } as const satisfies Record; /** @@ -956,11 +955,11 @@ function LogDetailDialog({ {cost?.kind === "value" ? ( <>
- {t("logs.detail.costTotal")}{formatEstimatedUsdValue(cost.estimate.cost.total, localeTag)} - {t("logs.tokens.input")}{formatEstimatedUsdValue(cost.estimate.cost.input, localeTag)} - {t("logs.tokens.cacheRead")}{formatEstimatedUsdValue(cost.estimate.cost.cacheRead, localeTag)} - {t("logs.tokens.cacheWrite")}{formatEstimatedUsdValue(cost.estimate.cost.cacheWrite, localeTag)} - {t("logs.tokens.output")}{formatEstimatedUsdValue(cost.estimate.cost.output, localeTag)} + {t("logs.detail.costTotal")}{formatEstimatedUsdValue(cost.estimate.cost.total, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.input")}{formatEstimatedUsdValue(cost.estimate.cost.input, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.cacheRead")}{formatEstimatedUsdValue(cost.estimate.cost.cacheRead, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.cacheWrite")}{formatEstimatedUsdValue(cost.estimate.cost.cacheWrite, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.output")}{formatEstimatedUsdValue(cost.estimate.cost.output, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} {cost.estimate.price && ( <> {t("logs.detail.matchedKey")} diff --git a/gui/src/pages/logs-cost-format.ts b/gui/src/pages/logs-cost-format.ts new file mode 100644 index 0000000000..65ce2e1d47 --- /dev/null +++ b/gui/src/pages/logs-cost-format.ts @@ -0,0 +1,11 @@ +export function formatEstimatedUsdTotal( + totalUsd: number | undefined, + lowerBound: boolean, + localeTag?: string, +): string { + if (totalUsd === undefined || !Number.isFinite(totalUsd) || totalUsd < 0) return "\u2014"; + return `${lowerBound ? "≥$" : "~$"}${new Intl.NumberFormat(localeTag, { + minimumFractionDigits: 4, + maximumFractionDigits: 4, + }).format(totalUsd)}`; +} diff --git a/gui/tests/logs-cost-lower-bound.test.ts b/gui/tests/logs-cost-lower-bound.test.ts new file mode 100644 index 0000000000..eddb95039e --- /dev/null +++ b/gui/tests/logs-cost-lower-bound.test.ts @@ -0,0 +1,10 @@ +import { expect, test } from "bun:test"; +import { formatEstimatedUsdTotal } from "../src/pages/logs-cost-format"; + +test("ordinary dashboard costs retain the estimate marker", () => { + expect(formatEstimatedUsdTotal(0.77, false, "en-US")).toBe("~$0.7700"); +}); + +test("priority long-context lower bounds render with a greater-than-or-equal marker", () => { + expect(formatEstimatedUsdTotal(0.77, true, "en-US")).toBe("≥$0.7700"); +}); diff --git a/src/codex/catalog/effort.ts b/src/codex/catalog/effort.ts index 3bf5daa086..0648b64d17 100644 --- a/src/codex/catalog/effort.ts +++ b/src/codex/catalog/effort.ts @@ -147,7 +147,7 @@ export function applyCatalogModelMetadata(entry: RawEntry, model?: CatalogModel) entry.service_tiers = [{ id: "priority", name: "Fast", - description: "1.5x speed, increased usage", + description: model.fastTierDescription ?? "1.5x speed, increased usage", }]; entry.additional_speed_tiers = ["fast"]; } diff --git a/src/codex/catalog/parsing.ts b/src/codex/catalog/parsing.ts index f42049150e..7ef17e6c14 100644 --- a/src/codex/catalog/parsing.ts +++ b/src/codex/catalog/parsing.ts @@ -125,6 +125,8 @@ export interface CatalogModel { supportsVerbosity?: boolean; /** Whether this exact routed model has a verified OpenAI-compatible service tier. */ supportsServiceTier?: boolean; + /** Optional provider-specific copy for the advertised Fast tier. */ + fastTierDescription?: string; supportsReasoningSummaries?: boolean; /** Normalized upstream capability names retained for management/API consumers (#485 follow-up). */ capabilities?: string[]; diff --git a/src/codex/catalog/provider-fetch.ts b/src/codex/catalog/provider-fetch.ts index 69203c8940..b5e01a8e49 100644 --- a/src/codex/catalog/provider-fetch.ts +++ b/src/codex/catalog/provider-fetch.ts @@ -34,7 +34,8 @@ import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, r import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive"; import { captureFastPolicyAuthority, - serviceTierSupportForModel, + fastPolicyForModel, + serviceTierSupportFromPolicy, } from "../../providers/service-tier"; import type { FastPolicyAuthority } from "../../providers/fastwire"; import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry"; @@ -646,8 +647,13 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig, const reasoningEfforts = configuredReasoningEfforts(prov, model.id); const defaultReasoningEffort = modelRecordValue(prov.modelDefaultReasoningEfforts, model.id) ?? model.defaultReasoningEffort; const supportsReasoningSummaries = configuredReasoningSummarySupport(prov, model.id); - const supportsServiceTier = serviceTierSupportForModel(prov, model.id, name); - const { supportsServiceTier: _staleServiceTier, ...modelWithoutServiceTier } = model; + const fastPolicy = fastPolicyForModel(prov, model.id, name); + const supportsServiceTier = serviceTierSupportFromPolicy(fastPolicy); + const { + supportsServiceTier: _staleServiceTier, + fastTierDescription: _staleFastTierDescription, + ...modelWithoutServiceTier + } = model; // 已发现窗口只允许被配置值压低;缺窗口时,已开的 Context cap 就是实际窗口。 const discoveredWindow = typeof model.contextWindow === "number" && model.contextWindow > 0 ? model.contextWindow @@ -670,6 +676,9 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig, ...(defaultReasoningEffort ? { defaultReasoningEffort } : {}), ...(typeof supportsReasoningSummaries === "boolean" ? { supportsReasoningSummaries } : {}), ...(typeof supportsServiceTier === "boolean" ? { supportsServiceTier } : {}), + ...(supportsServiceTier === true && fastPolicy.fastTierDescription !== undefined + ? { fastTierDescription: fastPolicy.fastTierDescription } + : {}), ...(prov.adapter === "kiro" ? { supportsVerbosity: false } : {}), // Default-on for openai-chat providers (explicit false opts out); other adapters // advertise only on explicit opt-in. @@ -1837,8 +1846,11 @@ async function gatherRoutedModelsUncached( ? nativeDefaultReasoningEffort(cm.modelId) : undefined; const supportsReasoningSummaries = configuredReasoningSummarySupport(rawProvider, cm.modelId); - const supportsServiceTier = effectiveProvider - ? serviceTierSupportForModel(effectiveProvider, cm.modelId, cm.provider) + const fastPolicy = effectiveProvider + ? fastPolicyForModel(effectiveProvider, cm.modelId, cm.provider) + : undefined; + const supportsServiceTier = fastPolicy + ? serviceTierSupportFromPolicy(fastPolicy) : undefined; const base: CatalogModel = { id: cm.modelId, @@ -1875,6 +1887,9 @@ async function gatherRoutedModelsUncached( ...(Array.isArray(cm.reasoningEfforts) ? { reasoningEfforts: [...cm.reasoningEfforts] } : {}), ...(cm.defaultReasoningEffort ? { defaultReasoningEffort: cm.defaultReasoningEffort } : {}), ...(typeof supportsServiceTier === "boolean" ? { supportsServiceTier } : {}), + ...(supportsServiceTier === true && fastPolicy?.fastTierDescription !== undefined + ? { fastTierDescription: fastPolicy.fastTierDescription } + : {}), }; // #962: the dedupe below drops the provider-derived row this custom row replaces. Inherit that // row's provider capability metadata (reasoning ladder, default effort, parallel tool calls, diff --git a/src/providers/fastwire.ts b/src/providers/fastwire.ts index d36a9cf19f..00e4bb198e 100644 --- a/src/providers/fastwire.ts +++ b/src/providers/fastwire.ts @@ -32,6 +32,7 @@ export type FastPolicyAuthTransport = export interface FastPolicyAuthority { readonly providerAdapter: string; readonly fastWireDeclaration: FastWire | null | undefined; + readonly fastTierDescription?: string; readonly modelWireOverrideAllowed: boolean; readonly authTransport: FastPolicyAuthTransport; readonly capability: { @@ -54,6 +55,7 @@ export interface ResolvedFastPolicy { | "pin-unavailable"; readonly adapter: string; readonly fastWire: FastWire | null; + readonly fastTierDescription?: string; readonly forwardCallerTier: boolean; } @@ -185,7 +187,16 @@ export function resolveFastPolicy( else if (capability === undefined) eligibility = "unclassified"; else eligibility = "eligible"; - return { capability, eligibility, adapter, fastWire, forwardCallerTier }; + return { + capability, + eligibility, + adapter, + fastWire, + ...(authority.fastTierDescription !== undefined + ? { fastTierDescription: authority.fastTierDescription } + : {}), + forwardCallerTier, + }; } export function canonicalFastTierMarker(callerTier: string | undefined): "priority" | undefined { diff --git a/src/providers/registry.ts b/src/providers/registry.ts index c81735ba15..a81a038add 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -217,6 +217,18 @@ export interface ProviderRegistryEntry { supportsServiceTier?: boolean; /** Registry default for exact model service-tier capability; explicit config keys win. */ modelSupportsServiceTier?: Record; + /** + * Registry-only service-tier defaults for an OAuth preset's explicit API-key transport. + * Applied only when `allowKeyAuthOverride` is true and the captured effective auth transport + * is key-based. Explicit provider config still wins field-by-field, including `false`. + */ + keyAuthServiceTier?: { + supportsServiceTier?: boolean; + modelSupportsServiceTier?: Record; + chatServiceTier?: boolean; + }; + /** Provider-specific copy for the Codex catalog's Fast tier. */ + fastTierDescription?: string; /** Registry default for plaintext reasoning replay; see `OcxProviderConfig.preserveResponsesReasoningContent`. Registry-only like `supportsServiceTier`. */ preserveResponsesReasoningContent?: boolean; /** Registry defaults for per-model Codex reasoning propagation; explicit user keys win during enrichment. */ @@ -991,6 +1003,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ baseUrl: "https://api.x.ai/v1", authKind: "oauth", allowKeyAuthOverride: true, + // Priority Processing is documented for xAI's public API-key Chat Completions and + // Responses endpoints. OAuth is a separate Grok CLI subscription gateway and remains + // unclassified; do not turn this into a provider-wide supportsServiceTier declaration. + keyAuthServiceTier: { + supportsServiceTier: true, + chatServiceTier: true, + }, + fastTierDescription: "Priority processing, 2x token price", featured: true, oauthId: "xai", jawcodeBundle: "xai", diff --git a/src/providers/service-tier.ts b/src/providers/service-tier.ts index a06c42c7b4..bfd613d4c2 100644 --- a/src/providers/service-tier.ts +++ b/src/providers/service-tier.ts @@ -59,22 +59,41 @@ function buildFastPolicyAuthority( registryTransportMatch: boolean, ): FastPolicyAuthority { const registry = registryTransportMatch ? getProviderRegistryEntry(providerName) : undefined; + const authTransport = resolveProviderAuthTransport( + provider.adapter, + provider.authMode ?? registry?.authKind ?? "key", + provider.apiKeyTransport, + ); + const keyAuthDefaults = registry?.allowKeyAuthOverride === true + && (authTransport === "authorization_bearer" || authTransport === "x_api_key") + ? registry.keyAuthServiceTier + : undefined; const authority: FastPolicyAuthority = Object.freeze({ providerAdapter: provider.adapter, fastWireDeclaration: cloneFastWire( provider.fastWire !== undefined ? provider.fastWire : registry?.fastWire, { freeze: true }, ), + ...(registry?.fastTierDescription !== undefined + ? { fastTierDescription: registry.fastTierDescription } + : {}), modelWireOverrideAllowed: !isCanonicalOpenAiForwardProvider(provider as OcxProviderConfig), - authTransport: resolveProviderAuthTransport( - provider.adapter, - provider.authMode ?? registry?.authKind ?? "key", - provider.apiKeyTransport, - ), + authTransport, capability: Object.freeze({ - ...(provider.supportsServiceTier !== undefined ? { provider: provider.supportsServiceTier } : {}), - models: Object.freeze({ ...(provider.modelSupportsServiceTier ?? {}) }), - ...(provider.chatServiceTier !== undefined ? { chatServiceTier: provider.chatServiceTier } : {}), + ...(provider.supportsServiceTier !== undefined + ? { provider: provider.supportsServiceTier } + : keyAuthDefaults?.supportsServiceTier !== undefined + ? { provider: keyAuthDefaults.supportsServiceTier } + : {}), + models: Object.freeze({ + ...(keyAuthDefaults?.modelSupportsServiceTier ?? {}), + ...(provider.modelSupportsServiceTier ?? {}), + }), + ...(provider.chatServiceTier !== undefined + ? { chatServiceTier: provider.chatServiceTier } + : keyAuthDefaults?.chatServiceTier !== undefined + ? { chatServiceTier: keyAuthDefaults.chatServiceTier } + : {}), }), modelAdapters: Object.freeze({ ...(provider.modelAdapters ?? {}) }), hardPins: captureWireAdapterHardPins(providerName), diff --git a/src/server/management/shared.ts b/src/server/management/shared.ts index 77d1f74264..3ebea685e8 100644 --- a/src/server/management/shared.ts +++ b/src/server/management/shared.ts @@ -86,7 +86,8 @@ export type CostEstimateReason = | "usage_estimated" | "cache_detail_missing" | "expected_price_overlay" - | "provider_cost_overlay"; + | "provider_cost_overlay" + | "priority_lower_bound"; export type CostResult = | { kind: "value"; estimate: NonNullable>; estimateReasons: CostEstimateReason[] } @@ -143,6 +144,7 @@ export function costResult(entry: MetricSource): CostResult { ? "expected_price_overlay" as const : undefined, estimate.price?.source === "user" || estimate.attempts?.some(a => a.price.source === "user") ? "provider_cost_overlay" as const : undefined, + estimate.priorityLowerBound ? "priority_lower_bound" as const : undefined, ].filter((reason): reason is CostEstimateReason => reason !== undefined); return { kind: "value", estimate, estimateReasons }; } diff --git a/src/usage/cost.ts b/src/usage/cost.ts index 5cf798e144..b574723902 100644 --- a/src/usage/cost.ts +++ b/src/usage/cost.ts @@ -22,7 +22,8 @@ import { activeConfiguredProviders, activeUserCostOverlays, userCostOverlayVersi import { EXPECTED_PRICE_OVERLAYS, findExpectedPriceOverlay, - resolvePriorityMultiplier, + findVerifiedPriceOverride, + findPriorityPricingRule, findContextTier, isLongContext, type Cost4, @@ -81,10 +82,12 @@ export interface AttemptCostEstimate { price: MatchedPrice; cost: CostBreakdown; estimated: boolean; - /** Applied OpenAI priority-tier multiplier (undefined or 1 = standard). */ + /** Applied provider priority-tier multiplier (undefined or 1 = standard). */ priorityMultiplier?: number; /** Set when the published long-context rate was applied (#908). */ contextTier?: ContextTierName; + /** Confirmed priority + long context has no published combined rate; this cost is a floor. */ + priorityLowerBound?: true; } export interface CostEstimate { @@ -93,10 +96,12 @@ export interface CostEstimate { estimated: boolean; attempts?: AttemptCostEstimate[]; price?: MatchedPrice; - /** Applied OpenAI priority-tier multiplier (undefined or 1 = standard). */ + /** Applied provider priority-tier multiplier (undefined or 1 = standard). */ priorityMultiplier?: number; /** Set when any priced attempt used the published long-context rate (#908). */ contextTier?: ContextTierName; + /** Set when any priced attempt is only a known lower bound. */ + priorityLowerBound?: true; } function finiteNonNegative(value: number): boolean { @@ -232,7 +237,7 @@ function resolveMatchedPriceInner( /** * Exact provider/model price lookup: user-configured `modelCosts` first, then - * the jawcode provider bundle, then the expected-price overlay, then the + * an exact official correction, the jawcode provider bundle, the expected-price overlay, then the * model-level vendor fallback. All-zero rows fall through ("not billable"). */ function resolveMatchedPriceExact( @@ -245,6 +250,20 @@ function resolveMatchedPriceExact( // operator's explicit price is authoritative for the ~$ estimate. const userOverlay = userOverlayMatch(provider, modelId, userOverlays); if (userOverlay) return userOverlay; + const verifiedOverride = overlays === EXPECTED_PRICE_OVERLAYS + ? findVerifiedPriceOverride(provider, modelId) + : undefined; + if (verifiedOverride && validCost4(verifiedOverride.cost4) && hasNonZeroCost(verifiedOverride.cost4)) { + return { + provider, + modelId, + cost4: verifiedOverride.cost4, + source: "expected", + sourceRef: verifiedOverride.source, + verifiedAt: verifiedOverride.verifiedAt, + status: "verified", + }; + } const metadataProvider = resolveMetadataProvider(provider); const bundled = metadataProvider ? getModelMetadata(metadataProvider, modelId) @@ -316,13 +335,6 @@ function isEstimated(usage: OcxUsage, usageStatus: UsageStatus, priceStatus: Exp return usage.estimated === true || usageStatus === "estimated" || priceStatus === "verified-derived"; } -/** - * OpenAI provider ids eligible for service_tier "priority" price multipliers. - * Only canonical OpenAI forward providers use the priority tier; routed providers - * (OpenRouter, Cursor, etc.) may share model slugs but have independent pricing. - */ -const OPENAI_TIER_PROVIDER_IDS = new Set(["openai", "openai-apikey"]); - /** * Resolve the effective service tier from persisted log fields. * Priority: responseServiceTier (server-confirmed) > requestedServiceTier @@ -399,9 +411,9 @@ function isConfirmedFast(tier?: ServiceTierInput): boolean { * normalized billable input — normalization subtracts cache read/write, so a * cache-heavy long prompt would fall below the boundary and under-bill. * - * Skipped entirely for a response-confirmed Fast request: OpenAI does not serve - * long context in Fast mode, so the two are mutually exclusive regimes rather - * than composable multipliers. + * A provider's declaration decides how a response-confirmed priority tier relates to this band. + * OpenAI declares the bands exclusive. xAI publishes neither a combined rate nor an exclusion, + * so its long-context rate remains the known lower bound instead of inventing a stacked multiplier. */ function applyContextTier( cost4: Cost4, @@ -409,25 +421,27 @@ function applyContextTier( modelId: string, rawInputTokens: number | undefined, tier?: ServiceTierInput, -): [Cost4, ContextTierName | undefined] { - if (rawInputTokens === undefined) return [cost4, undefined]; - if (isConfirmedFast(tier)) return [cost4, undefined]; +): [Cost4, ContextTierName | undefined, boolean] { + if (rawInputTokens === undefined) return [cost4, undefined, false]; const rule = findContextTier(baseProviderLabel(provider), modelId); - if (!rule || !isLongContext(rule, rawInputTokens)) return [cost4, undefined]; + if (!rule || !isLongContext(rule, rawInputTokens)) return [cost4, undefined, false]; + const confirmedFast = isConfirmedFast(tier); + if (confirmedFast && rule.confirmedPriorityRelation === "exclusive") { + return [cost4, undefined, false]; + } return [{ input: cost4.input * rule.multiplier.input, output: cost4.output * rule.multiplier.output, cacheRead: cost4.cacheRead * rule.multiplier.cacheRead, cacheWrite: cost4.cacheWrite * rule.multiplier.cacheWrite, - }, "long"]; + }, "long", confirmedFast && rule.confirmedPriorityRelation === "lower-bound"]; } /** - * Apply the OpenAI priority-tier multiplier to a Cost4 when applicable. + * Apply a declared provider/model priority-tier multiplier to a Cost4 when applicable. * Returns [effectiveCost4, multiplier]. Multiplier is 1 (no-op) when: * - serviceTier is not "priority" - * - provider is not a canonical OpenAI forward provider - * - model is not in PRIORITY_MULTIPLIERS + * - no exact provider/model rule exists */ function applyPriorityMultiplier( cost4: Cost4, @@ -437,8 +451,7 @@ function applyPriorityMultiplier( ): [Cost4, number] { if (tierScalar(serviceTier) !== "priority") return [cost4, 1]; const base = baseProviderLabel(provider); - if (!OPENAI_TIER_PROVIDER_IDS.has(base)) return [cost4, 1]; - const multiplier = resolvePriorityMultiplier(modelId); + const multiplier = findPriorityPricingRule(base, modelId)?.multiplier ?? 1; if (multiplier === 1) return [cost4, 1]; return [{ input: cost4.input * multiplier, @@ -467,12 +480,12 @@ export function estimateAttemptCost( const attemptServiceTier = attempt.tierOutcome ? serviceTierContextFromOutcome(attempt.tierOutcome) : serviceTier; - const [tieredCost4, contextTier] = applyContextTier( + const [tieredCost4, contextTier, priorityLowerBound] = applyContextTier( price.cost4, attempt.provider, attempt.model, attempt.usage.inputTokens, attemptServiceTier, ); - // Exclusive both ways: if the long rate applied, the request was NOT served as - // Fast (Fast does not support long context), so the Fast multiplier must not - // also apply — otherwise a downgraded request bills at both rates. + // A published long-context row owns the numeric estimate. OpenAI declares that band + // exclusive with Fast; xAI's confirmed combination is deliberately left unmultiplied + // and marked as a lower bound because no combined price has been published. const [effectiveCost4, multiplier] = contextTier ? [tieredCost4, 1] as const : applyPriorityMultiplier(tieredCost4, attempt.provider, attempt.model, attemptServiceTier); @@ -486,6 +499,7 @@ export function estimateAttemptCost( estimated: isEstimated(attempt.usage, attempt.usageStatus, price.status), ...(multiplier !== 1 ? { priorityMultiplier: multiplier } : {}), ...(contextTier ? { contextTier } : {}), + ...(priorityLowerBound ? { priorityLowerBound: true as const } : {}), }; } @@ -528,6 +542,7 @@ export function estimateComboCost( ? { priorityMultiplier: estimates.find(est => est.priorityMultiplier)?.priorityMultiplier } : {}), ...(estimates.some(est => est.contextTier) ? { contextTier: "long" as const } : {}), + ...(estimates.some(est => est.priorityLowerBound) ? { priorityLowerBound: true as const } : {}), }; } @@ -548,7 +563,7 @@ export function estimateRequestCost( if (!tokens) return null; const price = resolveMatchedPrice(input.provider, input.model, overlays, userOverlays); if (!price) return null; - const [tieredCost4, contextTier] = applyContextTier( + const [tieredCost4, contextTier, priorityLowerBound] = applyContextTier( price.cost4, input.provider, input.model, input.usage.inputTokens, input.serviceTier, ); const [effectiveCost4, multiplier] = contextTier @@ -561,6 +576,7 @@ export function estimateRequestCost( estimated: isEstimated(input.usage, input.usageStatus, price.status), ...(multiplier !== 1 ? { priorityMultiplier: multiplier } : {}), ...(contextTier ? { contextTier } : {}), + ...(priorityLowerBound ? { priorityLowerBound: true as const } : {}), }; } diff --git a/src/usage/expected-prices.ts b/src/usage/expected-prices.ts index 64a42e87e7..d0cb90d139 100644 --- a/src/usage/expected-prices.ts +++ b/src/usage/expected-prices.ts @@ -181,6 +181,30 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [ { provider: "cursor", modelId: "auto", cost4: { input: 1.25, output: 6, cacheRead: 0.25, cacheWrite: 1.25 }, source: "https://docs.cursor.com/account/pricing + https://cursor.com/blog/aug-2025-pricing", verifiedAt: "2026-07-20", status: "verified" }, ]; +/** + * Exact official corrections for stale nonzero catalog rows. These are intentionally separate + * from fallback overlays: they win over the bundled row only for the declared provider/model and + * therefore cannot reprice routed resellers that reuse the same model slug. + */ +export const VERIFIED_PRICE_OVERRIDES: readonly ExpectedPriceOverlay[] = [ + { + provider: "xai", + modelId: "grok-4.6", + cost4: { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 }, + source: "https://docs.x.ai/developers/pricing", + verifiedAt: "2026-08-18", + status: "verified", + }, +]; + +export function findVerifiedPriceOverride( + provider: string, + modelId: string, + overrides: readonly ExpectedPriceOverlay[] = VERIFIED_PRICE_OVERRIDES, +): ExpectedPriceOverlay | undefined { + return overrides.find(row => row.provider === provider && row.modelId === modelId); +} + /** * Exact-key overlay lookup. Returns verified first, then verified-derived. * NEVER returns "unverified" rows — fail-closed is enforced in code, not just docs. @@ -196,12 +220,7 @@ export function findExpectedPriceOverlay( ?? exact.find(row => row.status === "verified-derived"); } -/** - * OpenAI Fast mode (`service_tier=priority`) price multipliers by model slug. - * Source: https://openai.com/api-fast-mode/ (2026-07-31). - * Fast pricing applies uniformly to all token types (input, output, cache). - * Models not listed here fall back to 1× (no multiplier). - */ +/** OpenAI Fast price multipliers retained as a compatibility export. */ export const PRIORITY_MULTIPLIERS: Readonly> = { "gpt-5.6-sol": 2, // Post-price-cut Fast tables (https://openai.com/api-fast-mode/, 2026-08-05): @@ -219,6 +238,49 @@ export function resolvePriorityMultiplier(modelId: string): number { return PRIORITY_MULTIPLIERS[modelId] ?? 1; } +export interface PriorityPricingRule { + provider: string; + modelId: string; + multiplier: number; + source: string; + verifiedAt: string; +} + +const OPENAI_FAST_PRICING = "https://openai.com/api-fast-mode/"; +const XAI_PRIORITY_PRICING = "https://docs.x.ai/developers/advanced-api-usage/priority-processing"; + +/** + * Exact provider/model priority premiums. Routed resellers never inherit a vendor rule merely + * because they reuse its model slug. Multipliers apply uniformly after cache discounts. + */ +export const PRIORITY_PRICING_RULES: readonly PriorityPricingRule[] = [ + ...["openai", "openai-apikey"].flatMap(provider => + Object.entries(PRIORITY_MULTIPLIERS).map(([modelId, multiplier]): PriorityPricingRule => ({ + provider, + modelId, + multiplier, + source: OPENAI_FAST_PRICING, + verifiedAt: "2026-08-05", + })), + ), + ...["grok-4.5", "grok-4.6"].map((modelId): PriorityPricingRule => ({ + provider: "xai", + modelId, + multiplier: 2, + source: XAI_PRIORITY_PRICING, + verifiedAt: "2026-08-18", + })), +]; + +/** Exact provider/model priority-pricing lookup. */ +export function findPriorityPricingRule( + provider: string, + modelId: string, + rules: readonly PriorityPricingRule[] = PRIORITY_PRICING_RULES, +): PriorityPricingRule | undefined { + return rules.find(rule => rule.provider === provider && rule.modelId === modelId); +} + /** * Long-context pricing tiers (#908). Several vendors reprice the ENTIRE request * once the prompt crosses a published input-token threshold, so a flat Cost4 @@ -244,6 +306,8 @@ export interface ContextTier { inclusive: boolean; /** Per-field factor from the short rate to the published long rate. */ multiplier: Cost4; + /** Published relationship between confirmed priority and long-context bands. */ + confirmedPriorityRelation?: "exclusive" | "lower-bound"; source: string; verifiedAt: string; } @@ -277,6 +341,7 @@ export const CONTEXT_TIERS: readonly ContextTier[] = [ thresholdInputTokens: 272_000, inclusive: false, multiplier: OPENAI_LONG_CONTEXT, + confirmedPriorityRelation: "exclusive", source: OPENAI_PRICING_DOC, verifiedAt: "2026-08-03", })), @@ -287,19 +352,21 @@ export const CONTEXT_TIERS: readonly ContextTier[] = [ thresholdInputTokens: 200_000, inclusive: true, multiplier: UNIFORM_DOUBLE, + confirmedPriorityRelation: "lower-bound", source: "https://docs.x.ai/developers/pricing", verifiedAt: "2026-08-03", }, { - // 260813: grok-4.6 long-context tier mirrored from grok-4.5; the official pricing row - // was not yet published when the model page went up, so treat as provisional. + // xAI publishes the whole-request >=200k band for grok-4.6. Its combination with + // Priority Processing is not published, so confirmed priority uses this row as a lower bound. provider: "xai", modelId: "grok-4.6", thresholdInputTokens: 200_000, inclusive: true, multiplier: UNIFORM_DOUBLE, + confirmedPriorityRelation: "lower-bound", source: "https://docs.x.ai/developers/pricing", - verifiedAt: "2026-08-13", + verifiedAt: "2026-08-18", }, { // daybreak-blue-latest aliases gpt-5.6-sol, which publishes the full long-context row diff --git a/tests/management-api-logs-metrics.test.ts b/tests/management-api-logs-metrics.test.ts index f7077a8951..0255005e2f 100644 --- a/tests/management-api-logs-metrics.test.ts +++ b/tests/management-api-logs-metrics.test.ts @@ -95,6 +95,31 @@ describe("GET /api/logs display metrics", () => { expect(dto!.displayMetrics.cost.estimateReasons).toContain("cache_detail_missing"); }); + test("confirmed xAI priority plus long context is exposed as a cost lower bound", async () => { + addRequestLog(baseEntry({ + provider: "xai", + model: "grok-4.6", + usage: { + inputTokens: 200_000, + outputTokens: 10_000, + cacheReadInputTokens: 50_000, + }, + tierOutcome: { + canonical: "priority", + wireKind: "service-tier", + wireValue: "priority", + fastOutcome: "applied", + confirmation: "confirmed", + responseServiceTier: "priority", + }, + })); + const [dto] = await readLogs(); + expect(dto!.displayMetrics.cost.kind).toBe("value"); + expect(dto!.displayMetrics.cost.estimate.priorityLowerBound).toBe(true); + expect(dto!.displayMetrics.cost.estimate.cost.total).toBeCloseTo(0.77, 9); + expect(dto!.displayMetrics.cost.estimateReasons).toContain("priority_lower_bound"); + }); + test("unmatched price is unavailable instead of zero", async () => { addRequestLog(baseEntry({ provider: "no-such-provider", diff --git a/tests/service-tier-capability.test.ts b/tests/service-tier-capability.test.ts index 2e397be86b..2cbf756548 100644 --- a/tests/service-tier-capability.test.ts +++ b/tests/service-tier-capability.test.ts @@ -7,12 +7,20 @@ * ever receiving an injection (PR #860 family). */ import { afterEach, describe, expect, test } from "bun:test"; +import { buildCatalogEntries, gatherRoutedModels } from "../src/codex/catalog"; import { providerConfigSeed, enrichProviderFromRegistry } from "../src/providers/derive"; import { getProviderRegistryEntry } from "../src/providers/registry"; +import { decideTier } from "../src/providers/fastwire"; import type { RequestLogContext } from "../src/server/request-log"; import { applyServiceTierGate, handleResponses } from "../src/server/responses/core"; -import { canForwardServiceTierForModel, serviceTierSupportForModel, supportsServiceTierForModel } from "../src/providers/service-tier"; -import { serviceTierAdapterForModel } from "../src/providers/service-tier"; +import { + canForwardServiceTierForModel, + fastPolicyForModel, + serviceTierAdapterForModel, + serviceTierSupportForModel, + serviceTierSupportFromPolicy, + supportsServiceTierForModel, +} from "../src/providers/service-tier"; import { candidateCapabilityEvidence } from "../src/routing/capability"; import { resolveProductionBehaviorValues } from "../src/routing/compatibility/behavior"; import type { OcxConfig, OcxProviderConfig } from "../src/types"; @@ -47,6 +55,92 @@ describe("registry capability reaches saved configs without overriding them", () }); }); +describe("xAI Fast capability follows the captured authentication transport", () => { + function xaiProvider( + authMode: "key" | "oauth", + overrides: Partial = {}, + ): OcxProviderConfig { + return { + ...providerConfigSeed(getProviderRegistryEntry("xai")!), + authMode, + apiKey: authMode === "key" ? "xai-test-key" : "oauth-test-token", + liveModels: false, + models: ["grok-4.6"], + ...overrides, + }; + } + + async function catalogEntry(provider: OcxProviderConfig) { + const models = await gatherRoutedModels({ + providers: { xai: provider }, + } as unknown as OcxConfig); + return buildCatalogEntries(null, [], models) + .find(entry => entry.slug === "xai/grok-4.6"); + } + + test("registry declares a key-auth overlay without classifying OAuth", () => { + const entry = getProviderRegistryEntry("xai")!; + expect(entry.keyAuthServiceTier).toEqual({ + supportsServiceTier: true, + chatServiceTier: true, + }); + expect(entry.supportsServiceTier).toBeUndefined(); + expect(entry.chatServiceTier).toBeUndefined(); + + const keyPolicy = fastPolicyForModel(xaiProvider("key"), "grok-4.6", "xai"); + expect(keyPolicy).toMatchObject({ + capability: true, + eligibility: "eligible", + forwardCallerTier: true, + fastTierDescription: "Priority processing, 2x token price", + }); + + const oauthPolicy = fastPolicyForModel(xaiProvider("oauth"), "grok-4.6", "xai"); + expect(oauthPolicy.capability).toBeUndefined(); + expect(oauthPolicy.eligibility).toBe("unclassified"); + expect(oauthPolicy.forwardCallerTier).toBe(false); + }); + + test("catalog and runtime publish the same key/OAuth conclusion", async () => { + const keyProvider = xaiProvider("key"); + const keyPolicy = fastPolicyForModel(keyProvider, "grok-4.6", "xai"); + const keyCatalog = await catalogEntry(keyProvider); + expect(serviceTierSupportFromPolicy(keyPolicy)).toBe(true); + expect(keyCatalog?.service_tiers).toEqual([{ + id: "priority", + name: "Fast", + description: "Priority processing, 2x token price", + }]); + expect(keyCatalog?.additional_speed_tiers).toEqual(["fast"]); + expect(decideTier(keyPolicy, true, undefined)).toEqual({ kind: "set", value: "priority" }); + + const oauthProvider = xaiProvider("oauth"); + const oauthPolicy = fastPolicyForModel(oauthProvider, "grok-4.6", "xai"); + const oauthCatalog = await catalogEntry(oauthProvider); + expect(serviceTierSupportFromPolicy(oauthPolicy)).toBe(false); + expect(oauthCatalog).not.toHaveProperty("service_tiers"); + expect(oauthCatalog).not.toHaveProperty("additional_speed_tiers"); + expect(decideTier(oauthPolicy, true, undefined)).toEqual({ kind: "drop" }); + }); + + test("explicit supportsServiceTier=false wins in policy and catalog for both transports", async () => { + for (const authMode of ["key", "oauth"] as const) { + const provider = xaiProvider(authMode, { supportsServiceTier: false }); + const policy = fastPolicyForModel( + provider, + "grok-4.6", + "xai", + ); + expect(policy.capability).toBe(false); + expect(policy.eligibility).toBe("capability-unsupported"); + expect(decideTier(policy, true, undefined)).toEqual({ kind: "drop" }); + const catalog = await catalogEntry(provider); + expect(catalog).not.toHaveProperty("service_tiers"); + expect(catalog).not.toHaveProperty("additional_speed_tiers"); + } + }); +}); + describe("service-tier capability is exact-model and provider-scoped", () => { test("an exact model entry overrides the provider fallback in both directions", () => { const provider: OcxProviderConfig = { @@ -237,6 +331,16 @@ describe("the gate fires on the live handleResponses path", () => { ({ ...providerConfigSeed(getProviderRegistryEntry("deepseek")!), apiKey: "sk-test" }); const openAiKeyProvider = (): OcxProviderConfig => ({ ...providerConfigSeed(getProviderRegistryEntry("openai-apikey")!), apiKey: "sk-test" }); + const xaiKeyProvider = (): OcxProviderConfig => ({ + ...providerConfigSeed(getProviderRegistryEntry("xai")!), + authMode: "key", + apiKey: "xai-test-key", + }); + const xaiOAuthProvider = (): OcxProviderConfig => ({ + ...providerConfigSeed(getProviderRegistryEntry("xai")!), + authMode: "oauth", + apiKey: "xai-oauth-test-token", + }); test("DeepSeek never receives service_tier, even with fastMode on", async () => { const body = await drive("deepseek", deepseekProvider(), "deepseek-v4-flash", {}, true); @@ -286,6 +390,23 @@ describe("the gate fires on the live handleResponses path", () => { expect(body.service_tier).toBe("flex"); }); + test("xAI API-key runtime injects priority while OAuth does not", async () => { + const keyBody = await drive("xai", xaiKeyProvider(), "grok-4.6", {}, true); + expect(keyBody.service_tier).toBe("priority"); + const oauthBody = await drive("xai", xaiOAuthProvider(), "grok-4.6", {}, true); + expect(oauthBody).not.toHaveProperty("service_tier"); + for (const provider of [xaiKeyProvider(), xaiOAuthProvider()]) { + const optedOut = await drive( + "xai", + { ...provider, supportsServiceTier: false }, + "grok-4.6", + {}, + true, + ); + expect(optedOut).not.toHaveProperty("service_tier"); + } + }); + test("an unclassified custom Responses provider keeps caller values; only explicit false strips", async () => { const custom = (): OcxProviderConfig => ({ adapter: "openai-responses", baseUrl: "https://gateway.example.com/v1", apiKey: "sk-test" }); const preserved = await drive("custom-gw", custom(), "some-model", { service_tier: "priority" }); @@ -328,4 +449,3 @@ describe("unclassified chat-wire tier projection (release-audit fix)", () => { expect(serviceTierSupportForModel(provider, "some-model")).toBeUndefined(); }); }); - diff --git a/tests/usage-cost.test.ts b/tests/usage-cost.test.ts index 11fa3d77fc..a7f58d4d88 100644 --- a/tests/usage-cost.test.ts +++ b/tests/usage-cost.test.ts @@ -1,4 +1,5 @@ import { afterEach, describe, expect, test } from "bun:test"; +import { createAdapterTierMetadata } from "../src/providers/fastwire"; import { calculateCost, estimateAttemptCost, @@ -12,8 +13,10 @@ import { import { EXPECTED_PRICE_OVERLAYS, PRIORITY_MULTIPLIERS, + PRIORITY_PRICING_RULES, CONTEXT_TIERS, findExpectedPriceOverlay, + findPriorityPricingRule, resolvePriorityMultiplier, type ExpectedPriceOverlay, } from "../src/usage/expected-prices"; @@ -564,6 +567,128 @@ describe("priority (Fast) service tier multiplier", () => { }); }); +describe("xAI Priority Processing pricing", () => { + const usage = { + inputTokens: 100_000, + outputTokens: 10_000, + cacheReadInputTokens: 20_000, + }; + + function outcome(responseServiceTier?: string) { + const tracker = createAdapterTierMetadata( + { + capability: true, + eligibility: "eligible", + fastWire: { + kind: "service-tier", + canonicalToWire: { priority: "priority" }, + foreignCallerTiers: "verbatim", + }, + demandDecision: "force-fast", + }, + { kind: "set", value: "priority" }, + "service-tier", + "priority", + )!; + if (responseServiceTier !== undefined) tracker.observeResponseServiceTier(responseServiceTier); + return tracker.outcome; + } + + function estimate(tierOutcome: ReturnType, requestUsage = usage) { + return estimateAttemptCost({ + ordinal: 1, + provider: "xai", + model: "grok-4.6", + usageStatus: "reported", + usage: requestUsage, + tierOutcome, + })!; + } + + test("xAI rules declare exact 2x premiums with official provenance", () => { + const xaiRules = PRIORITY_PRICING_RULES.filter(rule => rule.provider === "xai"); + expect(xaiRules.map(rule => rule.modelId)).toEqual(["grok-4.5", "grok-4.6"]); + expect(xaiRules.every(rule => rule.multiplier === 2)).toBe(true); + expect(xaiRules.every(rule => rule.source === "https://docs.x.ai/developers/advanced-api-usage/priority-processing")).toBe(true); + expect(findPriorityPricingRule("xai", "grok-4.6")?.multiplier).toBe(2); + expect(findPriorityPricingRule("openrouter", "grok-4.6")).toBeUndefined(); + expect(resolveMatchedPrice("openrouter", "grok-4.6")?.cost4).toEqual({ + input: 2, + output: 6, + cacheRead: 0.3, + cacheWrite: 0, + }); + expect(resolveMatchedPrice("cursor", "grok-4.6")?.cost4).toEqual({ + input: 2, + output: 6, + cacheRead: 0.3, + cacheWrite: 0, + }); + }); + + test("grok-4.6 standard and confirmed priority prices include the official cache rate", () => { + expect(resolveMatchedPrice("xai", "grok-4.6")?.cost4).toEqual({ + input: 2, + output: 6, + cacheRead: 0.5, + cacheWrite: 0, + }); + const confirmedOutcome = outcome("priority"); + const confirmed = estimate(confirmedOutcome); + expect(confirmedOutcome).toMatchObject({ + canonical: "priority", + fastOutcome: "applied", + confirmation: "confirmed", + }); + expect(confirmed.cost.total).toBeCloseTo(0.46, 9); + expect(confirmed.cost.cacheRead).toBeCloseTo(0.02, 9); + expect(confirmed.priorityMultiplier).toBe(2); + }); + + test("an assumed priority outcome uses the same 2x premium", () => { + const assumedOutcome = outcome(); + const assumed = estimate(assumedOutcome); + expect(assumedOutcome).toMatchObject({ + canonical: "priority", + fastOutcome: "applied", + confirmation: "assumed", + }); + expect(assumed.cost.total).toBeCloseTo(0.46, 9); + expect(assumed.priorityMultiplier).toBe(2); + }); + + test("an echoed default records a downgrade and bills the standard price", () => { + const downgradedOutcome = outcome("default"); + const downgraded = estimate(downgradedOutcome); + expect(downgradedOutcome).toMatchObject({ + fastOutcome: "downgraded", + fastDowngradeReason: "response-declined", + confirmation: "downgraded", + responseServiceTier: "default", + }); + expect(downgradedOutcome).not.toHaveProperty("canonical"); + expect(downgraded.cost.total).toBeCloseTo(0.23, 9); + expect(downgraded.priorityMultiplier).toBeUndefined(); + }); + + test("confirmed priority at 200k uses the long-context price as a marked lower bound", () => { + const long = estimate(outcome("priority"), { + inputTokens: 200_000, + outputTokens: 10_000, + cacheReadInputTokens: 50_000, + }); + expect(long.contextTier).toBe("long"); + expect(long.priorityMultiplier).toBeUndefined(); + expect(long.priorityLowerBound).toBe(true); + expect(long.cost).toMatchObject({ + input: 0.6, + cacheRead: 0.05, + output: 0.12, + }); + expect(long.cost.total).toBeCloseTo(0.77, 9); + }); +}); + describe("long-context pricing tiers (#908)", () => { const SOL: ExpectedPriceOverlay[] = [ { provider: "openai", modelId: "gpt-5.6-sol", cost4: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 }, source: "test", verifiedAt: "2026-08-03", status: "verified" }, From 057f93ea50aa82b402b1bfab1180feb8c1bbee22 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Tue, 18 Aug 2026 15:18:35 -0700 Subject: [PATCH 2/8] docs(devlog): capture the xAI Fast pricing UI evidence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The readiness checklist requires a screenshot for GUI changes. Three seeded grok-4.6 rows exercise every branch of the new pricing path in one view: standard, a response-confirmed priority request at exactly the documented 2x premium, and a confirmed-priority request above the long-context threshold rendering as "≥$" because xAI publishes no combined rate. Captured against a local proxy with a seeded usage log; no live xAI request was billed to produce it. Co-Authored-By: Claude Fable 5 --- .../evidence/010_logs_priority_lower_bound.png | Bin 0 -> 155108 bytes .../260818_fastwire_b2_xai/evidence/README.md | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 devlog/_plan/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png create mode 100644 devlog/_plan/260818_fastwire_b2_xai/evidence/README.md diff --git a/devlog/_plan/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png b/devlog/_plan/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ac2ea00753d4be8f7bcbc49396317eae39bdfa GIT binary patch literal 155108 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

o8>2 z7mmpdGI*tOg7$$WN%0C*F%dWIH&&qn(Pw$CE~6$rf@#{>FjJFC&C?F86liwI9M`z5 zkDn`ho`3Sye)EKnKM~`Xa5y`*BmBNpa zvHi-Sw5F3$QBjv7P%I}71b$R?AU^lvBS|OPE^Ju3I!PbRIE-OY;8@PtF5cb+HExLc z2l<7Uj#zj1_)jnATyJ|*06{{&I1m&?`D*TN78JAkG}=KC&9b}F-!`GN^h6Bsl9793g&hdRq{o$c}_kLUK|OTkFg-wcN(?b;$v$DK#5$|%%l!W*HO_|cbM*(F&hP}5(Tk&tK2;BmZ7 zDQD}0{H}BdYGG*wv(5vyA#Te=G!pe2qpt^BlDo%Sao+H5QKCTZF@o<|aX<{B#9puA zy89WJ(f8Ya?J3gOo4SKp-pOuATBnWc$jNM_3~zTp6Jn(2q@`r8gPBfB&y1T|w;{$oxion84z^||tuY`WNw52{yWse3Lkz1SZWOA8LSpqgSv%XxcI`

hx4@5;i6-rd@8l%URM z^vXpp{V1GyBCkV}9rP<|vT7iRa=J2eu^S5TO+OdkV93_R_D$^z5f=$c?_*Ji1k3lO zj*w4JkJ_yqS6DKa5OE(0a?3h2^c1`UF5>My%y)nSn~bu|tAK2&5$&Cp+r9hvp;@}7 z2`acG>=J4Gnd#d5n;1SeazzVE{CZ{}yfgiZx{fVi&Gq;r`_ECuNJzI2iOP-9yWfnwpz28~yUBJI-AnYS?#a{2eE^@=a&+EWt4*(aKP<93t6IOzv8jEeNS z$?l6*ng=G@_vupy0v&_({Pvy;YvX9nh}J~=0g0hD&fSRZU;GUA`JxzR=c(LuksFib zUFp`lwMzKgP^_ZhCSCSV%09g|H%2EP7qQ#%p8Krc=GBG4T3Wo$Aqfm*uFd*rEWk^;2v7GojqJFK>Lv= z!Rs|qksY|8>H`w`ZK*TH>R^l=*A`9M%6r^< zv`L?R?~;Xv8&xX#_j=xbxH2|gP7`X*eGzQZ?ux`%JoRJ8vYQ(*LHh|4ZsRw=XjiE} zwlOEs$B?RRzk$;99ow*vX&c&Vs~qaEOh%Uq4+IVwVs`wO$Mi>jl{lnpV2)Qi`bZSI z|NaG2?eI&IIp0*ZPR+yNHfGYT-{&>dBVky~FS_2S*zSd~Dv5m*zMG3h@21w3p@!`K z5x+8>_wgbHFZv`tf#6i(BAkG$Sg!1(Of6_~^WSX|SK{l)a#+ZvsEATM^CV2~yWeMbUMKz&t2|?k)cL2u^ zJ-MZWsrB|O1pb{7T+8;8hkZgqNk%sWczUvL(}p5@*TI*r z?P^uw)E$GOE_U{a5%3e5C0CVDhU{vn5(1m5BiEZO_K3AZ+sg}GD!;Xcs9AACDmDV8 zhO@E~90EgnHa$Df=%YGqb!y0O3PH6^=j!lFD~X8OCmbawGkyE=Y_NrnJE+MlRAWtN z<+^Iu1mdgEoCo%y5Kt^9lb2x0aeJ?FPTfT%yjJCHh_(`nE5$~w1w8G-h7HL9WoBck zK_haM_4f|hPTs!Pz_S8mthsi$$FF?GAxq4a?3DA_UXO5&PYjC&xP~L)S~Sly+~ce< zW#vU5Vj`%63&_-X7I$*slIC;`H-{RaNY#(!@nvQ(k+fopYxYRq5OjN#N7~vqR~|Gj z-A51itDf}(o!>$+yvc9O8uZ@eCdc}Nk}(z?wV+%XSPr8!i!w7j$m-RL9x1}cJ=tFR zieTPI)$WY75@GC;|AL9``$bnq(w@lPJheT&QSL~b=IlO@B|eX21}Y^)r0lmSO)jkT zHCfcI6Mx50II0fJ*+FMfU57>478OCPw!ms`J8P%Xx62k8DQ)bN7Euc}+wK|Vp?~YG z@#hYq6~N4OpbwwdIXCX#UHypqL+qh>RPG-22kY5B+*V#Uz~Oq?KA8O9_rJ6L-B`%1 z-zky;e)4ILy+w9j6&q5x!KJZygqylm!SUdZ%Wq1DbaDS*O)9~vagu|{7FTLRutl`o zp*uo8e^!uO9O5Eseu=M%1u}tBo^!?!E_eDA8Du#-P&6aS59kPhjOkVd zT+8fq@r|EBHF%MiLDz)Kt(xEyINNG+`JI(`&ZOnKn%T05d1)|Hh6pxKjC_v>le9j#7EKY@&s*#-S(f`}QEa5b4vzp4l?@TMoxOCFnoUav$)pdR0LxvHq$jGV!&)ghE> zp$$}dK;dG{eK34th(jr@m!amNz1T*du7$6Y)Cb|UXBUP7Hp0}OXbI`Uy6|Ag#ib03n%^|Eqx>gY2NRDrHErn`SAs3jjI8Gv+*9BJ z)n)v7Y50ye?(jRd#+I&hGfY`tE?<3?Z58jS-ou`EPIlngWLS+F8Xe!DAsg@Y2Qr1% z2uM_f)*JfBxd+gH2`3TH??WGqcz=X55@n7boZ6R+ZV#SC3#!5*Fu^xQ42cANmZU5T zKm=}#`?z&%?A8}IudV13U?-_Y^JZcXvm&+|`A8tf*!*vJZQ3R2{#IOFpig?J**la% zTE2`JnUQX#O18%Ul0IvxPz9p;)k89!EB$8@J! zg7Sfvi6wYGmHg z3BYBxQHJb2r)gNPRL*uv_kx`r46293J_oz{Tl}sclE*mN3*t$B_xNzPLG#!gvL+*^ zacZjv6}6V^)GJY;3d*k?!ZAMe9E09<8pmw`8QM_VxIVr zP(!l=eU!2H*@=c!)Cdkx=?B{uI+p@4f|uIt)^N5W6^VLq;!xd>e~g!J4XN~avU$ik z5GZAWGRGf>gzXXsI&kBFIGpqi6asZNh`L*iw}K%*^2DegXQ^~U`#I}B?O{K6ONM?^ z@%ggHxYlSA(gv}y}DL}KCnD!>nZ2ILOb6Bz`nAjamk;;s@VPZ z?gB>Dt34%Z?3#a_lZbwQF~9h5J(mVtwI~-3%04r0QXjb~J3-4vrP270SyyA131b)J{x%na(LG zPG(Zn-WOwNyIzzeQnDayPsBu&37(PJ35h|?zf-!{6p>yiVfjNQsryJ~2Wz=3J2N;E<6^$ERV zh}Aj8P@cGl%A+&DpnEj*fu5SLF~Se>nKenKb*L`c9KnY+pI!#Cbxha)J>`sPP-gw55~jN(Jzq0=Gfe>ahdPMA$~cZWumOP>mb$0`|Nx3 z=V{2$ewXb*tmnOryHy16yw{! zETbnMfOt<2lwHt~l%x+NOfr2ncLuhnQ|}to1H^*3=+f~_8a}g8eYAnQSSjit9LKn7 zz%0<@vZsx=2^>!!e0P~MsWqFfefQ`7>gd_stFd;)N2LZFVn&;l)8*OOSwNY?*th<0 z_xt7Ef^oO{Wvg9ZM31n=Y|p;}Gckaq@Sg$@Et-&ao+SJOA?;&KcdH?=g~g&p`G-%r-`!+YtYG}s8` zcq?)Mb;u9a2%>aQ@0m=Z6^0GD{)Mbu%LAx&FQj`4f9!KsFZm?ss*Pc zyL)W3JJ#j;hkt0ZA1S$Y0|8}K5m4-fEv}a}I87tSIn*krw~ z^R=IiO2^cmylz#PulZAKU#xD>VWG$NXO#dH*kA6tOv zCHg*5;^0J@EuW6dP2VoH739hwn4@-V*qRApu*OHw48Rzf$`&t0mb9S+pV~ZEEWF=I znsFyIu__bTReY1ZzxnrWnw=as6At9VvTwN{uArT=Jdc=@ub&iq{E&ezE|pUiY_C7A zLZckJ-duG*TqF*}l5!wdTI070>|49WEg>gBt%YQJ`kpyu<)7hqWR*7yhP=6cT} zUkUiHZ`LGvppu+QXK-Bathb0P9um)MinQ9}g_yD1)fM>{uco^q#LAx?YbZP`kntZ) z1vxuAXJi0ekwd__gaXXtz-ts|Pl54J9>{tAr}s^vBqGlcZjXU0L47vmm!p$rSU4Pu zoT;#D&{hUlT90f^#kiqf@+=;DyAxoiJJMID^1}F}U;BgLpWi~nORFX?XEQ0YhE=FZ zw{7D&GxMLKI6tYtLbsthu=It6$aok~ojQrH#5Z*EZ}~YNDC3Wx8h)mb$S-}KrS3Cl z#2X{NKBAfaOjMUDl7*&ps>g_@9Fd6-J`(4r-SSlL`G;o7wccHF4otg0FVu- zXcKi1n{D@7yk2d|Eh3!^o_fchEI*7MRy&>31CQzU{Q)@xV35!1dA!%@yr~M$_#+Uh z;%e(X1Z;N#Y+$MqQ~-X!anc5G*9BgEFl}~Q@>z1oD)o$uUFAxs+rEN_HjX4YBC|>c zQssZ1e&jQ3HpovJz+#0}8EKTur@(EkgGsio`y>9TUKVVmn5nJrw=CPY^}tFq<0sFAfNEs3 zH+b-#TQ9f!X1xQrZz5e@i-@5Y|2aKeYM`as&?5nZI*+cynX)8S#WNrWC$lP&q^&|| zC1c9p-d{R9jAagSW6krusD$)Uc(xtyk%=BeaDpx9pvUDLKNM=Y&~2KY#3 zxj-*?l`T3!ywtDKEE~YZYz@th@&Y=IkmC>baMnm1?)0w>PId_n0Z!j_tc{x??QuM6 ztOR99p$1~?*ij}8@uWCzFv_lrFlx^-n| z$j06hCX)8nsnPu9%5&^CUi*dk;sNLU43R^=SY7bEkcw+Xh25Y?n!8svbY`IBMb%bZ zE;CbF!}oW3bOf%I6n_z{?)kp~O;2%{Sv^$E#l;2i!1O#E=|Cc@UR1r(O9yx%Ly5Gs z0%nDP+_si}f2^Pb=#k2bT-$sOA_%B|`h5^K?biNKiA%~7wM{ESIwOP}|*dLU*OEskF+wrW1-S&BhTG;C;_NuB|os z%%s2qJfIl+J+Fb77R0H=$shAsME_5esm7QCARyvtKQ5)S^hqTLz;P=G!vXcw!~m3# zRqLiVkHxV`O%0IZLP{SU9jJ{N@@?|w-5WopHkJ0Lp8zqledcMw;606LGnpwMEAGF) zyf0?sKLmAd?_FRcTA^-FSjGymgLewaIPRWst~2gEYNW74;|~5&8UtGCiBF250BEJg zvPHc&9Z}Vu>b}2x0VJ~r07+o9`eVLk1rV$Pu`T64dI!Kd0sK1U+2%bV@(F7_p`i)% zbkACOpsl#1Q*Z5AmO5Vl1ZvUR*8bz&x87>+-EI)i;FXKD`7g*>D}1qqW`VdA ze}{)D5SFDi>TPAT(OtBQ>Ft(}F@75bS{IG?ID3;Vv$zH$>0Bi|?~c5csitF^F7;B)1R&Pg|l37Ge_5fKTlF z);O`dCYeH(zXi@9w)-+BS23W?iqEo__nB{=~lCnvakKHxkN0hcIL@WL_gg6$YY=OH4nBZ1U-* z{@@~j=KsFg2hwyKmXxvfHxRj+t%pk+tX5Bs>3-` z+ft>w)0Pc@z7l)zQHPZ!;@1VBhX;lId%Woh{d3w9j%W7^!0xPh4G;rK8sW6m>b-M$ zI1^oD-sm*_-}hoxk5Ery2*LgZ)cr;IVpdu#Kx7-s;01QWQRve^Mu)I)130znt(O1; z9?&oYKfZfo(frtTxNqST9l(NQ+dnryNlnm-3l^6gF`jcD**;e=TK67d4zT_5j5ANt zv(~6Ifvk%|{r!*iu8v=2TwwritxlMI^8^3$5t?N z|CL56Myts;3+B4+H|uphGP)|jh1!%TYbP*xR|Ys999CoYC%O?LayPW2KX4DsJFD{L0I;ZeAZ0}G}jVTjF& z7gaRD+ql{~rHD|A;o2?DaVc0+1xO zfE_}VeX}T*1u^)&{-uT=G}Rs8uf;H2O1)nD@AY4Nnd0!>@ITrK047Uw0yu67nJ#>T zfg?7Ulv=8Nxe^)Ah#gj4E`RW=+kc*v$yZA2q^=f<`OcB9Uu5^^NgKr+(=?(HwnHeQ zHJ|!6L?A&8hmp!Y&MI;X)^0NF%LyJCh+oC*zkioBM=504Ds%M$^nFyU#mZatG0pw( zKyX}HXV(%xmcNfnKv%sE-4G3$?e|Ev@h02vN%h9G54(?+uFVV4JIZ0s>EW2UeK$;I zi~=#o1O0;Y6sQJ&ZPQ;^oE{=jKj(QumfQyo(dN&a{AQG0R8?nJa1oxhDo{yWfNLVi z9hipkTes^1_rLyOfOLZvh`2yT!K3;l7evv=4^ae~yB3c%8Hmc}&z|DYfAv65xY>&i zo2w4`?ONN06Rg}6a(;J!w+#}Que?&5|66~S=UJxoYQXJis_11JyV^;D- zhJk&)4)CKwNfmDGfZGb_oFV5}$`=*bzW`TG;at-KrS8vzP@wNVUZ@)xQn@SxR;>c# z1v!9s1kjXzY%dIrY*ST1Xb42cq%&;ZwBPGp!P6qk03)HnL5Jxz(2?QQa6sRl zEQ(saF+LHcnbO#T7P*o2ruKyb;KDQx4WRss0vDqJ1Fk`B$nCTfE(sH!BFWf$xM_{W z6%a|OcNjKt0h1BYm~-wwiaXr`8ER!yCJg3@52iNp7Jtt=ex_pj011>4u~Fd_)t$!J zZagday>NouO08>aRV{j_3@A^7=d}3Ci*eOsS4*_g+x?HBwt-{@!Co5dB`FeR!hC!L zw6vjyTcZRuRHj(et&Wo{yD3;efKWY1;b1=i*kEyzdO|%9K2}atbn)Jx)F(c@daq~n zBoAfXGp>OQqCss>fA?P-S4bcVrpk*?(<%>vF=UT25J;f33Am};fN?5tp5xO1dmfhH zS5?YK^k$Cjp9-YJY<~n1hNDKA2FnzXq_2*yqB_uK8=2(JVZ_M!fHeH-(v6YeQ7ZsQ z%GNYAf^R>H&_69+>olkOK*#NqtE6KaTXG{5G&2y>KId@47q1o4Ga6l^hjXFp_Jz$d znyCcsIy|!5-{unjk20(Jw7R1V&Vf&3=( zr3I7mf9nj&AklWNnnV6-SJdIOD9JrVJH3MfDV`u!<&OeB&m9x%_!Zq|0# z*_D3Oln=OEZ1sK#yq-=dI2WiS^fAaIg=JxhS}XLo=SFt{2DXGpxqg9A+b#v85(uktsm zkwH!j<1Oqa{Z^b)CoisRUq_yo|iQ0d7THo+tSRPN@|HepXV1 z`A?U(02H2QhF(U{f5QAVai=LX!5{z-3RvJiDI5Tcq_LzgPgicBlG(Wl1S7cs1&EkS zV=^ZD0WjIs;|rpP|6q}Sv&pm(^2Z&|xx!$T+>3uR&I`;gPZRYLZ$ftUCtxHb`JTFr zvGpq=9YdcORx8Zscd-FLF^y!|8^!VLti}@m21sGNe+!9CktH18KH8kTkm{K-h@p!;d_Vm>)*+fmC-+0*$$Xb@KAcNwH1Ph#Wc2c}!2a&Y3XyIYWBRxEn_dN?M8uzVEEb1g7cD9@L4; z=3Ev-d*PNIt7>AA9HSTBjVO-#*G56`Z`_P(Nl{j%A+(Q0UZ#^;YkJ?^WZV44ellZQ zZYaOpt&<8|A~kuyH1(N;De&3k$FBKW>)+pX{w;R~rC&KMd-Iu8a?aM_XOM+UWsl=g zv2jKel5P9DuQYzk;dpWl;UQJ0xFp@i#{>5Y{NZ@W5#l|k?8VCj&c`UZAa+>iwSa7x z8L7u>F}P|PU`bvVxfnJWQOaNT6dW!^x?QSLQP@AE{X7Klc!KTvJme;al~WIH)o(w1jebc_gwz{YEo8YpE&(n(*q}&94ON> zK>p7kro5;$;JcJeRF+ncJvsemPHY%)IzKbPXeMcRJ6(IXj>TJTjc{KXPPN_ zRP1D9^ttpHj7QJiR73}13r-^8omHe;H<<(pd=JVzoci?g)cV8=8PC^}krD)M1rHPi zWX@9<(*lP0W;7qzdcl?0B+7=rf8ZE~SC|#8Yz0N_()?wlD2z^*d0ATF#i`ot^t&V^ z90UHsO85k$G|4TInxFf@b&zREr2X$Ysw{)(p0kq|CaPF=O<+{{?MA9C|v~;L-`)yIQMqKm1Z=@6v<{J}<(XaPOxO0MNM`KM~QQx%o_ zQh5HooN@8+9uRJ_6+<6W6dx;6oPwkoPX6Gth7u7nsx!hEucf^ou&O1yx&kd624eg= z2wX-$s_r-fM&)irn@Yck%Q#i&OBiNE8ptz>Tgi2{>^Iq&U@g?>^pIXQ%*&rT$Kg#b zxre57^Ko-{zH+IaAA)hq+A0F%ohX*&%$x+{^pw#81p=;)=E!qZ?NY&1R@@+>LQ>!j zkP_!5&M)2viu$jPLn=QK2 z;jvcx!iuQ^)Pgk~nGYCM=KvgD5f*?PSOrEGtpuB9N>rBsXRBvF*R?ua77u^+cX5hB z!d@sqI+hZ)5>9^zLnYo;h7eyUe_Z}Cf(6*>ZmZL5(0b=0o~jJ8Q{k9n_rzyGRWTo~ zS3FNwT0Q~r2>_?XyQ8$kmLn5zf_D`+`UL97O`V{`8fBna@QMCu7&iIRFm@%t2iQfW z|7<_5#rpch3aI&kBR`ds!=J*VKY^H-=s-mj-E)6bb?T!;#>j#jCp0Q&q#7|*k2iV5 z)#%)bbC0eG*4zM$@U_5LhipK~LDQl2D;a&WEZKF$ZPXFvcXIUM+i!$|XUwNhk_prP z0W+PK@@ES56c`OjW7(oF$>4LeIBL-vXhk74#WNgl&F&>qE-l0vsY&+UyPb~V2ygw` z8UPik-O_H-D7h!z)8b**+Vk;UiUp9@-EJSH%kwNg{{1s#M*#U!TE5dMEE;0aNgB)D z_KO3oPXKg(D&BAT7uh5I!B1jP_4&Bk!GKnzmdtST67z>zb-Q*2?H7%(RhUTWF=xW4 z+}dGG^&gi!`ET$&w?VoW6+2n!M0}EPh^@pQ^I~FcDJuP9%`v6@F;FC(1iw9d9cBsY zc%>gwCW;4Q{|U{$beXHRq|RwgawGW(TU4!$MZIxI!Y}1&W>nBK=}>s%E`Z{STt`o4 zh(>VQ9BFbL87*yToFAo+TnTU>vM9=f#WXL=4rpB~--N2d{boQx| z#TqGK!_JqnE_hFGlGC(J7=~Q~6;a+@%VDzv!EYiHUV4X&D6wG*SEB54YY<<-n0A!Pb_A z+Yg)FNWy#Ii50ePe{PHykDKKT)X`FP08F!_q-4_s+?AaW$YAjZ^e%edcz`e`(A3>BiH|1QW5!j7qapW9@FAb}J;T;5zS?wNZkht~pk zu>YP4A*% zaDw*>zfVDj7%o`=k2sUG4=1oAy2WwwBLLSnR3Q_6@jQbL7&1f=M!^Tf;wFfns2pvQ z`+cb3p9KkR1CH>RJgP3PWiMc|Caxx^k9BT_)oXa*bfEdLlcLB@lw4`kJzzPqM64xCWU6dr!b|9`JO>h&j9`yYb(#>qY!1UF;Fym zp?abZh9z@)E@!VcMvh7!e3=}+(RtEs$n;N|(pDN~+evqB?51|Z=ZO{@z+EC+5%441 z=OW?vUEfe)R2a|KWsyfRAyU?@@Hgsye!Nx8suWu+%{qwQJJB+LJ1 z0lo+J8tn8E)~#ef<1QQ_grptdupatD7h`nPGmY_u2pS)OhAuN$(8xB|7l~^RXI~9R zWBv+RJeJ~FMGv@kqUs&&1o(@*dN@Z z5OEn&BLL~ds8>aa<7RWzpKnw{E3qCyv;fJxHTT2IX9wx{=!7dm$6VA`fGu6)swIbSbwYIMMr1-CxaR{yp%=xg*)r4uQ`aOTs&|2K} zwiwS}$fjw|l+6XlA%=wNXH$Yiyf5ScZ89-#eP?j`DAnvao<36nJ|8;mviKt1f@!DD z`%0w=9uW^0ahz&p?oos1QrcBb_VogMOn*iAu#&9YRQ5;76xbR=pVQVBwwhUB7#pVH zP)E-wYwz;T%9WOA(1+;25tDIUKO>;YrEVFg-c~=t;eaj~{ zjZ>J>Y~rz)7Njt3d3`_rNr?8V>t9V0%FkG8_*xRK4klFAHUUxJ>~2h`6uJ_n67AQ_ zdD0|fIn>02>A%O`fEh0n#=S2;{A~QBOUGYZM-!|e>R1EBs7wlVwg^FVK2tJH=hEBf zof*C95>Y{ssIRyzq=Y&M;5e6xvw0_>xcbgcDBJxrS>n7+Y4F4O2ym$KEGPZHtthtt z?rDLu+At`pgteTQ<{T_7s_TOmtM<78DxSJBsxB*5p!+jAS0WeFe10HX1Cn=vAHB;6 z6O~u{XKRk}QY)W3jf8$<^+i4f9Vv@%fs|>S5S!cTE8p%inRwtgo;<)8w)fzsZ#De{ zxrE?i!%z5ZxX5SkYVT&uGrw9Lo5ZxfFpA-R8>Mk28=6cnW zINBK7VLQ5kfWm3eP~>P{MzS8(Mh;IU!11CRfKd7T7j>A;=8MBF(s~Y}l6LrlfV@qk4m}3*l#Pp5qN%m zR+SmXGuje?y0YwRfBXFk#sga?6N=vYkp_?N=c(_c)3w;E;L4mxp_cY*rxVN1UKFUJ z)TvLpF7mFLel?eCKZ)GA8~O?}IHbv> zzcTB6|A}whZX;=nEtr_P_4aG5SShko#Oe~ww0?;6$z}1IasvCkUe%jLwV;+KJtqZk2 z#$2v^MdvmiO(s$luz%*u@NPGq^N#NI!f~3HlXCfI_9|8TAp~TN@K7c9;@*%ca(CYe zS&yMdqd9st(^qyvT=qg!cEfLc#>)AzQjM0Zxv9l3d?dB|ZsP~_xplyLQb9|zgr3s9AUpUFYr*4Qp5~F>An8s%niDBWydj!UT}p@dLuDBo z(Uri7yk-&P$U884O>e(3+K%~6^<>_$D~zU3}lBlSune47jRrNTUOe@>7P-1<=( z)*$O_aPn^m-_kV7Qp=wN&p1rd8?vgV920~J#nkGJz6cxt(`D#DKkH7B@b?HPCVxcs z*7ZELXX8s4hapH<74qr04L4tHolg>_n|qP?!xg3gg)8`PoZR1U=23oN^Ay(rPK;X*sg`*Q@b`614s@JJA`hohr6&J6`y`=HV9=pP5jl zqcR(N9p~*Sm@wSjrTyNI=w7`w-aRx5)shI6fw#ln7nIF$j*q7-TX1PK3tK-a!r+M% z@0I`PB&{;VtH`7N+V9K4xVFXwinr8!W0Tei)lM`F`EIa<@*IXVIxfu=XRY6KYAqKc;NXrNQ->Y+d^a* zJvD&UFUM)brTNTWUHD6_KD%jXDCdSJvDDCth!qy!SNYR!ip4hfPZd0o(@#i577QCh zgdbB#a`ID~zeVGd5qDdp;c0hW<Q#SPe;)_M%v4_fMmr@s+t$LE}(@p+G z{MA0goulF7n42t{V+F>{v%6E3LjZTsT!s2)$1EUOQBOP1tdK|gqnhF^nOHmo>KF&^ z)jIuU8JdH zIT?I;LxmaGQX6U5%!L_h=?9IXb7f{FS+Rw6k5@rJN|<6w%tspHEUk_gGN$~*);3;P z+SBQqD*#k-t5=MxX;hMSM@Eb3?yn-rvOa(3VNwgK9+_n5TS3i@7RaejMN!iPb8(Fqmu1jrb9LXGu zaB^<^q&u8qySRUB#KB`bJ#zP1|14MM{(6*zauAE>TN|>XIxb>vxA8Z)tDCGiE)~q* zvjVm+Z{1ReQOs68*^NdKFmEaj3@4D}#?)pD@Y{4KClPDzGLG_)^_Z@L3G}y5?RnX; zzz-sz71@0G(RbsgP=Q*EtZ?Mj<$%S)b2t5RmG~rtNTJhE`a>S`xzzZ^S5+f#MQ%|A zr})sRl7%CxH1W)^iX?~lV@I|H%gS7YLQW|%W68LFNb^?z)gE|x)y?E}!*JO2`cPFI zVRs<2WQ&gOG~VpBmx`^a!w;#I3I*uobuaixyvYei9q-ndj_h)f^(gC1JEv{$-Y|Qu z)B9pj3PIMhX#BHKOGigG@~Xq*3ve}HNd6*V$A}wYvIO2&Jcdev4`2}Jz1VWIP;3X@ z-BhaUz(mv;FP0Xa8uQ1+vU@%|Ym$Cnlor248xxOetVnWV@I3#O=4}&@FW!(B4CSJu z^`6BW2iej1jb2ZJmU@A$l-r<4(^6UpqmXoqzNMok_*Rfhw`3VRd*xXQB1K$E_h_m= zS3b-JQxJn*_@%rW8;1WfM$L>Y;|MO_=cqO*3@a+sGv%`D8aL8+Wy3GT9J>dE(k=?U zO5{S4o?)ZW9LKV6>S~}0>UIHh{HxASqgt|?H*~r4J4PImHBQ-MDU&&d<3#dB#8`Au zhvLGy5qMnux+Oze#&Ux~(mMz|J2GRTM)M4hxX85XAcB5UayA)84gPB{wjX+aqIvpo z=2itO(clf=7K09Rys>baKbgI~XHoR$Q<9n13ak zeWNOl>hbE?po0iE;uB)_QA6US50`L}QJPAhg{||iug_i+s3dvlu*F-^7;^MeO+Rwk z96s~~P_fZm5MbrGkJ37N+)Zy>Y(8xRc$`-?$K&$t_gD)$?N6cW37d8K+@ojvo`FRa zEnxbyynL-Uh5l<6*#nQP(NE`0yxJ`Z+o#m1L{>ku6N`kLlo`%}g2NwMu#e@dt7RWC zu`GB^uQ-ZM`ra9T5S?zv>vQ$*HY|^odSmW-T!O-jlZ`@)JHLXS^d`}i!;PNns8bGX z;2zEP7ZqVl>-@pxQSC6--8w0kFQUP|x>ZuwQi%jXC~ADisDa0)uqdQ)#**9jdZ`>{ z_=tH@{xat?#^ZXXlfm08au_o1sQk3Qlq~V5uE-xpSsYm%aYV)X-MvA5{Gvk9V;NQ{ z_`gi-Bk6a(-kL#|q#wiimvOX>?6&aJ5wkLmG9iJXA8&dQ4=UyFmNUI`9oqE|CF0Ij ze?r=kOL=y2ze4EF! z*hJk;qh$A5VYZ)BQ=5$+;yX^S<&GY)Bz(HKKH->OeA28UFqrB0%Jv&~GLMp}E?11q z)W2B3_^X>Hv~L*3O;J8tQdq7O>N(iV-yY+>;c!Eg%o>#;PorQYz>j2y;CFM40}U4< zn51XZR|hL)V0W~RE}vtaO;x;k{z>jo9{MBoN1C(@Dc=VjZfEwU1PY_j?w6l+j5@S5 zwz{MB-7@a8bL?YMpB?NE5sj!qAdU?SvQ5brcNSipf9R)p&T;lCgKeT$R_}WM(XMQ! zitoJSZ#-N5>xwn8n))g!1^0v?ltbjq|wmOL!4wy-y!Brl5a@ST?T@ewUh;t5flA?CQSRCqePf({R1tEowi^ zghs}NI;RPW2tQ~zhX^ll>uQCj=gxZudZbaR?@+QB#Z`gIt}0@T$3%GBCUHrCDDYai ze11T@L|4046K@tG&R;Y!|a@W4QeNd3( zc5t4BxrPpD7|up!e~SOZc5c5^y>Yz*?VU%V3D~5Z2_%?G6?jD_7)C`cVIS_vF4jx? zzR45OArvCWF+CEBGIq>LAY1ks$=I^&#;S@QaooE%)009?*kIrKz)awqR<4xu!*?keT()#0UjwA8EHE=zvvT>vNPzTQ(p)5yn#ABb!PY_~BjjbMGrn1tct6B? zNn^Mn@v%DFLAJ%qryfCrzWRQB`NQ&W=x^Kl)NKUgufkeNzmG2)f$V}?m^rg)B>bUl zh>L;5PKHsCp z;^}mJ=Z-~7*47&tp-~4BS=Rmd;)>r->j{V+V}B~P!Oe|ou0lg6z#+%1j^nJXWPI2x z{ir%_&~M1K`ssbOBm`SFkL}OiX^uc88qq);(!?P1;ZzPT)jZ|w`%QiM9PPCtvz}thahO_DJ^C zxAEMZ`|9PFmp>*b`OF%uUepE7Ao*Bt_de8XUf~)#4EWz(EKJRDczAkA%OToukKMVh z5F*b#*5`k9gRLnUZWk?_WhzLVk6E@X7RIvabIZ4`e_=^MP#3A?QdSQfSEbmFAJ4KL zOuRMVAZU=Pn)>2vH5~!!;5iN|S-WSlxjy%K#y@x&8-ArIspu`?6}E}qG)T1LoqTqt zRWUGN^_No1%4XL0xwVt=$=}mor?r`Tw)OhnI8{1@#T?H*xn|cJ6XU)w`ZO``|4!Tg zfZ6*v;Qf2IpWh$It>PTcf=C7Eq9j+y?Yiwhw++ixFdG_7+t1P&Wt(?C`gK2&zT*nT z&YPeEJ_ovAnq{Ab6N+?00n^lpZRi3Y*Rx~c-?32Ql>?mcdBW6LJ$dT>rKut(QtzmAc zruP)PJ$;98CuP$hr#h;+dV#`&?hfW|ghiqT{cT**tj!l^G){e4{3r6s)l!^K@%FOm zw%)Tn^^Mm`@XgHy#Ed zZ{d-8)O^^3TEmBaAfPqq=G4hWR`-K=loV5jWC&WXW+v;c_eDWRT_$oa87IIPqj(4X zj3&n-PZY!0jjF$?nCfR4H>6mnrbTRr=Z$})Ru?4n1@?W*Lged4C64oMdYJkTr#j!$ zVy5@jpED(1+9C-th_lhveNrg#ojxlxA)Fsus7a{kV7f}h`Sk;?VuSm~AtA+&-DvO* z+>FVyHkM|slG4OEpHUC0&RtY+-O!ZcHTzUik1O(V^o@;5VXFkgyW&3U@TCMi!pD@a z^XrA3Z?e}d*(A!a)+;{Ee33rWNa4kp5}DhdRTaH!o}ccnO)AnVfUjGz%&Z zAC>aEwfuUrBFHK4^+ZP{y@;?I)IladH-;6X=`_Mfy78y>AVKSw`>-#9^!KM`gevH3 z>r85IZw`|l>jJY2HkX=ph6eJpC#F#Hu?6!y`AHmxq`T;s!6+T%5_^C%pU z9M68dO--5SE`&?DEzGi?=172}qpu^3=|T8G%gPIfS<0DLYzOc9+*>+wsy$szG0Ge7 za*O->sHx+fe@G>_-VF;k6PVVf+rMvPIQZbXBl*5Khlo8Q-urIr4KFQq=ZA?BofT`{ zU*+!m*u>l%wDP6$+a`YC*h%kH1zGCn@*QH8UeJotN_bm9JimMJnGiuB9QMJ~JRIo0 zT^K?X`(R+~*Kza~aB5hP*8ZJLZ zq4MzX9CZC4GAc1QehGp@)4e(TuM>Y=M;~Bg5zif$Lg(l6=K@rt$fn6|*00QT5JPJ- zBu6~(X?In^CyNUaf}C2z?Bjl|l#B!Pdvi~xyc|SER%yhV!&5RXZMcuotZz>?8p!F9 zG-{FQ=sXb63X2rT-`7ynO!qSEO0b%Jr}u-BrP4CHL^ssLi7vz?bI0F|mR~-XCo6G+ zOI`a5qJp1Igi!EW0oltAR__M>P62T$nKb8h_pIdy(T!wWPJ!Ev;i};Y#&P>1VlJ-5 zYK0Z#{j7tHl}2Oik5w;h8e$*cY|p$VuS=A@;7_1>TUCY4$a{LRa|LlNZ6sDAonnGY zJ=8$?@VQcD1Kd`mlm>RpAXk|HSWh%f{m!6jb4^zl**yYPADFw{m&fl&Q4`2CW~sld zM6UJ#jee=srzo4xd{Fx#Y}*CqZ(*N$io+N2M>1cAZ{2baf?#v>GYU^+u{lD%uMa*z zHgNYruHqcORU66f1sLrLm*NPI=vNb*k^Q20er5FJAm@fx0zjlGWFM*~%cXusjZ{Uy zFM3Rue^UMQrWs;N}mN51ZHNj*H{qp_$O@VdJXlI_Qnwe>2#e=t>pfIfAXE5~_M;!lm5hKp_%vp{J6@GA~3qbfHpp3b{#96S4Q z!dbCD)n6%we0VsAj>#WYej9kl=Gzl@zbyf+;h^Q6KtC&Thf+i0elcj0p*sg5^KPc< z@bPm%7u%70W+|FED%aTG7j+xYlwr+1UxZ0Cu(`dg z54Kk#zZXkaYToCyY}qVRjwL(kbmVfSxPx6JeRv|5EE4D}N%RX|%u^`OZktkThxyU? z5$f7K&vGTJSXJ#!x%6*oNf>6O&~+xgBV)K#$Em`hVrr+UW~_!K8_t`|;NpKF9g>bU z#2tGY5`P?UVO!k5g|av%le9JG@$wwtCRKUq<#w z=Q!l~u$2#6qX|KdVY@ne!cCD6ZvOehoC*z%FK1ev_2^opt98F7(0BVJUk}0{t) zyiodi=6UII{Z0>B8!%acABtS+3yFuAkpHWv0yLRb2XLgPr$OOX7mw3;Z=$L)A{Gm7 ziyGHt(~|b~2Ux2Kbatb-5J{hZxbD zQ1)anJ?%Ii1>H~CLNY=QC4(T0&#iy;nlM4X5b}=`ho;`c2gTAxhmxF{KG($-r3`o1V5_@J};W|8mb4qCh_kLRu-KQ=v<+m3#-YpQ&ABm_Hd*;#aX)*#!2 zF?IPXP`P3b@}&cvVF^~~u-*i!5mcCMh<>b^ygJkRD09AlFXtfyhYK2vU}1fxe|7s zrP*MtJEEgXu%V}c>!216qHq)N_?Wr(^`D2HhGKHEwRcX4M zxAW812P|w&pR&?d8}L{$6w#yJDwf+u_dV1b)VvGRYGUj?Lk|P`eqbY8tVc1 zfQzw$2NYe#N&kFW45$8gxvlY%_gU;e2bpEE>StQ?rssu{b6*`~w^&XHx$1VYFXwUUn1wGU%lipZ@}&R^8I~W`CVsu{wP{5#&%(HprKs1XlD8Me2{YkTSX3H z&?hrR_R{MwJKi_(M-}X+@3o_C?H}Cu_jfw3EidM$`DCz{id&$FE|TgC4kt}X(a_?H?R`Exk-ZzL1K|M}_($z=NMrKY0tcv)j;`6CzSbItsr zwEy`8@blpe??1q`Urxpp$`N045d6>a?RUmg8=l`t467Ws?}7hh7Q;)*)v94fW&?w@zCATQI!Fv};0&v{V>yB7I*51O}K{&OYy4cfyWsRp@T zu-Tdb((fE-@$=uehipUW+w&jIZp67=(zy5zTzq6{+k{73k^f%i_XX>Nls`Rgs#qoV z#K@1NL?i2@xTOqsTmSrSj8*SZzNYG~b+O$=6!;A}u=G;yV7|SH=;BX8b*&^~cB`A>z5ez}q*EHMUx0CKm^E3bLI zhF`z7O1sb3Q$a)X6u4dEzmXbE7(MVAltp>SUnWB(?E9gP(#4$RM*;zDOd+X$C&b+f zgSp8molm3|4 zY6(Jc3bkNUW3(p9%{O58@<93*l;jAxs>hGlZh)#N1DZrx`=4C&yK;) zf|KjsA!yKg|0p{yr!Oh(&DJ9K>*$dC!H{LGFWwTA_a9#o8$E$t$4xw}iN5XMBxF&; zK#dT2^`rEV4Src#ep@ujOoe)CnF){kDs~*v6viQh-Q%qZeciE+!#;eN$IL}ES-&Du zn4=T-O!^{w(D59Mf0_!E99fZ^TV+~8rbqCYKX*s|ZASDrgE^Z5tg3h8_Ch`V`1+1p zTMEl22{zZOy?B9FkbBK-11W`N{GTYHOu$~e(^^ZHqo}GLS<*dFMQd10fP@_DSwnlOdWAh6G+|NUqF)kewV#ctJ2}H zZg%JeWl_c?UPTvZ>sj9R!OZpoXtDjHiAtk6*N6qa0MTy=a1mYuC z1z;*rj5`FcHW-Q?5;^*tPxDe%P=U9S0imHnTVgq1R<8fz!DW=Y zqPs)Q8AJt~i5GLfl0M^hQZa~uokiat5fJK`$ez6cCfY65lT-aPLW4FUt`5x~pZoJN z1-|w}WI}WPE?qn*+k-;@s@rD$QiW|vAcTK_5=H@5Gi~!B+Lcf?NFzYC23Jj$u?xDm zlbt7}TwM6KKsp4`3RyF>T?BWT)tlYyrj&ROE#2A<^_hH0Ev&U?WP0UJbC|*6h$VdZ z`QE`AZQ7ID;O0Y8OTNa~ka(jN3J)m*a4z`tSch60&B3Pn@ni7sI)VN1Zzqa;-`tQ2 zh`88!0=a9|85)^L*mt@)`cS8vy1|Y^X;b^T=B;;1MG!N>t?2T3aIUQ<#JmrJ^ zqLVZ=XP_BnxceFWQ0hm<%arB`XUL=NTYa{v`0*!j8r}*!y)7G*@SMfL?3s#ZihaHT zOjFI6zn5Ez=k4$+zP~KckELy`dGw6YGYeOSvAR1agf= zW*12h(Bb}oR`OPNC9J=u6+v&rF~VTvsp1dSIl)?>CN>b`Ef(RxqtF60-9 zGp8XHHkES(Asy?ka($dLQ^q6^8e#tR(rcbbUNID~A>Qq$)B?=k@lP({2*(DiI-aBP znm6$?u?2^fJ>d+b5h5VwA1jfke~*G-6WI=NcBc zi-PF(2u{MJNvlVSs;zm^7!MO}R zB15L&TCbys;0?(~rfHk+i>Nhl!#*C~*+3*ecSJTJ)fvG_tV=Py6bi&4kA3em& zM)vo7NRcFlMn1GP!bnW6~9Ot*hjIRD5y-%+uXSsK`Got?MWN{HG(pr$X$J)rYC7AMJ2pc5M-B{F`XR_EV?HA2spr@>{>;XPR`ESitx2^rryZo7{TG(5YXvhH`;#tqTDF_C-Q49rp6%ectAe16pB2@*f3 zEijrqK+8|E-!}n$MGoEX_zQK5YV)UdY{DH=0Mzt~QtG>_NswIa&NUV-#D!;}bBH;o zS)gx;Im%{E3i;|9ej&g(qfJARTRS=6?lVwgu*iQYGzt(MRxP_kZ*x?3VoQ8M)8oWm zydz@GsFzsL_9pmY*v%5FzSrIoZ(FZADy@h(4&Y#PO>UC#scK>P5STGKo;L5YP_Eu8 zE9*7n(#?;0iML^06sJM+-H|qdU4Spmuu52cGn3#R>r%4IgM#}ygaP;$zjg3DiH!{S z`&aOY={WhM7+cBnv&`NhE3qX@rNE@;Q6=`z95FBY0fXA^blv70Xhsm4pxq|9i8D|c zKa{b{dl<~N>0T&Fq)iZgt626s9R`!_bCnaCj1tgw;-B(YIY^)?TjAt_`s^d9PH)X22-)llkqgI)w!8&oj&clsEho2wHPL%qgdr-T7V((^jK(CI{UG>M+e5? z-qUV@x90AvBOF*A!UP-Cavu+AW=u$O9`^j8G-Gtes{O8S;jnbb8~n3esVwj)j8cCN zQY3=5(14>2a`ixi)dH{GbgW8L1(h{x@)O;H7z^P5^$aF?wFYr%Wls0!#}3NOsCKRH zq`DysFG5k%4vF+Eb$8j7Ov~Qbg?ypJxZ4CJ0^ie)Y?_IlukD~2ayTrfyiX@y2dZz7 zfl!iTt_058W_AGvFchk$A~vLE&?6BQh$Bjx`|2f`9TP_Bpv^5Q8LBcr*1kc$UMrDc znYjkb%FT^(SH4p=YM@RF(y?Nzcu=ZmvLp&0a6kRXByJoxr% z%C4U8I_KgJEmJQJCHqKj%eDsNIt1?_CVLjoU?+MRXSrLluLb64O4#I2+ztIvD73+< zVSLSn3qN+~7YNsreP2Q83MgCq*^-xS=WktC)Ld*C8E)&Tt(MJ#`7X!p@sXxl5#Ou{{vt#BP?lnINub`F#3BTxnNq2Vhd>utQW` z({IvfKlH{HE2G5#%8DD;6bgX`K*)Wc-}PhNoN`^ARcfH&R2$ZlN-oPmxR&{-4z3Z! zJlvtHAK@ySj{6X2T?;kXv{=nuj7!95|7f4nm#;;B5}Z*&=b*pH<+nuJQ1H-v+oz6m zK%jeb`2fm_obx&~m0l!oiC-qXLqV-(L2sQWXrF!p1+1Dq-cwYY$4ky}H8Li! z&bublTdI-8n^#I7$~8D*tEcpkb7<}+?y89Qsoy(4M3}IcHQ9Aqbv(i_$L|RC?@?|p z6Rfjsm$*A564qXmA^a~E@M8TvQTQLX0_vYJLzVsSeUe(gMZ96WqX57+1@^#v6A6X} zdG7|Eel2&0SMx6}{gMu7p`c7#MdfGXFdN`_bF0${nlaYT4J=%^N>8tU{(OT#s5wTS z;X)xG)v7~B?eFBPuOm5{1g3p--jsAGXAClqRf)>PlADn39g>$m;p_y|3kh5RAo-jjl9|0~K;C>lBm~izCRNKmeDSDJ|NWT)9pcW2+ii?CH;lpjP zoN6loS&Xjd9rdp&3~T`jx#-uUzvC-A$$jFH?%7b150oa@wn&amKjThN9a#hS%MNaM0XJ51J~cC+k<2r`F1-RYa9O z6nI~66{}K4SAA58wgyQ9L4KiO(uUK`N?1oxWbkYIT6u65{RO89&xP=bgH&ACl_iSK z>+=MaUNnl5PuIfJOBeP=xzGp245Tey`mJ0+TPlAtsk(h@_}W6ySiUea+hU;jI;}m+ zsSbmY00MU~Fo2l>IaZ(ly5%53b#gN~%<({>lKKn@r#b_|YWy@cqR;ut~%Ty1Wv*AspcrR$!5Gf4YTZ(b=UwGQDs zO?UO{B}BeM7JVHZ@=6Le$^l8OkJPS@$RDRqkx)^7v*M2SNs2*t zfs2q(sJC2p@wH%8W}=l|05tpvZy@ZYFjqsje5EZ6o&{HZ1~a7rpt0CSizjUnMTRKo z#bGv6qXiFOUlIwGWWCE1)NHf25E9w}px3l3ECi9>BSy;pr{QU#MfCaDL%w+^H%SPP zOXX-H2y6*|@shEaR9t5Vqdr1^{x&x%ev3&6qrg!JJowD`b_J=j90t4tU7B#vy8W&X z240!pvkvDGvN#qNN7jt22FPD<%M!n3BaV$`P2t&yG41g9Ou4?2HO`DJ9Vxd~rnebrnk)$qkg}3K5 z7#SHQ5HY@i2_Zx~Ha$>yMgD1L;2PLGPg-WnYzM+6j>9c}casHG93rqgq=Z;WT1`2o zEF6gyCmg`fAyAtW209pK$BLJLQl&g-*33eW6RxFta*jr`zQ9cuvAb2_3}_&kk}str z8BR%%KPovA%^V;kwnsUW7W^}Kx!H~ZvnA=lRBLbmoCD~AMGZbBIg_WN#lKkYV!JYl zz;Cf-(8-r%!n`gF);mLa6iCuTr}EPgwcUHUjTBoxU^_E-qXXJTz_O^GW82>Q_M)N{ zhdni;yK;d^6+3esIbRj`q>x*ON2mSOgX~`v79k?rh4tCa40-ej}* zM4$P%2X5nl3V3%Q^~rEG$Q{m(S1>C3FCtCD#TyV~Di9Ff8MNDwyTKU+u^Yd45v-i} zgWxp`tM4%<0u1moJf8E>6>tU`bgLbFRcCBuL}Hh4KC5uO(|b`wEfhvVC(ybBeU}4# zE0dW10x(6=qs3)sr?OA|yMhBOIu}y>1HcdzKni4q0zem#hZsf*VaM%tU6_1;Wm0}& z;S#*#$Y5m2Du0|6Ayx7(f}L=bq@o?1d1tsaddw@_9pNz@)s0T@5a-Ae+*kl^OcpeC z?lC&B=~Z%Z$r&BuR@TwC3cm$|UrxijHKMnrwA?e}9ikTqUTxXwOh-2|T`(AYz9p;w`z+kv77jmq`%Wihr zgHD|Ev_`am=!$^sUj&}e!8?zeT+`OIP>Ih0HEnXa#;|grW7LR5yAwia2P{nThjx418$M5fDQ#1y8QFe{X(?k>PPeugn5`k|9lmQi-;u43bI2qp=T(r`DF{8_ay%v zgltfo{^FfJ_+>RWSd(LkBC0ENs5|w~uiBs%Pji0iG4o0-pxNeHkueA?-nstwJN66d zCaf_`n=a>Qi5jITPDi%DBwNV8dp&Q< zx4UmUzy0_{{Q`PSC)j@W(-3- zB*m~4z_u7|ic%-X7SWUnfc@Cr#*5+Q?6Y_L=i748S7t1Y&bmCXSDbdCvrN-K0V(<; z`Fpv6g5-?u(p#pJDP_@s{ObJo32mM_b*FpI@?b}z(NtjWQb#5u9I+bDfTHZKH`&90 z_wRjt%gz+o@{pm+h~0T;HxHMJ6tCL*Envc|S3DA{%2J^xHA4e%w&B=Com@Sn!s7cT zGIRmWE9pNE9s{-Fb#)}iA8HH*HsIIS2;nNF#gB6-uLA8S0^nX`IUEM2pu3H)J{N)y ze>hqLk~Yx}*3E#(TtY%V1V?W81dGSo55Zj0T;~jsE|>r)YNj(lKZV45z?SVMwpEMs zNE)qZNyLRm+fZgOw5M9hV~`(b;Df-DJM%)O9~+u4kisgX0A|zyb?Gq4`dz5ckW-AD zYx>D#oPc`XzsWWMg;^vxCxYB^=oI)G>oC#y_pOiN+H`6}9sKf&8FlR7kH{^wZ+MM- zML4Sf$5X9?cLM7oj9GtxDJD=4o-6c1m1V#R4G)DPQq-e`93Zf8)IW}vK{|r5`0705 zeE~#|qm3bO$T9ybA%6S(>h}QcTd#_;|=`8lx2fukuC9k4R@V zFX*7pf5ALpLonU+shxa-8Y@hSVs2NAu?dSPD_NWvfY6(h%Xc59UNCp~zX&1%dpo*M z53edct~C~omne3BIqbrxdhfq~Qu}D=oREvl;~D)4L(E!j@Y{D;VQNvNJZuTc#EVY? zSz~0t%XRo&Yilb++|PGE`dl=EiLG4tMQeoI9e5y(1+xv7KOj980jLidlT_GurK@xD z6&mT)hkExv)i4DCwgX`~`9r7C{HoPHVRK;TC@pdBn(DCtL(p#gKc(LBM(ycAU~sC{Fo zRey&-9p4Skl@>jwOfmsA3qfE=-e!k{@c}qT&KY&N+y?pzCMGUV1`1jqFi2aQ)(W53 zHQ5UKu}FlsI*(V*3_Vf^dTG&xM(!I@AXXK7|o^zQLq}ZR(Ta|0! zI_8hX&9nGhpvm$em`NR&{*Db~?9aT>28)9lgL1h0*I|GO!;E!X8$#JNPqZsIi=KaA zYM8XhR?LS{_ZINha_=H>FV|0`YhPds0c>{2l6g;g#g=^uucs2@LvF)93JnFA3k@Wb zlSV>!Art}dha)6llFvxO4#14?>W)>aIBxc0Gb61*0bwWfOL|FWc!4}N<5}P;+XaI* z)dT{|7U$$U3jRh6M54ehm@x}B1{<~BloZ077r}LGH=CoeKs|eaTU7IVuE4}Z(hiz}ltV#>t*%%G49Rl?g zCTrF zzKoc$c^h!UULnE}fkZcvhK9Y~l-1u3Yc502$W7P8Y^xL7;sj*-=+; z42LuZwqOZtte;cGk|eV)d^l=Wp!7$^*_-MFfxZjPtVFH1zi1o9ez1rfq%*1SY;aI^M0yz_VV>H!EYC<9J~)!rnSl9i8DsY zh-~7I%}u-+kN;Lojwx{U-gbeDhPE>5b%IO24WL%7Iw3I1Rkb$4iauWez!{Ca8(^Kx zUZC7*E({blz!VR17y}tNovy}H+ky6PLX}uvjer~--ZmO=M2xxTZslKV0)Z013Pxw%(w;4|%xrTH^4ikQG7+o;lt)#kQRh{TTQp#-JR-6UflZ zK;Bu-V6(pBeo=gV-Hi60q;3K~-71<2N!nVtK`-9)}s>X^2^AsEjHv~?eX)v?ee}cW3`t2$3W*aAR7bxCsqI&jaQu@ib1otn zM~#8Lr{+$|F(m@!8!<)?fXta!U^$f}s*@3KXOza0Ds_Z0fg7p%HKcs(0Bel%7|P&aLSNRXcUrXrEw^#_&YcUa4Ikj zHBR~rxXj-7J0k?lN$*^G<}t0cgO|Y#%{O*W9IcYE!eDURlLdmlQjK?d>Wa{Lx3t(W zV9Ey{uEG?8-Latnb8;GV3DsiqY(RjKR0!B6AsYQAfr>Pl6(Bw#D?}*~LiB+@_0;>_ zCwTKDGY>sT9Zp)?8sH!^6^BC!pz*P+0gbm@w?L$6X3)j%Fc>t5&4HPN&$9kValf7`dc(Jz+^`MdB+ zG_k^j*D5=Ij2MkZh>YY{^WB{;FfZKzKFYqBp~h;^F`uGSRctu+IS@SJzEZq{ytCq| zKPy3OQ&M>^f&VIfH?!y^*Gq%2C(V#olC((3gYPTTBDG&A8J+(4WHGxOv1# zlN`brvB?paU#hhX7xhH#>>&=cwH$yVFRTVUk+R}H(q zkyJF=2?62r@I~*Uo6*hfu1Pl)rk*>>1=s;!gUe>0+S?y=aRlmO*r@wQA{8!G&7< z6;wTczxhS>tKMu#)nkIK9AaC091?xK4Uu$8;Z`R=~8 zOwiA3Z?7ilz+)!%yoy@Kb-QVs5eas|ZznT15k1Dg?d)2o>?G`@gcYJ7i?`qek zGa~tt0d<^abw!OM0}ZY26vm5ZB9_&nKn=5d6TsubFMQMgi(hqv?pKAxvcZif5J`c+ zp(M!VI{8T4i^|cg`4>r@dMkz}`K}|B6z8~i${{WG18_svcoW@_>zJ(3X)$u)duQG??T}f~iG}#f-t}p_gU=|A-@2ztQ_td7!#2qo z@YCqxm@!y8qLCKP4l56^Mwk^cq-$)sNfjj+lOOr==_pW{YB zxUYm<-xc`U_gh4Mdod5ytGLgS*fS<#YAK&rJ|E9uky6F&ljn~tduMjRMwCu5Laz8C z*a`q1eRp%Yjzj-}E_dF;B6la%Ra50)a_wXAOS?Fk(I2``zty4eJa#FHy&WvJn|O}$ z6FwXPO4H*SZ?1B1>Pm*ffH>U4-TDMMda^tDH0etPXC*=vMOJqTjhM(gG=P1n3^({* z{3mfxus}>?xwFQk5_vqWBUs)Gg~RWpKf9vQtx9cdiwsHk^ce>2-B)kvL{j}dV}OO7 z0S_G?f8G;6T#o-yN%dYJ_i4|7HS>7R?J3^vk7XaCxV}j;F8%&csD3(X7P|Kab-wobrfME&!lcdR)0v*}C3&6H?-}?HdlI1!geF zp=ATT*{AInqYwGqmg+i5njm>xqThC3=?Yg*q2RH7)B2LBM8SvlPoq~c$D-0Xb(8M! zy+1j`o)~VtD$CoMx4I@>*B}2xLdSnVbe1|q;^OMyxq*;@OfRQ_XiGg*z)%$6W@4_2 zcthZJ0o}=}^EZ)SV0tV7x;AKp0XoOaV|s}7n@RDR5eK(PjlrikfutWP6k*j-+K{`p zb_xmlp*Xw3ezO9%0dKGv%Y6B_@=GL647}z{86q@&Pm?~7McyYrfL%G8cysKNs1MPu z;y{t1+-y!BJMqn!jmB2_rJlknJX}f>IA1&Rc&fQb)JPS=F+;= zU3qu73rW>Vdi4i}z}sW2rfQB^ubZ(4ik_ea$0PX%CpFuxP}t!eyq3@-$-l}^!at5wgkTO%b+EjC6zifU#PF3deqK7o0;7;? zsiImsUXn2G)gES01(VSAuD<@?z`2^WvjO;TO38lf z-MB!7iY5LI{gds@(y-$jNo?GlG*p>)mgtXf{7$H2#F|5Fum&mF}5-h|kQs6QNBZ(|dw?a)nN z{iZCk@p%GZ3oK*0W5LWqk_+`xd|mYZ(1vx5y-{gZu!ZV68yP5Z%~PU7f=Sp3uE1WK zIFtsDD&2b_)sc<}HI2kM#;yMOh5C4NC_=Q1HH)6aJlJjppb#OYqu~hfvL6iE$d8)> zNGOTi<`y@?r-q^-L^zD`Vht|#IeCwNfk$OUGyZWe8b@54tmj+<&jsNC2f$Ww-{WT& zG1fe$yB?wxQ8iWQ{v3$~V1keO;W8%XovlGd$G);WRmm8=uYhqU2*jHbKaBp46C@uh z=glbT%fNvZQDLm|gz!ePD|*)J@ZWd0rZf(>%i9xTiT_CCsym*r8gK#uTB*Gct=9NW z(h14uNu2F1R%4_G&VIZZ9%UjCv*W#wEtoO(I~70Smou_`lUoaNkaRep6okde`L*(v(B3z zOVI=K`tGyag1fWQXu5{cuGp1N?)^+9Z--&}Ux16=6-f%7a5TBYI7iEL@GuV_VX2RK zonHUuO%(i*l!xwO+}Fb*gfNRsV@SquDEbq6VyQ5dLKpZdbY2j3ST;LE2P25hQavaL z&ky^F4JBHIY-sW+4%?h%D(^!Zi>^FLyo))0vuz8}Vli~j4Ufb;E`J~v_buLjK5(-( z7N0>!2PvNEGa_HS*>=@Ce$XCdkVe=HYXxZNIH-}{F(DZ2Bdn2CYM~aFIBu-kLN22e zJ}B43t}}&Aer|3eVJkZ%xmGn)zDAvJMnx>xeySWjDX<@hL^-Hv!sZ+=2=2qIgk}4h zg8FA5EE}zWwHUT)=Ijkg16-J8?MziBYGn)yd23@x#$CkwzaYv}idUUjfr1y9n1w^R z=&5KDSR%~Aju^+0n^~HxcW!*eJ^ZF;(}BpMjqZ%=(=U>^xr|?B_W%m36lGNRYdF=t zI1lU$#IUB)Q28Zo!fkHuv5;<-xeyKH5os~H6DFFN8DXYTZr{N8)>`VHS^i4xXOe`* zuDfx-xO=ogqtNPL6#KbBYpu*`q_9g;v(U6^Oo?m|(M|k&9_jRAuEW^nYPy0AeYUcT%?e71*=brOR7t>}*?%ii2 z338_xo0ux9Y;g)-qQfm?`TDek21(cY2eh5^cc0+aD3MVhIvMKc6;drxF=CVJbS2+?5G0-p zXnDjReT2%Af{QKGwVGbbv0r*+{F7qzL+p~amd`4|=q0`x&(sV;V8Ep#-kx58!qZ|I znbkk*gBQ;UAd_01?Nq9Rw;Yg#$G*&vLz?yJDh2GAz1aHAC~=JiDrMJMFXr>H!oQ&; zo8-8CtE<7{LqP%_55;R0RdC-r2^g|y`q*!1CwFMjSFH0sP_?B%%1+G3Tc#`qv;iW- zV`R%ZB?A@hy>z|WemY0ldlfxnb}34EnQ7D?xNP$1hK}FV6RU00iHdTDh*SBh)U#DB z-qvLuWPI-a6W2L9?!2Lm%QT(##e(rkpKjp|Khw zXVYB5$Zg2oLZ8NbBB2=bH5c1bEW3LeMW&29uDEa}3zPHSETvmv;Y=LqtO8p{v5R2d zt34Rqg%6Y&KTX5FW_@Bow-l(%HgZn>Io2v_i@KNb3WL{IaRWNQ7ivQHQ$EM9G519k z>v=HG94zdiGa4g=vb=XY-Y1hv0RV;GOf(18;Xe`7d<4OuQ)~lc_*wQovuFsA_hb&_ zwsBhgeDVC}ShxJ^t;VkJtk*906?mt&+9jT538dgUO|MWve$dC-FUsw)Tf``iBCnA? zrcJNoj>hLN4mg`1Sp7F#)NQh}8|#@U+s_OdoZF7MDed!X@j~ZGRvI~9E1W)NeYVBU zl$3|`Mtyb(ZKK0etB&$~eVoz4NrnFFR-{74KGS0M2fs5f<(vYv%mF1*yn@H9&_}tz zJ9gv+qjb2MUBkuLoTIr5AlX95eDe@etDgQC+b^89z-N`|F|(8j5YHvtgOP6GN!@+;g96-!%m$R*otF>L?f#l zPRbR4RXPp!_jX-E8$m8mvCw(MiELyZpc0c7+J0xP1#Ap9SB@puA^gKYQ&P;6e0;Wn zpqrDHL7;>-<~uOeZS;}xVv&u3*p8NSm`}$rh$+CKK_dBsu`5!0<}nXhlIa}}A81!D zKE3Y+6aBLdNzIK&DaQd3@1~jhq&UqTN=%^e1y;6s{g~y`!AdkmWS;bOG(eSz);j1a zQMIEo>BvzJ91|JDWNwJ(nCm-}=WrO&IWM`MJN zcJ+r9Y_U2WQDT1U0_x})hHisI199?a1;$3+R;|V=7eC-K5*0!(#+o0sxX?eHLFRZ38VoYpD9a)%l)dS1 z12+M6@pPi#ry;uKii~26j=sWor*`GTWTO-w-m-05_~0xjK66yU(XO9Q-=y&{9=ckK zEky0jK1v~^vo2%u%Y8CVlj$6nb24BN&bGk!7XKV>2?PnY@&iUkvAPlLb9t_ng#%Fq zj~(+1b`Si%IOnsn3<>a`kCS92i_S~v1+jTC1fC_?JHI~Hm`ZIOFaO3m z&H7=0y)a6OjI4qP_wK1lOu`Qe{z?il*10iHj=m&f;;f%+F!DO|+}s0?=UdJDf0~^_ zw$$)P0eKJdzrY8TNk0H_jA?y!{QcW~X+$LeOKpi%)>WQNl|PV8TC#_UErwxLb8Ol`Ks(qC<}< zzKMfMi|4HTFi|l|nEn5#H=eAXpQSURUJ4ZC|%T2p=%LL+N(e zntJ;750rySmi}zh+7gV|Pxa zlHs!NsxELoS9ElEeb;-;)|;>z|33CPLlIL>*!IzxYFPfZ4aI10WL_z~^!2SCJZ+syN2bR=Vf)oRz%W7Q_%gNohRmr)K$!wd8w^Kh zKkwWLroeUcG%@|$M{&`K-PhBD3cC%_p`z;8GfWS%jCNH{8S8e9aL)hGz=`Sg^#@qg z!^PP1KU}7%$t>fwc-rNkFHF4V;OZTuc-yc83ltJS8i4DwoF^_CJHML|vA{4Ueqmo1 zBjfv4tWD8cHbrkboRIZQDPau0KeIJpF9GBL0&!?Pan_8clu`V`F0J8?p5q1JQnBrs zexFogE6Hv}S9}G&wKD7G53@vm{I0=kcEDTq^UN(RW3I3mQt^eX9s8lqzI_WW=QSh` z;?YWxiagWsdPJaydH;YDZC_MWLG+|>v02JMl7l%FWzym&V5$que)@w~^++TpB?H$N z8)=Jol7FXaaDDpU;O*(LJwMA9iW7Q%i@U83c z@Ya0dDU_q)W%UDXDOynvCp6ruDiV|%CrB?zw-UKEZ%DfeV;k-DP#q_B?{I1LH(kt< zzXlmRq$tHCav%dYz>y&kYIMK{I(?gafa**7k$pP72NgFMXa$(0ETzLQ%*K83upcZU zpH&O~nEicI)K{})L2E3XW`yeP#;y4dFsr(-yn|(h=*co_XZ^TTJp(4L4<8D<84P!K z!+K`p4`GnL+Ie#4_Ug`qdwnnH?X42%|?)tr~dbLd=A%uf%9R z=!+Nl-V_4yXQk!JoWBi2K>txfTC9S|4gKGGjMD>QS_yKj{T4AVG_0%Ks=Zh8owf;4 zz)j+M5u)SMW?#M`xQ5!JNWeP2D1tKc4pQVr*jkF1QWToP--mZcop%S%uZ2Mb-nhds z_}FitD>~1WK%d7r2f(^8g~{{AjZx6#2mg924qEQ^C0EvK;e@s6S02z(0zWH|j-}@T zh!B`fF|p9!r*X*Tkox3TM=H=;0wb7bF`lcepSTj{VN8mf@4v3d-K9A#RaR#KB*RS* zYk+h<`8`F738)Udx8SDTHQ;y?FQ--_&uqBt6zj#0K#IaB^Ae1*0Lvp{Y(N%bS@wpT z_r5K7xnxlIR?#ZZv&vMUmjD?RD6 zD{-Jhtpnf$!LLcFPf)5Y;Er8Q3fTB^DSN8xQh0`W4h{RRbf2q2sMEz?g5l2wZYWt; zpU+)=IjZ~&n-FkC-_ZSG-bExL;j!e!9FcV?UQmLB5+bP0Zc!Mx8@RYTdBzd+%hnM* zV3@B%obR03A-`E`ug9tw0F1QmJ6?<5sArCAQxC^LKrhw-9Q_ffuHP)WuugA{6R_W? zpZJ4HfO=Can7UXQC-uqY$f(*jq*l;!Cdpp~=;_!P^i*Bzqo6_rM;9>XsGCVek%?rd zsaKUn!ckpL3U<_YnP#|4y+}2(DOoP=Z1& zuAz?rXQB>sxrpc%E3m0&-=o`bRzhCyv}5054Ym(uooVtY9NGOegdQeCntCT6zr6?ShWDK|XY~>?beUCD+#8^qhRU6;hKz%0^czrG$X%ka)wxyN zjp);qqZ`U@Xvz!xH!t9GgAL81A?gxwOEr^T{B(^h-X7Bfh=8ickcDm>dQC>TFw~|M z-j+UUMs7M9*4sWvGonrJzHtFn(KfD}%Ke2?bLh~vk(j#BajEFE(34cJ+eTKwE!xT) z8;mt?0<2u1);v3ddTRlU*-*XCqZgPzA2MhX5|PUGG-MK40v-qPB6lR@XLXt$B(g^M zhXDplihrZ4njhoLAX|xxk=^Ub(MBegmYA9RIfU$I(qe=t%~S~3(n=hIh$9fxyr3_O z(%l83FBm+T#QNG$Ax3>@=fik?2tdcI10q&>woyaD;-vo5WO(Quci}bPKg!DtAgq@1 zor-!TiIo9d7@h&rAcRn|g&dZ!!Ps@It(ZvxL+#-3AR}T0u-2%aLr=*LB{T=r#?u^M z{h$;wX`L4m%UFMlS91eC_I24L-W7(KgF!#C{S)$g3{X*^Y{;Y65|8#LRBVnc5Z~|v zy$h~SsU#LhSBiaP<^?epHb21ceP6F@`qjD(Y6h@Mhtz%y;vAW3P9mOZABDWAjG_o{+t#WDP*_jCu1R^I9X`&!RTl~UMN zZK>gJ(Jwt~Uvy`9CN^U#GK}JIV%B{SvbajA{teM#Z9jdck6nLrTR6b_^3<47@kz`> z*ob$w@zCzpj`(NOmb5!U;08Ts@XRf>I~o=2#8YmCa5NuIIbC(pQcK$M=X(d7Cc&XL zRObM4Z)OJx%|Ui0z|jvUf&Vy7dssID#qkanoX&=|dd(m4GN6iK_Vf_Hd1V7Zr}v-&|_l>tv2_@#Xy!tP4hF-qkJ zz~)jq*F~>o-62knurgpuhD|$cyLtzB+Gdzqp=i>$HUM&Pf6b$`PByjNsf2I|yvBLgRym&H{DzJ3N0E z3p}8u`P%RvjQk3~(*W9%4Xx^P#}bok@G(Mxx3K`^gwSX}e-nC=ezsVebeIi54}JWD z(*-pqM{c%(cOakAUjJ+h+@#@WyCCP5`>Snqa)m`j?pF^8TiF}fN6jZnC?Im3On!>% zaSYrhtN9?+gL_p~6fCBh-`T8gUe1`MC?ONh*dbTq(wvrSvn-3|bjmyR!&+ZU^3~hi z&E+XMlRqM{AR49)ssno5@Z--*1=lEVkAbqWL(xuk=Vadz5I@UbU@`tjwkjq8;$fPw zhXee_LQGZOW#}DcbH#P(#vBh`BukO*G&##gTkwyPl~v-o;0e(O+2>dC)c=vg;>0rJ zefoTibgTZCruNcx?m@FlW5P&g%0EAP=dSamEXZm2=jGbZc$XJwD4e4Ie8`AS#@D#@N8-e`By0-*5oOZP#DN4=!z+0^+ll)d&war@ z*kcef#1V7hesexm@FAA)lT4rp98ZBF-D+)W3Kz0rFh{mB#l6v8HHu3#9@d5LK-(Xh7&ka1jE%l zM=HQ*2s!Vhovq(Uf(at-g`Nqn@hygm2lIIjIQOsNv;yS+7Nx=?{1PibxdNNw`o_X3 ziZe`nS|c!~mX$a)>ri+DZ)l9nh(f4D%8ocer-Kf#rz_wYNlYG*t*6lSeJb!9*hxXI zBo#zZJPnaO)L#|YD`-qR@Y6ug~!wG!~FHAjZz6?6v~xo^!VFupvzltm&I!;B^rC2xtLlI+wG=4@eu-jrIb3 zj$hVUU!DqspMnYF??^F-*F(-`y&?C=THugjwg?1@8eA|~{PSavs6ILG0$OGi2@ej6 z4#a-^vjHrAVJGf~UU+dxTX$f&g+g9rDT+x4kC9K$Kaw9&X(LzPx&p3tirj6Q5S9n zy9O39v6(!WT2I5}CC3%~%10`pKZ|4HM>1&4 zdHp9qfdpy{{oSMgxu+by<4DgYU>_0q`al1?RqdG9Y~gY}um+7ZTt&x3aXDmh44NmH zCJe_BjWrAa))mt=lZfc8IUD0T{8S%zAG zWo3*pfTd_T;`3kY0WK_B>q*j0gIObtvjQzw*ui5U@_$qaGtoaQ;uQI^&6ijs3<03s zvv7O@S|RdJ)d8ZCx$3E9_mKc~W-?s$m5B%ce0{(!=>#x;0?$T9v@ckRfDN$rj5f%J zofK#h3}h=nC|SB;ISp+8SKWTZW;vHJXY& zI3$+Wu=QB z0-$H@&lgyQNWN5-%;6GSVBMBL3ZVZ0KoGe9oB+6SBnqmx`Uv?g;J|KfcaOER~8=IS&jCtDx z?FtO)@Mg2uX<$x|J?#*5X%C82^Gkgw^&ae#)p zvkfd($RPlW@~^383%K5SSj<3+SIohl{KrE>Bc6^|V2Zey5q)iipg(IqUJW|*XjzEM z3lEiwmMW;h&kC_f4Qau_qBa?D4l2UhTWq0m0DC0+`!_I7*#_REhf?jcG=7NLP;A}y zypRvX>j~Vei87diLcr1unLON~d*C)8C7uJDAd}RwH!!*9Wl2<-C0Ssx?Ak{`-WMOw z4doQvV${e=OiJ?Q=((!humLo+$`u(>uKbDA*Zx>-VvIYkr=f%_DO-Z=0LV*Q!odN z!dG%`>+WG#C(Q4`$D&HB3RlS60^&7nhyn*G!+XSo?8*;|2r4KoyYvVwX($2AtmNR@ zFkM5kai9i5`qq7Wnq?DsZp8jK_oVDV9=mMg^X5E&ASc6)#b92OfMz(>c{TiCWxnt` zEYqd=_r4l^?SD1Eu=*bU6Jjd%P6m)+9R#^J#q)1qX3zQ^kv8p}0nWLHwQZ%JV4i}v zlQRfeY8FGN?T!4>R|egTuQxWf##Gm`*S_c5idTfrv}!Y1aM~(g-4h?(umH>3yj6Ci z`rB`$3%X%-=Z`-6bz<3l)Mb5gG3rS$^yhOlbL>02=b&q(xd6;!Un*Ev)G6LV2ZQWP zFV;3c>nEd0wv1)qI)SdGSLe@yA(OTr1Q^`{&jrLzjGio>EiG^2DtJ>p4EPe%eqFad z0p=62eQi^4enXvp3EtU0XV+UO(VGwoR(_n>15&+GC~UvKGx|ptfW+loL=w=BYZ$b4 zbfDaEvQ%}FYs-~+fy^Tu@8O;geV&o88L5%)Z%GO5{TxQ}iQ(qesNRCG{XJx}o~Plv z@?|lTQO&Qup>@Ji^X#dT?abdlZt%ZWSLha(`c7O9d$kr)eSP!J79Fp-=GMga1$!l* zt#XoP{j#vX%3S!5jt{q_HXrlB9pKNQsm|z#$dJ7I6UwS=sH+Qb)T4-=)-0y<9+Z5D zd^H8Xi)@8j9 z@01>oXxj|a1{VBc_igY5eqn<~J53$-Xa+Q#P&#=NzCqjuHW$RUEDw=Z0H9R+m{PO& zW^j46kVSUc$2TGtQb5&hoCZ3R9dxYgc{OiDYYbjO3N4Jf;~|?9&@31fa%|z(;Xsjr zILUBlV(3(-p&h!es}~<6J<7q&-D-vtKJ8aGjoQM=XR0Xb}WMJ!S#t;EDFOO zv>+P2zV>jKA+7UfD1e7`FCbPe(M`k|uu(7x{Q)iDx?vfd)fr{2t!d&{a!7%AgGW~z zTSA@{m)}>v@9&H-|3901DCk*vs+}3gRgA20%k(-pJGm(fE4HJ`}75U|&=6Cg|aX z-1=cF3bVekGundV*Z3(;2V~MG?9N}iEYa(bS zYmb~VprnINJ(L*3;r*Ck5wRp%)2j9BjW9+4Ht27Nenr}o^G8<7v~Dcma!v)f2995V z1NEV*m-|2wgQ!%T)#DA^pDC|6rr^ddnr$UBRzvx|^EXEW zNzExgyPMI{ac{nSb9X+!gumE>mZe>Ut;g5dc>kj?&0mMN<8z_!q`|MUZT?XC(~|uf zk9JnG&~C(XU#SC8^QzIX)uRhf3iyHcxN{rqeE2*)j|V-cr>ZL z&!PGyABDOr+{jZV{CjaooCYfigGeJZ7!o;zruOU;B{rh2cgTweps$(-v6{U(ko^MdmgS|3A`~i;(YrfPi!Ysc6K$&Rfv)kPFLs z-kqb&ix{N;php!IPTuJL9Ryiwq2x~!3T6l8l18CK2-2JB96Vl}>iR6wdJNOGLzb*&DX!3o+KCTnen}?0GcI-(?_R++0Pl zVjt*^|7f}6PG6~~%wGnT@w6ZIiw+nI?R9~gFI$HGAABI-xAGeGfU4-0J91y~5b&RD zQ{RT{A>9f9YG^8MycNBl#H%@^5{HnOI%I|Ar>crVg;t^c&nF`?=)eAC64P=ocJdJf zA7BHRwAWHqTVGFX7rH`+;3oK$b=mU5|E-YQzS`}!?+ch@SvfBaWMd8I)6jPW|%CI>;SvpAPQVglxrG+gn&4UzjW0O z(2v92k&Q5D!sIVharH1{I`kfpaX3I0tH=Z^I0j>s$6~;!zW|!9`=3XaK`t35uP&qn zZEVn5zv+5fsD2Iu&@C{;-pxD&wP$L7&mT9B1o1)V>ixiVHyipWz)7!+)h@zV7{gqy z>m!9ZfTT*n95_|yBby zX-+;o3|W;%%j8U2;Tc+zi(oqt6N>;T5>Kc$vEek5=A~B2SLx`-=r)3F@fH|eWb{Q+ z8=*@XqOxomlcHFGPWsY^jEE00L9+nj-Gun4J_6FUcZ9BILa^R1c(>-N)YFr!rmGxW z0pp)?JDat;<$B1@Jhm^{3}n#qC+-v*S0#Rvl3yK)`(5Z*O}nk?rjlp=$1-jswC3Wm z#uxV{UtK{dyrtgWfhObFhdj}TR8~aQ*2X+bbo&a{4Ni-seAwhC{XL<42wsD+<(9K% zWLD9$Q92?QlB*{W2f&S8q1kPOot}qapi-G1Z_~O}j^6QfoAP>vO_CB?)#Bt{*A2lXICKiCBYnL|BCpxN)_-NuxkB%C7@}-khh@mZlnOH_{AT zlb}%YlpZm+(q6x7=J`@b&)IW*wx9ebmlrNSROS~I2TE2#L{^B1-B+iyp_UP*4~0u5 z;F9@IkQ+)v)DO7=N1klkOa(D(u)Mq|k}GPYX|Z<9Iu3F@2E*#J&FS&+w|`cJvkb)^ z23xYV1UjG_5f8l%aT)LcS@Qcol2}z&OE62v%%yPWLn1K*RkeoUnDX<<|=sZrXot5^@$sq9l-z5GD=U~xh3Dl}13ittB zsptMA`Y87&js)fh?16(@M^b0A!42{riSY$1I8EkHZ)=-uk2m#jjvb-d~}Nf(@$A4Z8VF>rTYn$W>vVGRd-)CpXA(b%oICt>TKlv<4QsGo<_dRtEHP zXB+GbTQl35vnqeu`9wuV2_umm7Iz{qX(`)49q1Ga@%tP$hmWMW3drX4UBz)I;1WG9e*+MXN~?0Hl^FDgjHQGx3^e1Q8{ zVo2u(z>M@PQ{N_)WzJ<)I17PDp^L_&v*e#)5AOna!YmtZybhcNl1^o2@pPmszmkee z4|D84O5p}>WHcl5OVQrI9Y_wHNBqHSti!zN{3Taa>`H7XBj}_Jn+jM!$3ea;{pryO zz0w_Mnhkah>Kx*ZlI#G#eb`W@A`G=i@gtj^ApLF z-Hnc-ZxrRhyPYG&>riJaIS8DW&(VQ&4rtXr7yYc*cEYl=zvS*HG@Hsdq+YvfT{UVQ zJfLrWJ@^sB!zPz_#k}D~D8(?5_c1~O1&2{S)KSUWHahw+1}@cfkoHSGZe)GI_VbwT zu4Kui=vskmky@5mPvV`2C>8m?c4)?ViXxe6+C8Bn@gV|{dNnqD1?NQI$ESX=D^B5V2XdVhV3E79I2d|E0OYFpD+UH6af44O?`uU;$qxvoFnpVj-=GOi!UtYMf$SDTFMjeiw07v+B7lzytPdeVrZ zyH#1`Y5oJqtxXFr-gm4Vml=A7_U!{bZX56PpRbUQxm zK-Xsw`tY#AJvh@^QAoy&vnev6-R&63R=?l?2Uc7Wl7#@Fm+Nvn6uH=TuDC{;D<2JT@pcTcPJZi(FE?|~JFO=i z@oT&|Qr!z68T0Jxj?$@1Cot8|C)lgUERV$Az{o8EQnbkE9_M|*eIOf#68+^S<|e@e zbjK|pyqDw~WUjmGMF7AFqGkMAUhYB**@oMNFLb?p<;=}*_rP(?@!mUh>gJE^LMr!_ z104Kq5x3E{mpmfapo^U%-5jVCPF|?Vq}du?Ie3R-yol z_wkx)I%7damH8xula4*(|Eza4*liPIh17F4DR;s9G$o8&7`9X zu7_9)bTB+*p$xC5q4eN#dbY3n5cShI=pJuStLs2hSd_1RDGsb@qvVm3W?zgt1FhXc zS3b>Rm(ns!p@{iB_ zkUaOoz^kANu<+87ef>WXeC+Q>Dc+lrUoAV+voe9F#Jg8Z?9#RKjj`-pDUnQV6*Am? zeRTGALvN7e17)uIP*Dx#wd=qdcb^9(xPNI`3$-ZmbU#e2@s0r(4Fwtc4)*Y@ z<(ygC*+M)%Z%q=j6UHtrK_*lnbRt2>SWrLkwh%A;2nHi4SdIM@l)bO!zdZ3;Q`74` z%u1U*cER=jn;$a2eEHJ&J5x#XJ+m`=VyCN0R^NnC(h@{v>MK*ZymCe}NrIjse!C@A zFE8)+>LdQ$*_Z@I?KSIgAK`&=AwSI}P~(APS(OBQz)K+#YLEJ%;M4fV*>3_};M6nA zQ+#zWyaghiV?ClCg@&HB@zWMRPKHtp@MaG2_nRiy(t8RM82yBLA(>s?6#v>=>stca z*k!=5mtds#rePo0Fo+f=b0*gcpH+X@<1YtQ$?h~cjE@hSu5_?k zbqJ76S05IalMJ#oZ@RdUYRHHdr}pqc?9grrt-+orLzKZdnI+sOr$ZjaoD6yEXaa>Z zc9igH1Vldlp{iq^RfPV{;tCE!GeR5~yU9HXB6YGiK<@@k8I>x@8{SphAw5c})+B33 zl`*fEy!XWJlf~D3{osCc)a}tNnD}FZuCCC_EFHz&Ed0beQ`bGMbVcr+=1+?U07P7= zt$@3&?BpM0x}Yw=Vdj=zcdC^p7PC4);M6!utM(Qee8iXV?&(}zVzQLEX7uOC&c7c_ zdY{!U1Ro!M++=$ND9jckug2$T3c)@=(O0Q;N$6L5vxnfLooRjQ&{B^iq**f*b*8V>m=q75 zOFl%Uqd(EomfB@GmdHXjdMW!lJ0o=*)rX4aRG!wbyFb9YN%36ZdrI|NQG4_!!sDr( z?`nu1flV+v1e~F-(vE7-O!NVCxHrwwj7hh29W?bwKM+i{CZullSm9BzrTXc~YIbD@ zO^28l$8NshPar|@abaq2gRMZwu7nmMS{Qlw{SK_w=CbzL^!QjPTso#2j`=qAe1|2)Y z5EYXe=-ja52P`<%mm7Y!ZPJS(())mxJR-15F3aQ}#3FJpeSAQ<^1T075C|?gV>6<* z7%e6IYNJ5Y||&vt4<(-g~$6#ViXei?0>_Nck=s{gMok)BG$yUShN}7i&}(m@<`{SPum3e5KN$rB@{9jHC#LJ zF!XYA1Esd@^>RWr;J|M{nnFPBND$8`?s`{_v{9GFmI*K1cSkpx-t8Drtp? z96puy(pQl!q31;J1vb7#lAz9fS=Aj?ydk9U-*!NqJge1pSd^Y~|jy%Ab;Y(n5T#+$v#z#-^tC+`?8zfu~EjUBA0IwGR|6fbp+Y zN1UU)8T0~B-rMxD8_QGt=Nc>MTjpRuMSS78`SN{$`=Y6kVdUzoIWY?ERtIPKU?i62 zN*+KMT#+AoIz8?8XeZke29zOM?Y-=4&)18!?Uw@q`(SQupZn9TyQdJiHcKE11?zlG zHAH0fS3c6oHB`Xgj*y998K1KX{gs9&??iSIop#!EM%!EmPnq5`l4h4GU3ni0-zo2n z5f{>9=+@EO^w6CwLqcJ!zCw$3=uSXzr<^9$yQ_7?SRhQXGx?|-5HYPRRP`cKLId4d z@(-ZJYG&5mTTNNCnyJcj7QLDHAlRfbrLFV8>HGjTH}=x!ZF$b}X`FfZsb|iat$KN1 zoc6beF+XEZ>&bz<<7S1B=yZVI$Jx;+6kmr!M0Wp|XW>@r^*^7M7Gz$5kAN1BDpH;B z8(6s)o8D$0jVM}C&p@w%Kb%Isx(=t7$uR{1TDy*4191I^EJ!+>_I}-0cfv*Wje6qt2r@YjFTs=*UE1TUfd*?+w*CFmuR4H95}zWO6Hf z6X?0f{$1xqgpeYOMYb3e#7X~gAL={j=-@hUhI;4>u$*0Um=6#{tLV~3vA_kQ7}qx) zu9fy>?BYprLO+3uTy{-8z#D5U8uCMPDt?q;`W1X)H{q4;=&He6=G$J>-JP>y(wNOV zNm7?6C7#eC=b&%FD2~G*Kg;t-{8?Fjd;SzWS`F@YxelXYs0vJS^y14?!eO4LjkXh`1BPft2MY9ilY45TEr0 zs$&Og;o#>(1>agvOVuQL8BumL!mmM_V4w7H=4`@iA$PuWLbwTp(?~AI@0{asz|@A{ ziaIsh>Xc*#DSvb7K3Xc-ovxJ*IV-+>P>3N3llU?K+K~y*s*lCh*K9sh&CEfMFeiH7 zG4i%YQllgv_WUcrWtge=0NDD|&cBBM9U|RxgYZ!i{xWcEi2`r>!sQoBjoxQ z9R~?S5HSGv_X7^Dvky?SR>k!($`;HUq}Txm69PmMlNuxBwq8P?;am&S>x5|u_&Ygy zoSH_`g$7t$@|X|MklALPN)kt$xI26tFr>J1wmGjw)3C}v*LH3RM+&pYy|a0=)5rz3 zaIb0t|6Rg8v?&~mfc_!zOBJhvaXi<6mp`}06LMyuEl={q+3@H+)$Lx`JDh=?qyA4S z?PVF9a3DfIuukRmN zq74;mo7x3w!Ci5U4%UCL0Q<6&kJ?Z;AMuX+*eGMGCCI+2wgTB3zVwraQ_*7CrPc(OsBGuNQB{I$Q_!Fh3@N;Kqsnv}Y+O(Gv)|GXwY= zBJ2iyfXZJURd_+<2>|K!fWyTeF(hvc-F!WDL4v;29{Rc)b^es|c9}z4e*(?lnT&sv zpN!MQnJBk$AM8>L-KfeX8pT;1XcrGO7>WGNJ+68R`C`Unn7zK8VWNBrFcrvX#Mn#q z4@ifI+Kk32iX51wuw@Pe{sayo9p?()E56QA`C=UkZ>-?P?-m$(u|1S)vAw9M0B8x` zBV%<%J;=NjUQqsU{w^RAiQ|7V>W{9DL%KAhx})wgLk7LpIArXH?vlM! zBEJeY6zb`H+C&-(IW>)?x|!f~CCd9nft)O{kF4cdFGc%CG0na}{08vf>QHRytlvS~ z%U%=+01Al8Z+3BBvcB9pC@>EIWC(nb#(Frx`+3FCM;qN*2Kxj&5aWp>hwmBvAzTrB z_piS@^C`jH2jV8EzVtj)V+y-}4=yuP>lEAm>6Hi(9tT@~BJcv}EfHKcAaN56Roc*6 z!nsYwj|}b58T>*pixze&5WCltwUD*}7F7<&Zo+`ldE>9I;6LBQ$@ey^Rxuz}nbY0hVruyXeKDC8pHBz#U31Na3PaGU zwLFJP^=Ig1lM008cv2kAz4q=P<_AQVA*xvTLN-7=1pI#^sI@4M6p|&iUoHs0WK@U* z~}16MZ!ky+S;LYhH+A2I7X$0M!QI@CnU%fk18K4$s*nIQji$f6}DU^PweMl-!$ zc#$}yn9;rv{Gbw4qOAst2}3dmN#g*)e$$`c6OD0FlRUUH3Ya1!9z-6N3qfxSeM*!e zX%vh6ug`bF1w}7Gc>)5BxQHiTe}nGQ`HRRh=NME15R@j@t%0u2^cGi4fyVuXfB#}t zYT7cq86ZUXpzDI>dIgMu9xo9ifwYU2Wj>!M^S|CWmma$2@WH5FopSbfWkR{=$?AW< z3wEhn2D8uRq$gC7?hJO$U#sw>6qa26U$5a1g9}Ht)qj>~;-Dll+x_>)%T5YX{RLb7 z{UX?7QRSQqljFXAGbN7I|8fS8uA9giSzR6R4BBjhd}Ka|M%)(>j$-cpKp5{ z7;6y3sKZ@;kRrUl9*Gt1k1)B!5xf(pfeJl#HsAmG<_JZRp>r7!J^3>~4Cr!*$t6MwQp+2FmfF$J) zBEwEX+3&V$z zLd%#p30itSi^MVIJ9%br4)8-*);bckTIZ<{OKH{w`L-KSRuHh_wYRVMKfBCvRtznK zKwThreUyVrI?B5p4L#BuE{`&O>8gZWG6eeHU-EA@ESq~hwF^1~uS&{XD|I48HThbF z6HEbNcE~UQrUbXqM9gpKEX-qm!X-SA&!fM>sb2-LZx8It3{-r-TdasICDm!PJa+5q zZ$#NCYZ>|ZG*XOU=b<)?W|c29%~IxJZhW*Z{R($QBdDyy}+16q(Bo}hJXo#oqu%=~@4x}X37 z>q-ca+=6$NK{)yD86cIE;-o%9rpME3sM98SKh6(DUH9 zv^GZ0JqiNfvt{+q+2HR0Fo|T-Lx2;ggz7p}$Gr`WlKR~qrQj%tQxe9e{+3zlJ{@>w zQbS!KB>M`a|AEsZ-x=~)lU(;+FAf_>70C)l0jJw9922U=n`dW>j;r_$Ov^# zv*#CJocoQ-AxybI2k~Z=gJR_*T&pYGOvy=`8+H@`Dq_j`D$ zQmiI$0<<%FEc9wowtW}c`-h!PZ?bO7RElWY)1S|iM zt9qhe4l^I1;YTmuP>XF2glO?v|o~FX&gTlgh&jVJZE)ihH9)DQ-7|C?3GnJ{} zMu)2?q=Ggh&Ox=XDr1ctv>-^#f6M>$@ z5JU$dO&(lFurd(Oe`;-|zY;mWDmNkOuiH$yiA=O5)Rf1ELI?}+f-YI;mvlwSU9yVX zyxYgaiy-_ERe^2+$ddYalw0aST9hB{tFs8q)t5bX;r(ax7@rEzZVAJQF;GkCT#wSHMC`3jM z#46CIO6s)JUt%=(9wlr10UTLo}0lcxBXP#qlmlq|utXo{GZ1kMo6JmlbyNe&qM`F4ialrG}TJe%Xx7B;?w79wgbRpIhkguOGVmH~-v; zl&Q09M{!_ey^}CHAYZ{y#%;QuJm&DEkf7AX;FE^#i$x)+CFk_G{4=~GmHX64*d*0y zD(;;GYVOu9+)X38W8XoK0!`!A&xPy_I^F&GMZ26ovlG2PKB7@mDnv@pt#zI51zpeg z5I-0?I-Z%*ptjd{dyJ@5I~DTbkdtgi9CnqJzhUcJ+s)lqzs?m<^cbC&+DXV6f#DB; zck-QI(^9jW$i#ta27(Wy`N5c{)MY}EDcGmMw@`>A(q>XC{jS&bzk-5Kw*K5dbUByMY29psxw%M-3D2avgVV+38#->N2I zc^qDt2n-fivTpGg9`2_t0+4(H> zFxfSl5|slac4ztrwBkn|bxwb3X;jZxOgSyMl+-Hg*qP_#cu>Dg9m^8Cd2*-o$IjJj zu3ut^s-KncYNUjRAUYcN1;R;GMnUlw`ZoXl=m9Rzj?^j04hR5zP;vK#z4Xb%&v8|7 zE#up-41t?db<00bTzt}Vs_+I|rY);SR&(A}qo_l1eZi_8+rdxgg zAi~}czOF}3%x?{U(60T_Hz#R>WKZDUMMi4=VwTBApR&q&SzkyqClt%&kso_SkkT=_ z)qXwl_Q6OjX$5-f{1gsrn!{`xQ<3*hg%^F5N!4=g#BWieagyt6cVd_>k;Qi7nIueR zsV8( zei7msaf>bLw5lei{$#`s;Ve2MrXt1%S~K`Q+IAe>Xs)8A@&RWO^cx{i0=x@bnASAo zI}_;IMcu>^?q?|w%mS!j*9NhXm}A`I)MsQr66vQs{OXsj@r%?^ z0>o`%AJ9Ea^wW`va>7!#cp;)s#UF539!rdEQCsT5IPc?95b%m*Zdt9=d z{rWdrw{Aby`Te*k-RtV*fWa{S@xVoflW$Kfhplfdh|Sq+U11ZM)EBfvLXK<4KkY`0Jl^ZpZOnl7a z2V7j5tbTNn*_4NQLK7&Gj&dyL|H0K)hGn^J?E=yr5+V(vbeGc7h|--ZAPowLlyrl1 zcXvpNbcrYk(nu?%(kP+G8INnnx6i+I87$xV&KTpaK^V}iVpyxVCkS!|r_fJH3V~!O znF_pO*fAF9g9ohFzqmn%0z$!W0LrWFmGaikrX$RhdD|7izhME@g~|Qeys7DCA6%o6WfX-0u=$*N@1n+-Tupg7>Mg zKPzI9))nb*hbMmYH`tGwmNVIz1bklkI^*h_XuM4k!l^j|voDLj*$T^6t z|Kp=-gV(vPRS+#ahdtqEIt~#fA(#Ts|9HDL6O*FcTm)g@NMI>ZU~g9wLy48JVp`-szVsd;UtB5$X#%cBI^-e*Vv8p3;)j8U$tJ~?JQ^j%5JIo?4Go04O;#9mn2o+ph=Pd9(XU3&P3sz+?dK|qTo`70l(2d zBz%OgF=t1$zA`+Q~OOJZi_I96N+-ecDy*|Oh-74$JQz?sn^BmZ$z0dR) zqTU|R2>rg!M6sM)dZoue#+s-gw41XWem8U^iM4FvJCIA~AsEZAR4HgZeyt@~t36-7 zMY+S<(po;^*{*jF>(=9XT15h59*>=;N;Q*Ds6vl+Zn=#_rXmpuTS>?OvKdvbFnhzud0 zVVnl3e-}~{x8&f{-V{uIW=+z=g5q=;vz=!?v$vWu_XzoPzjMzEEDCrie`4-GA+quX zlHUITwC^{$QNOeR$P=JeOKFjcU=E`B=3*=f&b^2S5>*{=)@QM`WtU@}SEx8S>z|L~ z%FA_L8OBJ8RN93`!yB-D=HesZgaKuQ=If78`a5V`!sf(5w%|>}gdzm<$}`8{@LkC| zSf03l2WP5V%asY*WH?;}E+N#8~P~>ymU{psx)>W5rp6cFq25IEgJKgsva3nBuC3&eX3Cv8^ z`UF$tn#%~4r;J+JX3_3MJ^FC6R!k!+pl}$uNN+H#(UP>XK_QjUc{W)Sz(rM}9h$eB^QhOlM{WiLOf)uo)L*4f;(GO> zSneCI1NV-&sa)EBb&?u9Q`9~r_Z8X4Bd?CdvXHwlS~{cHA>aJQkiBa>{tF&0=^8vK zqetS8aFkTn=LYTxD;Pl(4Qm7Og!q7W;J#>sf@?kRL8K|enFT}`!2Eq$Ax(&N$Cxbt@vWLd|JPL3_>er)yD_K$x&Te#_Z(=#&5(RW)4 zNQU#}04Z1d=|4`NFMb97a`NjQL=D`1zA@<|GPj!ZChHz&z4LYSoB68rmL_t8U1}}X z2o!e0NSG3$QUPe->bku_gaOG-kHKRY7lE3npp_?a$=i8}dv7GFpaT$wQ3sU42wlE?)IGR&PX8!wjPlMUvw}-*Ejam~g6P?Q-_Lu)3wV^Y4d@vPcE97^cNwGM z(;=*8=#oCcButRG@RXi)%gx_WmcAYYPzf-A#eNpML*D`|COV<3X6| z)Fy z)EDSnO@=u=S`&B=KOpy7?m0qjQ-#yPETI9de5oO-^HUHZM*MNOO~7#1tk%tD zLh(3#UB-_Y^P4X!-Pvlo8nEl6=Ud}=B$wZqut{&kmkO6>Sx4sUy`%_<#Koq>>C!g* z^$VzOg*HkN)_E;nUn06}7{B3oFH99>r%uFJy}VeK2A?(<2Cp!)^8+t~|Hnl?3J|Fd zzbhlhr5G=qv1F%d_9gn1#>6836yMDh<@+7w(glYpiCWX*&1DJ0JuxNDlL%9u(m#L> z9bROqb}*M)1TPBv61!9<1e!eu3!>7>nee{E0|AhlQrmRkK~W*k*j8N_udMi5!$NMIT?< z_M`EBMXjRjoG|x_m7-!!CB|M<_(G86w;p{-YBM*`k7Figv7hJpscNQKln^t1w)ZEo zat!}&5Y$~Wrc~#+D1Hq3m3-=6#hjib4TB#f>3jp8pv=9?yNB4<0InCBiOI_ZRNWpR zZ0vi0US0^a8<6Q;IV&1sSM+!O=^|#HOQ46Sk+K0ghp7y-&7+j29;hjpO;}b`95PR2 zwO@@DfB3!eB`4LLwsURz#q9l(dmF>l^9A=jF6^268nLoZzWKzYb-L~wF5po;aa!x) zDwR}KMKc{`-7oOVyRy-jBO7$WN8fVjC6P=d4xkzs^*#ae%;z43eKn-2?1=N`pqR�a&$Ph!rr~ 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 Q@D2<_>Z4@k2cxh54}?k+ssI20 literal 0 HcmV?d00001 diff --git a/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md b/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md new file mode 100644 index 0000000000..034ab80d71 --- /dev/null +++ b/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md @@ -0,0 +1,17 @@ +# FastWire B2 (xAI) — UI evidence + +`010_logs_priority_lower_bound.png` — Logs & Debug, three seeded `xai/grok-4.6` rows +that exercise every branch of the new pricing path: + +| Row | Situation | Cost cell | +| --- | --- | --- | +| `req-standard` | no Fast requested | `~$0.0300` | +| `req-priority` | response-confirmed priority, prompt under the long-context threshold | `~$0.0600` — exactly the documented 2x premium over the row above | +| `req-longctx-priority` | response-confirmed priority, prompt above 200k | `≥$0.8760` — the published long-context rate, marked a lower bound because xAI publishes no combined price | + +The `≥` prefix is the visible change: a cost that is a known floor rather than an +estimate now says so instead of rendering as `~$`. The detail drawer explains why +via the `priority_lower_bound` estimate reason. + +Captured against a local proxy with a seeded `usage.jsonl`; no live xAI request was +billed to produce it. From 1d7d8177a383db5066e8040cfb6ae5e4ab6d52a9 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Tue, 18 Aug 2026 22:59:13 -0700 Subject: [PATCH 3/8] fix(xai): address B2 pricing review --- .../260818_fastwire_b2_xai/evidence/README.md | 2 +- .../docs/reference/configuration/providers.md | 11 +-- gui/src/i18n/de.ts | 3 + gui/src/i18n/en.ts | 3 + gui/src/i18n/fr.ts | 3 + gui/src/i18n/ja.ts | 3 + gui/src/i18n/ko.ts | 3 + gui/src/i18n/ru.ts | 3 + gui/src/i18n/tr.ts | 3 + gui/src/i18n/zh-TW.ts | 3 + gui/src/i18n/zh.ts | 3 + gui/src/pages/Logs.tsx | 54 +++++++-------- gui/src/pages/logs-cost-format.ts | 69 +++++++++++++++++-- gui/tests/logs-cost-lower-bound.test.ts | 57 +++++++++++++-- src/usage/cost.ts | 8 ++- src/usage/expected-prices.ts | 3 + tests/usage-cost.test.ts | 47 ++++++++++++- 17 files changed, 228 insertions(+), 50 deletions(-) diff --git a/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md b/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md index 034ab80d71..0ae66d31f3 100644 --- a/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md +++ b/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md @@ -7,7 +7,7 @@ that exercise every branch of the new pricing path: | --- | --- | --- | | `req-standard` | no Fast requested | `~$0.0300` | | `req-priority` | response-confirmed priority, prompt under the long-context threshold | `~$0.0600` — exactly the documented 2x premium over the row above | -| `req-longctx-priority` | response-confirmed priority, prompt above 200k | `≥$0.8760` — the published long-context rate, marked a lower bound because xAI publishes no combined price | +| `req-longctx-priority` | response-confirmed priority, prompt at or above 200k | `≥$0.8760` — the published long-context rate, marked a lower bound because xAI publishes no combined price | The `≥` prefix is the visible change: a cost that is a known floor rather than an estimate now says so instead of rendering as `~$`. The detail drawer explains why diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index aa095c4eff..29eb441e3b 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -154,14 +154,15 @@ Explicit capability `false` and Responses caller-tier forwarding retain their ex ### xAI Priority Processing The built-in `xai` preset advertises and injects Fast only when its effective transport uses -`authMode: "key"`. It sends `service_tier: "priority"` to xAI's public Chat Completions or -Responses API. `ocx login xai` uses the separate Grok CLI subscription gateway, so OAuth remains -unclassified: its catalog rows do not advertise Fast and the proxy does not inject a tier. +`authMode: "key"`. API-key mode uses the key transport at `https://api.x.ai/v1` and sends +`service_tier: "priority"` to xAI's public Chat Completions or Responses API. `ocx login xai` +instead stores OAuth credentials for the separate Grok CLI subscription-gateway flow, so OAuth +remains unclassified: its catalog rows do not advertise Fast and the proxy does not inject a tier. xAI charges Priority Processing at 2× the standard token price for input, output, cached, and reasoning tokens; cache discounts are applied before the multiplier. Cost estimates use that premium -only when xAI echoes `service_tier: "priority"` (or when an adapter explicitly records an assumed -priority outcome). An echoed `default` is a downgrade and stays at the standard price. +only when xAI's response confirms `service_tier: "priority"`. A missing or unparsed response tier is +not confirmation, and an echoed `default` is a downgrade; all three stay at the standard price. For `grok-4.6`, the standard rate per 1M tokens is $2.00 input, $0.50 cached input, and $6.00 output. A prompt of at least 200,000 tokens reprices the whole request at $4.00 / $1.00 / $12.00. diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index cdc510443c..c7e4bafc85 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -657,6 +657,9 @@ export const de: Record = { "logs.col.tokens": "Tokens", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "Ausgabe-Tokens pro Sekunde über die gesamte Anfragedauer", "logs.metric.estimatedCostTitle": "API-Listenpreis-Äquivalent, keine tatsächliche Belastung; bei fehlendem Preisabgleich nicht verfügbar", "usage.cost.total": "API-Listenpreis-Äquivalent (dieser Zeitraum)", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 2a80b4feb7..22b18ab4df 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -690,6 +690,9 @@ export const en = { "logs.col.tokens": "Tokens", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "Output tokens per second over the full request duration", "logs.metric.estimatedCostTitle": "API list-price equivalent, not an actual charge; unmatched pricing is unavailable", "usage.cost.total": "API list-price equivalent (this range)", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index ba7798d64f..1388137610 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -671,6 +671,9 @@ export const fr: Record = { "logs.col.tokens": "Jetons", "logs.col.tokPerSec": "jetons/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "Jetons de sortie par seconde sur toute la durée de la requête", "logs.metric.estimatedCostTitle": "Équivalent au tarif catalogue de l’API, et non montant réellement facturé ; aucun tarif n’est disponible en l’absence de correspondance", "usage.cost.total": "Équivalent au tarif catalogue de l’API (cette période)", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 88a4e47f6c..9ade561da5 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -633,6 +633,9 @@ export const ja: Record = { "logs.col.tokens": "トークン", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "リクエスト全体の所要時間あたりの出力トークン数", "logs.metric.estimatedCostTitle": "API 定価相当額(実際の請求ではありません); 未対応の価格は利用できません", "usage.cost.total": "API 定価相当額(この期間)", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 14a3512c9d..38fea1b47e 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -676,6 +676,9 @@ export const ko: Record = { "logs.col.tokens": "토큰", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "전체 요청 시간 기준 초당 출력 토큰", "logs.metric.estimatedCostTitle": "API 정가 환산치이며 실제 청구액이 아닙니다. 가격 미매칭은 표시하지 않습니다.", "usage.cost.total": "API 정가 환산치 (이 기간)", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 10243dbe2f..12bbe081f7 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -674,6 +674,9 @@ export const ru: Record = { "logs.col.tokens": "Токены", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "Выходные токены в секунду за полную длительность запроса", "logs.metric.estimatedCostTitle": "Эквивалент стоимости по прайс-листу API, а не фактическое списание; если цену не удалось сопоставить, значение недоступно", "usage.cost.total": "Эквивалент стоимости по прайс-листу API (за этот период)", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index b9c7705c14..a37107e91f 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -681,6 +681,9 @@ export const tr: Record = { "logs.col.tokens": "Jetonlar", "logs.col.tokPerSec": "jeton/sn", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "Çıktı jetonu / saniye", "logs.metric.estimatedCostTitle": "Tahmini API liste fiyatı", "usage.cost.total": "API liste fiyatı eşdeğeri", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 664d74f256..256d9828ee 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -527,6 +527,9 @@ export const zhTW: Record = { "logs.col.tokens": "Token 數", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "按完整請求耗時計算的每秒輸出 token", "logs.metric.estimatedCostTitle": "按 API 標價估算,並非實際扣費;價格無法符合時不顯示", "usage.cost.total": "API 標價折算(當前範圍)", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 91eb89045f..7f58c0d103 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -669,6 +669,9 @@ export const zh: Record = { "logs.col.tokens": "Token 数", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", + "logs.cost.approximate": "~{amount}", + "logs.cost.lowerBound": "≥{amount}", + "logs.cost.unavailable": "—", "logs.metric.tokPerSecTitle": "按完整请求耗时计算的每秒输出 token", "logs.metric.estimatedCostTitle": "按 API 标价估算,并非实际扣费;价格无法匹配时不显示", "usage.cost.total": "API 标价折算(当前范围)", diff --git a/gui/src/pages/Logs.tsx b/gui/src/pages/Logs.tsx index afd722de2b..468805b301 100644 --- a/gui/src/pages/Logs.tsx +++ b/gui/src/pages/Logs.tsx @@ -15,7 +15,7 @@ import Debug from "./Debug"; import type { LogsTab } from "./logs-tab-keydown"; import { logsTabKeyDown, readTabFromHash, selectLogsTab } from "./logs-tab-keydown"; -import { formatEstimatedUsdTotal } from "./logs-cost-format"; +import { formatEstimatedUsdTotal, summarizeEstimatedCosts } from "./logs-cost-format"; import { modelTitle } from "./logs-model-title"; import { speedLabel } from "./logs-speed-label"; import { cacheSplit, isCursorUsageProvider, tokensTitle } from "./logs-token-title"; @@ -239,17 +239,17 @@ function formatTokPerSecond(result: TokPerSecondResult | undefined, localeTag?: return `${result.estimated ? "~" : ""}${value}`; } -function formatEstimatedUsd(result: CostResult | undefined, localeTag?: string): string { - if (!result || result.kind === "unavailable") return "\u2014"; +function formatEstimatedUsd(result: CostResult | undefined, localeTag: string | undefined, t: TFn): string { return formatEstimatedUsdTotal( - result.estimate.cost.total, - result.estimateReasons.includes("priority_lower_bound"), + result?.kind === "value" ? result.estimate.cost.total : undefined, + result?.kind === "value" && result.estimateReasons.includes("priority_lower_bound"), localeTag, + t, ); } -function formatEstimatedUsdValue(value: number, localeTag?: string, lowerBound = false): string { - return formatEstimatedUsdTotal(value, lowerBound, localeTag); +function formatEstimatedUsdValue(value: number, localeTag: string | undefined, t: TFn, lowerBound = false): string { + return formatEstimatedUsdTotal(value, lowerBound, localeTag, t); } /** Consecutive failed polls before a stale table is called out. Two seconds each, so ~6s. */ @@ -344,29 +344,16 @@ function summarizeFilteredLogs(entries: LogEntry[]): { requests: number; totalTokens: number; estimatedCostUsd: number; + priorityLowerBound: boolean; unpricedRequests: number; unmeteredRequests: number; } { let totalTokens = 0; - let estimatedCostUsd = 0; - let unpricedRequests = 0; - let unmeteredRequests = 0; for (const entry of entries) { const tokens = displayTokenTotal(entry); if (tokens !== undefined) totalTokens += tokens; - if (entry.usageStatus === "unsupported") { - unmeteredRequests += 1; - continue; - } - const cost = entry.displayMetrics?.cost; - const total = cost?.kind === "value" ? cost.estimate.cost.total : undefined; - if (total !== undefined && Number.isFinite(total) && total >= 0) { - estimatedCostUsd += total; - continue; - } - unpricedRequests += 1; } - return { requests: entries.length, totalTokens, estimatedCostUsd, unpricedRequests, unmeteredRequests }; + return { requests: entries.length, totalTokens, ...summarizeEstimatedCosts(entries) }; } export default function Logs({ apiBase }: { apiBase: string }) { @@ -611,7 +598,12 @@ export default function Logs({ apiBase }: { apiBase: string }) { {t("logs.conversation.totals", { requests: conversationTotals.requests, tokens: formatTokens(conversationTotals.totalTokens, localeTag ?? locale), - cost: formatEstimatedUsdValue(conversationTotals.estimatedCostUsd, localeTag), + cost: formatEstimatedUsdValue( + conversationTotals.estimatedCostUsd, + localeTag, + t, + conversationTotals.priorityLowerBound, + ), })} {" "} @@ -730,7 +722,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { {formatTokPerSecond(log.displayMetrics?.tokPerSecond, localeTag)} - {formatEstimatedUsd(log.displayMetrics?.cost, localeTag)} + {formatEstimatedUsd(log.displayMetrics?.cost, localeTag, t)} @@ -955,11 +947,11 @@ function LogDetailDialog({ {cost?.kind === "value" ? ( <>

- {t("logs.detail.costTotal")}{formatEstimatedUsdValue(cost.estimate.cost.total, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} - {t("logs.tokens.input")}{formatEstimatedUsdValue(cost.estimate.cost.input, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} - {t("logs.tokens.cacheRead")}{formatEstimatedUsdValue(cost.estimate.cost.cacheRead, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} - {t("logs.tokens.cacheWrite")}{formatEstimatedUsdValue(cost.estimate.cost.cacheWrite, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} - {t("logs.tokens.output")}{formatEstimatedUsdValue(cost.estimate.cost.output, localeTag, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.detail.costTotal")}{formatEstimatedUsdValue(cost.estimate.cost.total, localeTag, t, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.input")}{formatEstimatedUsdValue(cost.estimate.cost.input, localeTag, t, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.cacheRead")}{formatEstimatedUsdValue(cost.estimate.cost.cacheRead, localeTag, t, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.cacheWrite")}{formatEstimatedUsdValue(cost.estimate.cost.cacheWrite, localeTag, t, cost.estimateReasons.includes("priority_lower_bound"))} + {t("logs.tokens.output")}{formatEstimatedUsdValue(cost.estimate.cost.output, localeTag, t, cost.estimateReasons.includes("priority_lower_bound"))} {cost.estimate.price && ( <> {t("logs.detail.matchedKey")} @@ -977,7 +969,7 @@ function LogDetailDialog({ ) : (
- {t("logs.detail.costTotal")}{"\u2014"} + {t("logs.detail.costTotal")}{formatEstimatedUsd(undefined, localeTag, t)} {t("logs.detail.unavailableReason")} {cost?.kind === "unavailable" ? t(metricReasonKey(cost.reason)) : t("logs.detail.reason.usage_missing")}
@@ -1032,7 +1024,7 @@ function LogDetailDialog({ {attempt.durationMs}ms {formatTokPerSecond(attempt.displayMetrics?.tokPerSecond, localeTag)} - {formatEstimatedUsd(attemptCost, localeTag)} + {formatEstimatedUsd(attemptCost, localeTag, t)} {reason} ); diff --git a/gui/src/pages/logs-cost-format.ts b/gui/src/pages/logs-cost-format.ts index 65ce2e1d47..656cc4fe60 100644 --- a/gui/src/pages/logs-cost-format.ts +++ b/gui/src/pages/logs-cost-format.ts @@ -1,11 +1,72 @@ +import type { TFn } from "../i18n/shared"; +import { cachedNumberFormat } from "../intl-formatters"; + export function formatEstimatedUsdTotal( totalUsd: number | undefined, lowerBound: boolean, - localeTag?: string, + localeTag: string | undefined, + t: TFn, ): string { - if (totalUsd === undefined || !Number.isFinite(totalUsd) || totalUsd < 0) return "\u2014"; - return `${lowerBound ? "≥$" : "~$"}${new Intl.NumberFormat(localeTag, { + if (totalUsd === undefined || !Number.isFinite(totalUsd) || totalUsd < 0) { + return t("logs.cost.unavailable"); + } + const amount = cachedNumberFormat(localeTag, { + style: "currency", + currency: "USD", minimumFractionDigits: 4, maximumFractionDigits: 4, - }).format(totalUsd)}`; + }).format(totalUsd); + return t(lowerBound ? "logs.cost.lowerBound" : "logs.cost.approximate", { amount }); +} + +export interface LogCostSummaryEntry { + usageStatus?: string; + displayMetrics?: { + cost?: + | { + kind: "value"; + estimate: { cost: { total: number } }; + estimateReasons: readonly string[]; + } + | { kind: "unavailable" }; + }; +} + +export interface EstimatedCostSummary { + estimatedCostUsd: number; + priorityLowerBound: boolean; + unpricedRequests: number; + unmeteredRequests: number; +} + +/** Sum valid displayed costs without upgrading a mixed estimate into a known lower bound. */ +export function summarizeEstimatedCosts(entries: readonly LogCostSummaryEntry[]): EstimatedCostSummary { + let estimatedCostUsd = 0; + let pricedEstimates = 0; + let everyPricedEstimateIsLowerBound = true; + let unpricedRequests = 0; + let unmeteredRequests = 0; + for (const entry of entries) { + if (entry.usageStatus === "unsupported") { + unmeteredRequests += 1; + continue; + } + const cost = entry.displayMetrics?.cost; + if (cost?.kind === "value") { + const total = cost.estimate.cost.total; + if (Number.isFinite(total) && total >= 0) { + estimatedCostUsd += total; + pricedEstimates += 1; + everyPricedEstimateIsLowerBound &&= cost.estimateReasons.includes("priority_lower_bound"); + continue; + } + } + unpricedRequests += 1; + } + return { + estimatedCostUsd, + priorityLowerBound: pricedEstimates > 0 && everyPricedEstimateIsLowerBound, + unpricedRequests, + unmeteredRequests, + }; } diff --git a/gui/tests/logs-cost-lower-bound.test.ts b/gui/tests/logs-cost-lower-bound.test.ts index eddb95039e..c0bf527f4c 100644 --- a/gui/tests/logs-cost-lower-bound.test.ts +++ b/gui/tests/logs-cost-lower-bound.test.ts @@ -1,10 +1,59 @@ -import { expect, test } from "bun:test"; -import { formatEstimatedUsdTotal } from "../src/pages/logs-cost-format"; +import { describe, expect, test } from "bun:test"; +import { DICTS, interpolate, type Locale, type TFn } from "../src/i18n/shared"; +import { + formatEstimatedUsdTotal, + summarizeEstimatedCosts, +} from "../src/pages/logs-cost-format"; + +function translator(locale: Locale): TFn { + return (key, vars) => interpolate(DICTS[locale][key], vars); +} test("ordinary dashboard costs retain the estimate marker", () => { - expect(formatEstimatedUsdTotal(0.77, false, "en-US")).toBe("~$0.7700"); + expect(formatEstimatedUsdTotal(0.77, false, "en-US", translator("en"))).toBe("~$0.7700"); }); test("priority long-context lower bounds render with a greater-than-or-equal marker", () => { - expect(formatEstimatedUsdTotal(0.77, true, "en-US")).toBe("≥$0.7700"); + expect(formatEstimatedUsdTotal(0.77, true, "en-US", translator("en"))).toBe("≥$0.7700"); +}); + +test("USD placement and separators follow a non-English locale", () => { + expect(formatEstimatedUsdTotal(0.77, false, "de-DE", translator("de"))).toBe("~0,7700\u00a0$"); + expect(formatEstimatedUsdTotal(undefined, false, "de-DE", translator("de"))).toBe("—"); +}); + +describe("conversation cost lower-bound aggregation", () => { + const priced = (total: number, lowerBound: boolean) => ({ + usageStatus: "reported", + displayMetrics: { + cost: { + kind: "value" as const, + estimate: { cost: { total } }, + estimateReasons: lowerBound ? ["priority_lower_bound"] : [], + }, + }, + }); + + test("marks a total only when every included priced estimate is a lower bound", () => { + expect(summarizeEstimatedCosts([priced(0.77, true), priced(1.23, true)])).toMatchObject({ + estimatedCostUsd: 2, + priorityLowerBound: true, + }); + expect(summarizeEstimatedCosts([priced(0.77, true), priced(1.23, false)])).toMatchObject({ + estimatedCostUsd: 2, + priorityLowerBound: false, + }); + }); + + test("preserves unpriced and unsupported exclusions without minting a lower bound", () => { + expect(summarizeEstimatedCosts([ + { usageStatus: "reported", displayMetrics: { cost: { kind: "unavailable" } } }, + { usageStatus: "unsupported" }, + ])).toEqual({ + estimatedCostUsd: 0, + priorityLowerBound: false, + unpricedRequests: 1, + unmeteredRequests: 1, + }); + }); }); diff --git a/src/usage/cost.ts b/src/usage/cost.ts index b574723902..3af42a3c8e 100644 --- a/src/usage/cost.ts +++ b/src/usage/cost.ts @@ -451,7 +451,9 @@ function applyPriorityMultiplier( ): [Cost4, number] { if (tierScalar(serviceTier) !== "priority") return [cost4, 1]; const base = baseProviderLabel(provider); - const multiplier = findPriorityPricingRule(base, modelId)?.multiplier ?? 1; + const rule = findPriorityPricingRule(base, modelId); + if (rule?.requiresResponseConfirmation && !isConfirmedFast(serviceTier)) return [cost4, 1]; + const multiplier = rule?.multiplier ?? 1; if (multiplier === 1) return [cost4, 1]; return [{ input: cost4.input * multiplier, @@ -542,7 +544,9 @@ export function estimateComboCost( ? { priorityMultiplier: estimates.find(est => est.priorityMultiplier)?.priorityMultiplier } : {}), ...(estimates.some(est => est.contextTier) ? { contextTier: "long" as const } : {}), - ...(estimates.some(est => est.priorityLowerBound) ? { priorityLowerBound: true as const } : {}), + ...(estimates.every(est => est.priorityLowerBound === true) + ? { priorityLowerBound: true as const } + : {}), }; } diff --git a/src/usage/expected-prices.ts b/src/usage/expected-prices.ts index d0cb90d139..af94b8b262 100644 --- a/src/usage/expected-prices.ts +++ b/src/usage/expected-prices.ts @@ -242,6 +242,8 @@ export interface PriorityPricingRule { provider: string; modelId: string; multiplier: number; + /** Apply the premium only after the upstream response confirms this tier. */ + requiresResponseConfirmation?: true; source: string; verifiedAt: string; } @@ -267,6 +269,7 @@ export const PRIORITY_PRICING_RULES: readonly PriorityPricingRule[] = [ provider: "xai", modelId, multiplier: 2, + requiresResponseConfirmation: true, source: XAI_PRIORITY_PRICING, verifiedAt: "2026-08-18", })), diff --git a/tests/usage-cost.test.ts b/tests/usage-cost.test.ts index a7f58d4d88..362868f4f2 100644 --- a/tests/usage-cost.test.ts +++ b/tests/usage-cost.test.ts @@ -609,6 +609,7 @@ describe("xAI Priority Processing pricing", () => { const xaiRules = PRIORITY_PRICING_RULES.filter(rule => rule.provider === "xai"); expect(xaiRules.map(rule => rule.modelId)).toEqual(["grok-4.5", "grok-4.6"]); expect(xaiRules.every(rule => rule.multiplier === 2)).toBe(true); + expect(xaiRules.every(rule => rule.requiresResponseConfirmation === true)).toBe(true); expect(xaiRules.every(rule => rule.source === "https://docs.x.ai/developers/advanced-api-usage/priority-processing")).toBe(true); expect(findPriorityPricingRule("xai", "grok-4.6")?.multiplier).toBe(2); expect(findPriorityPricingRule("openrouter", "grok-4.6")).toBeUndefined(); @@ -645,7 +646,7 @@ describe("xAI Priority Processing pricing", () => { expect(confirmed.priorityMultiplier).toBe(2); }); - test("an assumed priority outcome uses the same 2x premium", () => { + test("an assumed priority outcome stays at the standard price", () => { const assumedOutcome = outcome(); const assumed = estimate(assumedOutcome); expect(assumedOutcome).toMatchObject({ @@ -653,8 +654,26 @@ describe("xAI Priority Processing pricing", () => { fastOutcome: "applied", confirmation: "assumed", }); - expect(assumed.cost.total).toBeCloseTo(0.46, 9); - expect(assumed.priorityMultiplier).toBe(2); + expect(assumed.cost.total).toBeCloseTo(0.23, 9); + expect(assumed.priorityMultiplier).toBeUndefined(); + }); + + test("missing provenance and a requested tier do not prove the xAI premium", () => { + for (const serviceTier of [ + "priority", + { requestedServiceTier: "priority" }, + { configuredServiceTier: "priority" }, + ] as const) { + const unconfirmed = estimateRequestCost({ + provider: "xai", + model: "grok-4.6", + usageStatus: "reported", + usage, + serviceTier, + })!; + expect(unconfirmed.cost.total).toBeCloseTo(0.23, 9); + expect(unconfirmed.priorityMultiplier).toBeUndefined(); + } }); test("an echoed default records a downgrade and bills the standard price", () => { @@ -687,6 +706,28 @@ describe("xAI Priority Processing pricing", () => { }); expect(long.cost.total).toBeCloseTo(0.77, 9); }); + + test("a combo is a lower bound only when every priced attempt is a lower bound", () => { + const confirmed = outcome("priority"); + const lowerBoundAttempt = { + ordinal: 1, + provider: "xai", + model: "grok-4.6", + usageStatus: "reported" as const, + usage: { inputTokens: 200_000, outputTokens: 10_000 }, + tierOutcome: confirmed, + }; + const ordinaryAttempt = { + ordinal: 2, + provider: "xai", + model: "grok-4.6", + usageStatus: "reported" as const, + usage, + }; + + expect(estimateComboCost([lowerBoundAttempt, { ...lowerBoundAttempt, ordinal: 2 }])?.priorityLowerBound).toBe(true); + expect(estimateComboCost([lowerBoundAttempt, ordinaryAttempt])?.priorityLowerBound).toBeUndefined(); + }); }); describe("long-context pricing tiers (#908)", () => { From d887a4f2d5b299b2846d09d12aa7435d1065238f Mon Sep 17 00:00:00 2001 From: olddonkey Date: Tue, 18 Aug 2026 23:43:41 -0700 Subject: [PATCH 4/8] fix(gui): translate estimated cost labels --- gui/src/i18n/de.ts | 6 +++--- gui/src/i18n/fr.ts | 6 +++--- gui/src/i18n/ja.ts | 6 +++--- gui/src/i18n/ko.ts | 6 +++--- gui/src/i18n/ru.ts | 6 +++--- gui/src/i18n/tr.ts | 6 +++--- gui/src/i18n/zh-TW.ts | 6 +++--- gui/src/i18n/zh.ts | 6 +++--- gui/tests/logs-cost-lower-bound.test.ts | 4 ++-- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index c7e4bafc85..8dd5ff7b58 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -657,9 +657,9 @@ export const de: Record = { "logs.col.tokens": "Tokens", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "ca. {amount}", + "logs.cost.lowerBound": "mind. {amount}", + "logs.cost.unavailable": "nicht verfügbar", "logs.metric.tokPerSecTitle": "Ausgabe-Tokens pro Sekunde über die gesamte Anfragedauer", "logs.metric.estimatedCostTitle": "API-Listenpreis-Äquivalent, keine tatsächliche Belastung; bei fehlendem Preisabgleich nicht verfügbar", "usage.cost.total": "API-Listenpreis-Äquivalent (dieser Zeitraum)", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index 1388137610..e99e1d2abd 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -671,9 +671,9 @@ export const fr: Record = { "logs.col.tokens": "Jetons", "logs.col.tokPerSec": "jetons/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "env. {amount}", + "logs.cost.lowerBound": "au moins {amount}", + "logs.cost.unavailable": "indisponible", "logs.metric.tokPerSecTitle": "Jetons de sortie par seconde sur toute la durée de la requête", "logs.metric.estimatedCostTitle": "Équivalent au tarif catalogue de l’API, et non montant réellement facturé ; aucun tarif n’est disponible en l’absence de correspondance", "usage.cost.total": "Équivalent au tarif catalogue de l’API (cette période)", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 9ade561da5..8b07c757a9 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -633,9 +633,9 @@ export const ja: Record = { "logs.col.tokens": "トークン", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "約{amount}", + "logs.cost.lowerBound": "最低{amount}", + "logs.cost.unavailable": "利用不可", "logs.metric.tokPerSecTitle": "リクエスト全体の所要時間あたりの出力トークン数", "logs.metric.estimatedCostTitle": "API 定価相当額(実際の請求ではありません); 未対応の価格は利用できません", "usage.cost.total": "API 定価相当額(この期間)", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 38fea1b47e..9f84f7c9e0 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -676,9 +676,9 @@ export const ko: Record = { "logs.col.tokens": "토큰", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "약 {amount}", + "logs.cost.lowerBound": "최소 {amount}", + "logs.cost.unavailable": "사용 불가", "logs.metric.tokPerSecTitle": "전체 요청 시간 기준 초당 출력 토큰", "logs.metric.estimatedCostTitle": "API 정가 환산치이며 실제 청구액이 아닙니다. 가격 미매칭은 표시하지 않습니다.", "usage.cost.total": "API 정가 환산치 (이 기간)", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 12bbe081f7..1ef1a7b10c 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -674,9 +674,9 @@ export const ru: Record = { "logs.col.tokens": "Токены", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "около {amount}", + "logs.cost.lowerBound": "не менее {amount}", + "logs.cost.unavailable": "недоступно", "logs.metric.tokPerSecTitle": "Выходные токены в секунду за полную длительность запроса", "logs.metric.estimatedCostTitle": "Эквивалент стоимости по прайс-листу API, а не фактическое списание; если цену не удалось сопоставить, значение недоступно", "usage.cost.total": "Эквивалент стоимости по прайс-листу API (за этот период)", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index a37107e91f..ce94f89e61 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -681,9 +681,9 @@ export const tr: Record = { "logs.col.tokens": "Jetonlar", "logs.col.tokPerSec": "jeton/sn", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "yaklaşık {amount}", + "logs.cost.lowerBound": "en az {amount}", + "logs.cost.unavailable": "kullanılamıyor", "logs.metric.tokPerSecTitle": "Çıktı jetonu / saniye", "logs.metric.estimatedCostTitle": "Tahmini API liste fiyatı", "usage.cost.total": "API liste fiyatı eşdeğeri", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 256d9828ee..864818bbda 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -527,9 +527,9 @@ export const zhTW: Record = { "logs.col.tokens": "Token 數", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "約 {amount}", + "logs.cost.lowerBound": "至少 {amount}", + "logs.cost.unavailable": "無法估算", "logs.metric.tokPerSecTitle": "按完整請求耗時計算的每秒輸出 token", "logs.metric.estimatedCostTitle": "按 API 標價估算,並非實際扣費;價格無法符合時不顯示", "usage.cost.total": "API 標價折算(當前範圍)", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 7f58c0d103..15e09582b1 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -669,9 +669,9 @@ export const zh: Record = { "logs.col.tokens": "Token 数", "logs.col.tokPerSec": "tok/s", "logs.col.estimatedCost": "~$", - "logs.cost.approximate": "~{amount}", - "logs.cost.lowerBound": "≥{amount}", - "logs.cost.unavailable": "—", + "logs.cost.approximate": "约 {amount}", + "logs.cost.lowerBound": "至少 {amount}", + "logs.cost.unavailable": "无法估算", "logs.metric.tokPerSecTitle": "按完整请求耗时计算的每秒输出 token", "logs.metric.estimatedCostTitle": "按 API 标价估算,并非实际扣费;价格无法匹配时不显示", "usage.cost.total": "API 标价折算(当前范围)", diff --git a/gui/tests/logs-cost-lower-bound.test.ts b/gui/tests/logs-cost-lower-bound.test.ts index c0bf527f4c..44cc160e74 100644 --- a/gui/tests/logs-cost-lower-bound.test.ts +++ b/gui/tests/logs-cost-lower-bound.test.ts @@ -18,8 +18,8 @@ test("priority long-context lower bounds render with a greater-than-or-equal mar }); test("USD placement and separators follow a non-English locale", () => { - expect(formatEstimatedUsdTotal(0.77, false, "de-DE", translator("de"))).toBe("~0,7700\u00a0$"); - expect(formatEstimatedUsdTotal(undefined, false, "de-DE", translator("de"))).toBe("—"); + expect(formatEstimatedUsdTotal(0.77, false, "de-DE", translator("de"))).toBe("ca. 0,7700\u00a0$"); + expect(formatEstimatedUsdTotal(undefined, false, "de-DE", translator("de"))).toBe("nicht verfügbar"); }); describe("conversation cost lower-bound aggregation", () => { From c13981b5a029d8ee66d662829ee85d2b92e74976 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Tue, 18 Aug 2026 23:52:14 -0700 Subject: [PATCH 5/8] docs(xai): clarify API key transport --- .../src/content/docs/reference/configuration/providers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 29eb441e3b..181820d24b 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -154,8 +154,8 @@ Explicit capability `false` and Responses caller-tier forwarding retain their ex ### xAI Priority Processing The built-in `xai` preset advertises and injects Fast only when its effective transport uses -`authMode: "key"`. API-key mode uses the key transport at `https://api.x.ai/v1` and sends -`service_tier: "priority"` to xAI's public Chat Completions or Responses API. `ocx login xai` +`authMode: "key"`. API-key mode targets `https://api.x.ai/v1` through the `openai-chat` adapter and +sends `service_tier: "priority"` through Chat Completions. `ocx login xai` instead stores OAuth credentials for the separate Grok CLI subscription-gateway flow, so OAuth remains unclassified: its catalog rows do not advertise Fast and the proxy does not inject a tier. From 33e1c3e08d2f3145e5b92164ad23f3cd0743f8c3 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Wed, 19 Aug 2026 00:01:31 -0700 Subject: [PATCH 6/8] docs(xai): separate OAuth gateway rows --- docs-site/src/content/docs/guides/providers.md | 2 +- docs-site/src/content/docs/ja/guides/providers.md | 2 +- docs-site/src/content/docs/ko/guides/providers.md | 2 +- docs-site/src/content/docs/ru/guides/providers.md | 2 +- docs-site/src/content/docs/zh-cn/guides/providers.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index e1451dcfbd..fd56b2ad47 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -110,7 +110,7 @@ ocx logout | Provider | Adapter | Base URL | Notes | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | Live-first Grok catalog; `grok-4.5` is the fallback default. | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth uses the separate Grok CLI subscription gateway. The API-key override uses `https://api.x.ai/v1` and may inject Priority Processing. Live-first Grok catalog; `grok-4.5` is the fallback default. | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude models; live model list fetched from `/v1/models`. | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 coding models. | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research subscription gateway (same backend Hermes Agent uses). Device-grant login against `portal.nousresearch.com`; the access token is the per-request inference JWT. Mixed paid + `:free` model catalog (`tencent/hy3:free`, `stepfun/step-3.7-flash:free`, ...) discovered live from the signed-in account. Refresh tokens are single-use and rotated on every refresh. | diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index 81a19e5e5b..f99bf35aec 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -105,7 +105,7 @@ ocx logout | プロバイダー | アダプター | ベース URL | 備考 | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | ライブ一覧を優先し、フォールバックのデフォルトモデルは `grok-4.5`。 | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth は独立した Grok CLI サブスクリプションゲートウェイを使用します。API キーのオーバーライドは `https://api.x.ai/v1` を使用し、Priority Processing を注入する場合があります。ライブ一覧を優先し、フォールバックのデフォルトモデルは `grok-4.5`。 | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude モデル; ライブモデル一覧は `/v1/models` から取得。 | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 コーディングモデル。 | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research サブスクリプションゲートウェイ(Hermes Agent と同じバックエンド)。`portal.nousresearch.com` へのデバイスグラントログイン; access トークンはリクエストごとの inference JWT。有料 + `:free` モデルの混在カタログ(`tencent/hy3:free`、`stepfun/step-3.7-flash:free` など)はサインイン中のアカウントからライブ探索されます。Refresh トークンは単回使用で、更新のたびにローテーションされます。 | diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 4f57dab7cc..b24e17f3fc 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -104,7 +104,7 @@ ocx logout | 프로바이더 | 어댑터 | 베이스 URL | 비고 | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | 실시간 목록을 우선 사용하며, 폴백 기본 모델은 `grok-4.5`입니다. | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth는 별도의 Grok CLI 구독 게이트웨이를 사용합니다. API 키 오버라이드는 `https://api.x.ai/v1`을 사용하며 Priority Processing을 주입할 수 있습니다. 실시간 목록을 우선 사용하며, 폴백 기본 모델은 `grok-4.5`입니다. | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude 모델; 실시간 모델 목록은 `/v1/models`에서 가져옵니다. | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 코딩 모델. | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research 구독 게이트웨이(Hermes Agent와 동일한 백엔드). `portal.nousresearch.com`에 대한 디바이스 그랜트 로그인; access 토큰은 요청별 inference JWT. 유료 + `:free` 모델 혼합 카탈로그(`tencent/hy3:free`, `stepfun/step-3.7-flash:free` 등)는 로그인한 계정에서 실시간으로 발견됩니다. Refresh 토큰은 단회 사용이며, 갱신할 때마다 회전됩니다. | diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index 1966d9db63..1dfe171a58 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -114,7 +114,7 @@ ocx logout | Провайдер | Адаптер | Базовый URL | Примечания | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | Каталог Grok загружается в реальном времени; фолбэк по умолчанию — `grok-4.5`. | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth использует отдельный шлюз подписки Grok CLI. Переопределение с API-ключом использует `https://api.x.ai/v1` и может добавлять Priority Processing. Каталог Grok загружается в реальном времени; фолбэк по умолчанию — `grok-4.5`. | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Модели Claude; актуальный список моделей загружается из `/v1/models`. | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Модели Kimi K2.7/K2.6/K2.5 для кодинга. | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Шлюз подписки Nous Research (тот же бэкенд, что использует Hermes Agent). Вход по device grant против `portal.nousresearch.com`; access-токен — это JWT для каждого запроса к inference. Смешанный каталог платных + `:free` моделей (`tencent/hy3:free`, `stepfun/step-3.7-flash:free`, …) обнаруживается вживую по авторизованному аккаунту. Refresh-токены одноразовые и ротируются при каждом обновлении. | diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index 4e924458ee..a73676e496 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -95,7 +95,7 @@ ocx logout | 提供商 | Adapter | 基础 URL | 备注 | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | 优先使用实时 Grok 目录;回退默认模型为 `grok-4.5`。 | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth 使用独立的 Grok CLI 订阅网关。API 密钥覆盖模式使用 `https://api.x.ai/v1`,并可能注入 Priority Processing。优先使用实时 Grok 目录;回退默认模型为 `grok-4.5`。 | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude 模型;实时模型列表从 `/v1/models` 获取。 | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 编程模型。 | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research 订阅网关(与 Hermes Agent 使用同一后端)。通过设备授权登录 `portal.nousresearch.com`;access 令牌是每个请求的 inference JWT。付费 + `:free` 模型混合目录(`tencent/hy3:free`、`stepfun/step-3.7-flash:free` 等)会从已登录账户实时发现。Refresh 令牌是单次使用,每次刷新都会轮换。 | From d4023aedda625b81ab234bfecfa396dbd6a092e8 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Fri, 21 Aug 2026 06:28:10 +0000 Subject: [PATCH 7/8] docs(xai): separate the OAuth gateway row in the remaining locales 33e1c3e08 split the OAuth subscription gateway from the API-key endpoint in the provider table for en, ja, ko, ru, and zh-cn, but zh-tw, fr, and tr kept https://api.x.ai/v1 as the base URL. OAuth routes through the Grok CLI gateway at https://cli-chat-proxy.grok.com/v1; only the API-key override targets api.x.ai, and only that transport injects Priority Processing. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017zpLCh4eEms6un3VjapRgL --- docs-site/src/content/docs/fr/guides/providers.md | 2 +- docs-site/src/content/docs/tr/guides/providers.md | 2 +- docs-site/src/content/docs/zh-tw/guides/providers.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs-site/src/content/docs/fr/guides/providers.md b/docs-site/src/content/docs/fr/guides/providers.md index 59381edd60..40ee8021f1 100644 --- a/docs-site/src/content/docs/fr/guides/providers.md +++ b/docs-site/src/content/docs/fr/guides/providers.md @@ -115,7 +115,7 @@ ocx logout | Fournisseur | Adaptateur | URL de base | Remarques | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | Catalogue Grok découvert en direct en priorité ; `grok-4.5` est le modèle de repli par défaut. | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth utilise la passerelle d'abonnement Grok CLI distincte. Le remplacement par clé API utilise `https://api.x.ai/v1` et peut injecter Priority Processing. Catalogue Grok découvert en direct en priorité ; `grok-4.5` est le modèle de repli par défaut. | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Modèles Claude ; liste des modèles récupérée en direct depuis `/v1/models`. | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Modèles de programmation Kimi K2.7/K2.6/K2.5. | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Passerelle d'abonnement Nous Research (le même service en amont que celui utilisé par Hermes Agent). Connexion par autorisation d'appareil auprès de `portal.nousresearch.com` ; le jeton d'accès est le JWT d'inférence envoyé avec chaque requête. Le catalogue mixte de modèles payants et `:free` (`tencent/hy3:free`, `stepfun/step-3.7-flash:free`, ...) est découvert en direct pour le compte connecté. Les jetons d'actualisation sont à usage unique et renouvelés à chaque actualisation. | diff --git a/docs-site/src/content/docs/tr/guides/providers.md b/docs-site/src/content/docs/tr/guides/providers.md index ee153a0780..15e2ab3cf4 100644 --- a/docs-site/src/content/docs/tr/guides/providers.md +++ b/docs-site/src/content/docs/tr/guides/providers.md @@ -129,7 +129,7 @@ ocx logout | Sağlayıcı | Adaptör | Temel URL | Notlar | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | Canlı öncelikli Grok kataloğu; `grok-4.5` geri dönüş varsayılanıdır. | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth ayrı Grok CLI abonelik ağ geçidini kullanır. API anahtarı geçersiz kılması `https://api.x.ai/v1` kullanır ve Priority Processing ekleyebilir. Canlı öncelikli Grok kataloğu; `grok-4.5` geri dönüş varsayılanıdır. | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude modelleri; canlı model listesi `/v1/models` üzerinden getirilir. | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 kodlama modelleri. | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research abonelik ağ geçidi (Hermes Agent'ın kullandığı aynı arka uç). `portal.nousresearch.com`'a karşı cihaz yetkilendirmesi girişi; erişim belirteci istek başına çıkarım JWT'sidir. Oturum açmış hesaptan canlı olarak keşfedilen karışık ücretli + `:free` model kataloğu (`tencent/hy3:free`, `stepfun/step-3.7-flash:free`, ...). Yenileme belirteçleri tek kullanımlıktır ve her yenilemede döndürülür. | diff --git a/docs-site/src/content/docs/zh-tw/guides/providers.md b/docs-site/src/content/docs/zh-tw/guides/providers.md index ee7d709880..28298c768a 100644 --- a/docs-site/src/content/docs/zh-tw/guides/providers.md +++ b/docs-site/src/content/docs/zh-tw/guides/providers.md @@ -103,7 +103,7 @@ ocx logout | 供應商 | Adapter | Base URL | 備註 | | --- | --- | --- | --- | -| `xai` | `openai-chat` | `https://api.x.ai/v1` | 優先使用即時 Grok catalog;fallback 預設為 `grok-4.5`。 | +| `xai` | `openai-chat` | `https://cli-chat-proxy.grok.com/v1` | OAuth 使用獨立的 Grok CLI 訂閱 gateway。API key 覆寫使用 `https://api.x.ai/v1`,並可能注入 Priority Processing。優先使用即時 Grok catalog;fallback 預設為 `grok-4.5`。 | | `anthropic` | `anthropic` | `https://api.anthropic.com` | Claude 模型;即時模型列表從 `/v1/models` 取得。 | | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi K2.7/K2.6/K2.5 coding 模型。 | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research 訂閱 gateway(Hermes Agent 使用相同 backend)。透過 `portal.nousresearch.com` 做 device-grant 登入;access token 是每次請求使用的 inference JWT。混合付費與 `:free` 模型 catalog(`tencent/hy3:free`、`stepfun/step-3.7-flash:free` 等)會從已登入帳號即時探索。Refresh token 為單次使用,每次 refresh 都會輪換。 | From e8c62a90dba91c185fe706f7b45c007cd53b7ed5 Mon Sep 17 00:00:00 2001 From: olddonkey Date: Fri, 21 Aug 2026 01:41:51 -0700 Subject: [PATCH 8/8] test(fastwire): expect xAI key-auth chat to forward Fast #2255's wire/tier matrix assumed API-key Grok chat does not forward service_tier. B2 documents Priority Processing on that transport, so the key-auth chat row must now match the Responses key-auth row. OAuth remains unclassified. --- tests/fastwire-policy.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fastwire-policy.test.ts b/tests/fastwire-policy.test.ts index 412d0524a4..a29debcb34 100644 --- a/tests/fastwire-policy.test.ts +++ b/tests/fastwire-policy.test.ts @@ -288,6 +288,7 @@ describe("resolveFastPolicy matrix", () => { settledCallerTier: undefined, }, { + // B2: key-auth Chat Completions is a documented Priority Processing transport. name: "xAI API-key default", providerName: "xai", modelIds: ["grok-4.6", "grok-4.5"], @@ -297,7 +298,7 @@ describe("resolveFastPolicy matrix", () => { authMode: "key" as const, }, adapter: "openai-chat", - forwardCallerTier: false, + forwardCallerTier: true, callerTier: undefined, settledCallerTier: undefined, },