Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub struct ProviderDetail {
pub weekly: Option<RateWindowSnapshot>,
pub model_specific: Option<RateWindowSnapshot>,
pub tertiary: Option<RateWindowSnapshot>,
/// Locale key naming the tertiary lane when it carries a semantic label
/// beyond "Tertiary" (upstream F5). Drives the settings metric picker.
pub tertiary_label_key: Option<&'static str>,
pub extra_rate_windows: Vec<NamedRateWindowSnapshot>,

// Cost / pace.
Expand Down Expand Up @@ -90,6 +93,7 @@ pub(crate) fn build_provider_detail(provider_id: &str) -> Result<ProviderDetail,
weekly: None,
model_specific: None,
tertiary: None,
tertiary_label_key: metadata.tertiary_label_key,
extra_rate_windows: Vec::new(),
cost: None,
pace: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,28 @@ function rateWindow(usedPercent: number) {
}

describe("MenuBarMetricSection", () => {
it("offers Monthly for OpenCode Go only after a tertiary window is observed", () => {
it("renders the provider-declared tertiary label key before observation", () => {
const base = provider(false);
base.id = "opencodego";
base.displayName = "OpenCode Go";
base.tertiary = null;
base.tertiaryLabelKey = "ProviderMonthly";
const onChange = vi.fn();
const { rerender } = render(
<MenuBarMetricSection
provider={base}
providerMetrics={{}}
disabled={false}
t={(key) => key}
onChange={vi.fn()}
onChange={onChange}
/>,
);

expect(screen.queryByRole("option", { name: "DetailWindowTertiary" })).not.toBeInTheDocument();
expect(screen.getByRole("option", { name: "ProviderMonthly" })).toBeInTheDocument();
fireEvent.change(screen.getByRole("combobox"), { target: { value: "tertiary" } });
expect(onChange).toHaveBeenCalledWith({
providerMetrics: { opencodego: "tertiary" },
});

const observed = { ...base, tertiary: rateWindow(37) };
rerender(
Expand All @@ -78,8 +84,26 @@ describe("MenuBarMetricSection", () => {
/>,
);

expect(screen.getByRole("option", { name: "ProviderMonthly" })).toBeInTheDocument();
});

it("keeps the generic tertiary label when no provider key is declared", () => {
const base = provider(false);
base.tertiary = rateWindow(37);

render(
<MenuBarMetricSection
provider={base}
providerMetrics={{}}
disabled={false}
t={(key) => key}
onChange={vi.fn()}
/>,
);

expect(screen.getByRole("option", { name: "DetailWindowTertiary" })).toBeInTheDocument();
});

it("offers extra usage when a provider has extra rate windows", () => {
const onChange = vi.fn();
render(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ function metricOptions(
if (provider.modelSpecific) {
options.push({ value: "model", label: t("DetailWindowModelSpecific") });
}
if (provider.tertiary) {
options.push({ value: "tertiary", label: t("DetailWindowTertiary") });
if (provider.tertiary || provider.tertiaryLabelKey) {
options.push({
value: "tertiary",
label: t(
(provider.tertiaryLabelKey ?? "DetailWindowTertiary") as LocaleKey,
),
});
}
if (provider.id === "cursor" || provider.extraRateWindows.length > 0) {
options.push({ value: "extraUsage", label: t("ExtraUsage") });
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src/types/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,8 @@ export interface ProviderDetail {
weekly: RateWindowSnapshot | null;
modelSpecific: RateWindowSnapshot | null;
tertiary: RateWindowSnapshot | null;
/** Locale key for the tertiary metric lane when it carries a semantic label (upstream F5). */
tertiaryLabelKey?: string | null;
extraRateWindows: Array<{
id: string;
title: string;
Expand Down
4 changes: 4 additions & 0 deletions rust/src/core/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,10 @@ pub struct ProviderMetadata {
pub is_primary: bool,
pub dashboard_url: Option<&'static str>,
pub status_page_url: Option<&'static str>,
/// Locale key shown for the provider's tertiary metric lane in settings
/// pickers when the lane carries a semantic identity beyond "Tertiary"
/// (upstream F5). `None` renders the generic tertiary label.
pub tertiary_label_key: Option<&'static str>,
}

/// Errors that can occur when fetching provider data
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/abacus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl AbacusProvider {
is_primary: false,
dashboard_url: Some("https://apps.abacus.ai/app/billing"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(30))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/aiand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl AiAndProvider {
is_primary: false,
dashboard_url: Some("https://console.aiand.com"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/alibaba/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl AlibabaProvider {
is_primary: false,
dashboard_url: Some("https://modelstudio.console.alibabacloud.com"),
status_page_url: None,
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/alibabatokenplan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl AlibabaTokenPlanProvider {
is_primary: false,
dashboard_url: Some(DEFAULT_DASHBOARD_URL),
status_page_url: Some("https://status.aliyun.com"),
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/amp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl AmpProvider {
is_primary: false,
dashboard_url: Some("https://ampcode.com/settings/usage"),
status_page_url: Some("https://sourcegraphstatus.com"),
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/antigravity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ impl AntigravityProvider {
is_primary: false,
dashboard_url: None,
status_page_url: None,
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/augment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl AugmentProvider {
is_primary: false,
dashboard_url: Some("https://app.augmentcode.com/account"),
status_page_url: Some("https://status.augmentcode.com"),
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/azureopenai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl AzureOpenAIProvider {
is_primary: false,
dashboard_url: Some("https://ai.azure.com"),
status_page_url: Some("https://status.azure.com"),
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/bedrock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ impl BedrockProvider {
is_primary: false,
dashboard_url: Some("https://console.aws.amazon.com/bedrock"),
status_page_url: Some("https://health.aws.amazon.com/health/status"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/chutes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ impl ChutesProvider {
is_primary: false,
dashboard_url: Some("https://chutes.ai"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/claude/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ impl ClaudeProvider {
is_primary: true,
dashboard_url: Some("https://claude.ai/settings/usage"),
status_page_url: Some("https://status.claude.com/"),
tertiary_label_key: None,
},
web_fetcher: ClaudeWebApiFetcher::new(),
oauth_fetcher: ClaudeOAuthFetcher::new(),
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/clinepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl ClinePassProvider {
is_primary: false,
dashboard_url: Some("https://app.cline.bot/dashboard/subscription?personal=true"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/codebuddy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl CodeBuddyProvider {
is_primary: false,
dashboard_url: Some("https://www.codebuddy.cn/profile/plans-usage"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(20))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/codebuff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl CodebuffProvider {
is_primary: false,
dashboard_url: Some("https://www.codebuff.com/usage"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(30))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/codex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ impl CodexProvider {
is_primary: true,
dashboard_url: Some("https://chatgpt.com/codex/settings/usage"),
status_page_url: Some("https://status.openai.com"),
tertiary_label_key: None,
},
api: CodexApi::new(),
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/commandcode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl CommandCodeProvider {
is_primary: false,
dashboard_url: Some("https://commandcode.ai"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/copilot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl CopilotProvider {
is_primary: false,
dashboard_url: Some("https://github.com/settings/copilot"),
status_page_url: Some("https://www.githubstatus.com/"),
tertiary_label_key: None,
},
api: CopilotApi::new(),
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/crof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl CrofProvider {
is_primary: false,
dashboard_url: Some("https://crof.ai"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/crossmodel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl CrossModelProvider {
is_primary: false,
dashboard_url: Some("https://crossmodel.ai"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/cursor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ impl CursorProvider {
is_primary: false,
dashboard_url: Some("https://cursor.com/dashboard/usage"),
status_page_url: None,
tertiary_label_key: None,
},
api: CursorApi::new(),
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/deepgram/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ impl DeepgramProvider {
is_primary: false,
dashboard_url: Some("https://console.deepgram.com/usage"),
status_page_url: Some("https://status.deepgram.com"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/deepinfra/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ impl DeepInfraProvider {
is_primary: false,
dashboard_url: Some("https://deepinfra.com/dash"),
status_page_url: Some("https://status.deepinfra.com"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(30))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/deepseek/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ impl DeepSeekProvider {
is_primary: false,
dashboard_url: Some("https://platform.deepseek.com/usage"),
status_page_url: Some("https://status.deepseek.com"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(30))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/devin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ impl DevinProvider {
is_primary: false,
dashboard_url: Some("https://app.devin.ai/settings/billing"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/doubao/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl DoubaoProvider {
"https://console.volcengine.com/ark/region:ark+cn-beijing/usage",
),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/elevenlabs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl ElevenLabsProvider {
is_primary: false,
dashboard_url: Some("https://elevenlabs.io/app/settings/api-keys"),
status_page_url: Some("https://status.elevenlabs.io"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/factory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ impl FactoryProvider {
is_primary: false,
dashboard_url: Some("https://app.factory.ai"),
status_page_url: Some("https://status.factory.ai"),
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/fireworks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ impl FireworksProvider {
is_primary: false,
dashboard_url: Some("https://app.fireworks.ai"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/gemini/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl GeminiProvider {
is_primary: false,
dashboard_url: Some("https://aistudio.google.com"),
status_page_url: Some("https://status.cloud.google.com"),
tertiary_label_key: None,
},
api: GeminiApi::new(),
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/grok/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl GrokProvider {
is_primary: false,
dashboard_url: Some("https://grok.com/?_s=usage"),
status_page_url: Some("https://status.x.ai"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/groq/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl GroqProvider {
is_primary: false,
dashboard_url: Some("https://console.groq.com/settings/metrics"),
status_page_url: Some("https://status.groq.com"),
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(15))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/infini.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ impl InfiniProvider {
is_primary: false,
dashboard_url: Some("https://cloud.infini-ai.com"),
status_page_url: None,
tertiary_label_key: None,
},
client: InfiniClient::new(api_key),
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/jetbrains/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ impl JetBrainsProvider {
is_primary: false,
dashboard_url: Some("https://www.jetbrains.com/ai/"),
status_page_url: None,
tertiary_label_key: None,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/kilo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl KiloProvider {
is_primary: false,
dashboard_url: Some("https://app.kilo.ai/usage"),
status_page_url: None,
tertiary_label_key: None,
},
client: crate::core::credentialed_http_client_builder()
.timeout(std::time::Duration::from_secs(30))
Expand Down
1 change: 1 addition & 0 deletions rust/src/providers/kimi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ impl KimiProvider {
is_primary: false,
dashboard_url: Some("https://kimi.moonshot.cn"),
status_page_url: None,
tertiary_label_key: None,
},
}
}
Expand Down
Loading