From 194d76b8434726d3ca6dc15a8d0533678857ad92 Mon Sep 17 00:00:00 2001 From: RCD <90105158+Finesssee@users.noreply.github.com> Date: Thu, 1 Oct 2026 05:48:17 +0700 Subject: [PATCH] Port upstream 0.70.0: offer Mistral Monthly Plan metric The Monthly Plan menu bar metric now selects the provider's plan allowance window (Mistral's Vibe plan, mistral-monthly-plan) instead of a spend window. A missing or unknown plan falls back to the Included API allowance. Providers declare the window through a Provider hook, so the shell has no Mistral branch. The picker offers Automatic, Included API and Monthly Plan for Mistral, and the tray label shows the plan percent when it resolves, otherwise the current-month spend. provider_status_label now takes the caller's settings instead of loading the real settings file, which keeps its tests hermetic. --- .../src-tauri/src/commands/provider_detail.rs | 9 ++ .../src-tauri/src/tray_bridge.rs | 73 ++++++--- .../src-tauri/src/usage_metric.rs | 149 +++++++++++++++++- apps/desktop-tauri/src/i18n/keys.ts | 2 + .../sections/MenuBarMetricSection.test.tsx | 76 +++++++++ .../sections/MenuBarMetricSection.tsx | 30 +++- apps/desktop-tauri/src/types/bridge.ts | 4 + rust/src/core/provider.rs | 15 ++ rust/src/locale.rs | 2 + rust/src/locale/en-US.ftl | 1 + rust/src/locale/ru-RU.ftl | 1 + rust/src/locale/tr-TR.ftl | 1 + rust/src/locale/zh-TW.ftl | 1 + rust/src/providers/mistral/mod.rs | 29 +++- rust/src/settings/types.rs | 8 +- 15 files changed, 371 insertions(+), 30 deletions(-) diff --git a/apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs b/apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs index 418bd5c146..610746dd98 100644 --- a/apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs +++ b/apps/desktop-tauri/src-tauri/src/commands/provider_detail.rs @@ -28,6 +28,13 @@ pub struct ProviderDetail { /// 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>, + /// Id of the extra rate window holding the provider's monthly plan + /// allowance (upstream 0.70.0 #4072). Offers the Monthly Plan metric in + /// the settings metric picker. + pub monthly_plan_window_id: Option<&'static str>, + /// Metric picker label for the primary lane when it is not a session + /// window (upstream `menuBarLayoutPrimaryLabel`). + pub primary_metric_label: Option<&'static str>, pub extra_rate_windows: Vec, pub usage_items: Vec, pub hidden_usage_item_ids: Vec, @@ -100,6 +107,8 @@ pub(crate) fn build_provider_detail( model_specific: None, tertiary: None, tertiary_label_key: metadata.tertiary_label_key, + monthly_plan_window_id: provider.monthly_plan_window_id(), + primary_metric_label: provider.menu_bar_primary_label(), extra_rate_windows: Vec::new(), usage_items: Vec::new(), hidden_usage_item_ids: settings.hidden_usage_item_ids(id), diff --git a/apps/desktop-tauri/src-tauri/src/tray_bridge.rs b/apps/desktop-tauri/src-tauri/src/tray_bridge.rs index 882573b666..be67b2369d 100644 --- a/apps/desktop-tauri/src-tauri/src/tray_bridge.rs +++ b/apps/desktop-tauri/src-tauri/src/tray_bridge.rs @@ -513,7 +513,7 @@ fn status_labels_for_settings( if settings.tray_icon_mode == TrayIconMode::PerProvider { return healthy .into_iter() - .map(|s| provider_status_label(s, lang)) + .map(|s| provider_status_label(s, settings, lang)) .collect::>(); } @@ -524,7 +524,7 @@ fn status_labels_for_settings( return vec![]; }; - let (_, label) = provider_status_label(selected, lang); + let (_, label) = provider_status_label(selected, settings, lang); vec![("status_summary".to_string(), label)] } @@ -554,25 +554,34 @@ fn ordered_snapshot_refs<'a>( fn provider_status_label( snapshot: &crate::commands::ProviderUsageSnapshot, + settings: &Settings, lang: codexbar::settings::Language, ) -> (String, String) { - // MonthlyPlan metric (PAYG spend, e.g. Mistral): show formatted cost. let provider = codexbar::core::ProviderId::from_cli_name(&snapshot.provider_id); let preference = provider - .map(|id| Settings::load().get_provider_metric(id)) + .map(|id| settings.get_provider_metric(id)) .unwrap_or_default(); - if preference == MetricPreference::MonthlyPlan - && let Some(cost) = snapshot.cost.as_ref() - { - let amount = if !cost.formatted_used.is_empty() { - cost.formatted_used.clone() - } else { - crate::commands::format_cost_amount(cost) - }; - return ( - snapshot.provider_id.clone(), - format!("{} {}", snapshot.display_name, amount), - ); + if preference == MetricPreference::MonthlyPlan { + // Upstream 0.70.0 (#4072): label the provider's plan allowance when + // it resolves, otherwise keep the current-month spend amount. + if let Some(plan) = crate::usage_metric::monthly_plan_window(snapshot, provider) { + let label = crate::commands::compact_tray_status_label(plan, lang); + return ( + snapshot.provider_id.clone(), + format!("{} {}", snapshot.display_name, label), + ); + } + if let Some(cost) = snapshot.cost.as_ref() { + let amount = if !cost.formatted_used.is_empty() { + cost.formatted_used.clone() + } else { + crate::commands::format_cost_amount(cost) + }; + return ( + snapshot.provider_id.clone(), + format!("{} {}", snapshot.display_name, amount), + ); + } } let label = crate::commands::compact_tray_status_label(headline_window(snapshot), lang); @@ -1315,14 +1324,42 @@ mod tests { "{japanese_tooltip}" ); + let settings = Settings::default(); let (_, english_label) = - provider_status_label(&claude, codexbar::settings::Language::English); + provider_status_label(&claude, &settings, codexbar::settings::Language::English); let (_, japanese_label) = - provider_status_label(&claude, codexbar::settings::Language::Japanese); + provider_status_label(&claude, &settings, codexbar::settings::Language::Japanese); assert!(english_label.contains("Resets in"), "{english_label}"); assert!(japanese_label.contains("リセットまで"), "{japanese_label}"); } + #[test] + fn mistral_monthly_plan_tray_label_shows_the_plan_then_the_spend() { + let mut settings = Settings::default(); + settings.set_provider_metric(ProviderId::Mistral, MetricPreference::MonthlyPlan); + settings.show_as_used = true; + let lang = codexbar::settings::Language::English; + let mut mistral = + fake_snapshot_with("mistral", "Mistral", 2.0, None, None, Some((12.5, 50.0))); + let mut plan = fake_extra_window(42.0); + plan.id = codexbar::providers::mistral::MONTHLY_PLAN_WINDOW_ID.to_string(); + plan.title = "Monthly Plan".to_string(); + mistral.extra_rate_windows = vec![plan]; + + let (_, label) = provider_status_label(&mistral, &settings, lang); + assert_eq!(label, "Mistral 42%"); + assert_eq!(selected_tray_percents(&mistral, &settings).0, 42.0); + + // Without a known plan allowance the label keeps the spend text. + mistral.extra_rate_windows[0].window.is_informational = true; + let (_, label) = provider_status_label(&mistral, &settings, lang); + assert_eq!(label, "Mistral $12.50"); + + // Automatic keeps the existing headline label. + let (_, label) = provider_status_label(&mistral, &Settings::default(), lang); + assert_eq!(label, "Mistral 2%"); + } + #[test] fn selected_tray_percent_uses_cursor_extra_usage_cost() { let mut settings = Settings::default(); diff --git a/apps/desktop-tauri/src-tauri/src/usage_metric.rs b/apps/desktop-tauri/src-tauri/src/usage_metric.rs index 65a2408a15..9ee7254a94 100644 --- a/apps/desktop-tauri/src-tauri/src/usage_metric.rs +++ b/apps/desktop-tauri/src-tauri/src/usage_metric.rs @@ -95,10 +95,44 @@ fn preferred_window( extra_usage_window(snapshot).or_else(|| cost_window(snapshot)) } MetricPreference::Average => average_window(snapshot), - MetricPreference::MonthlyPlan => cost_window(snapshot), + // Upstream 0.70.0 (#4072): the provider's plan allowance. A missing + // or unknown plan falls back to the primary (included API) allowance, + // never to a spend window. Providers without a plan window fall + // through to Automatic. + MetricPreference::MonthlyPlan => { + let window_id = provider.and_then(monthly_plan_window_id)?; + plan_window(snapshot, window_id) + .or_else(|| non_informational(Some(&snapshot.primary))) + .cloned() + } } } +/// The provider-declared monthly plan allowance, when the snapshot carries a +/// known value for it. Providers without a plan window return `None`. +pub(crate) fn monthly_plan_window( + snapshot: &ProviderUsageSnapshot, + provider: Option, +) -> Option<&RateWindowSnapshot> { + plan_window(snapshot, provider.and_then(monthly_plan_window_id)?) +} + +fn monthly_plan_window_id(provider: ProviderId) -> Option<&'static str> { + codexbar::core::instantiate_provider(provider).monthly_plan_window_id() +} + +fn plan_window<'a>( + snapshot: &'a ProviderUsageSnapshot, + window_id: &str, +) -> Option<&'a RateWindowSnapshot> { + snapshot + .extra_rate_windows + .iter() + .find(|extra| extra.id == window_id) + .map(|extra| &extra.window) + .filter(|window| !window.is_informational) +} + fn automatic_window( snapshot: &ProviderUsageSnapshot, provider: Option, @@ -606,6 +640,119 @@ mod tests { assert_eq!(selected.remaining_percent, 60.0); } + fn mistral_snapshot(plan: Option) -> ProviderUsageSnapshot { + let mut snapshot = snapshot(); + snapshot.provider_id = "mistral".to_string(); + snapshot.display_name = "Mistral".to_string(); + snapshot.primary = window(2.0); + snapshot.primary_label = Some("Included API".to_string()); + snapshot.secondary = None; + snapshot.extra_rate_windows = plan + .map(|window| crate::commands::NamedRateWindowSnapshot { + id: codexbar::providers::mistral::MONTHLY_PLAN_WINDOW_ID.to_string(), + title: "Monthly Plan".to_string(), + window, + fallback_lane: false, + }) + .into_iter() + .collect(); + snapshot + } + + fn metric_settings(provider: ProviderId, preference: MetricPreference) -> Settings { + let mut settings = Settings::default(); + settings.set_provider_metric(provider, preference); + settings + } + + #[test] + fn mistral_monthly_plan_selects_the_vibe_plan_window() { + let mut snapshot = mistral_snapshot(Some(window(42.0))); + snapshot.primary = window(80.0); + + let monthly_plan = metric_settings(ProviderId::Mistral, MetricPreference::MonthlyPlan); + assert_eq!( + selected_usage_window(&snapshot, &monthly_plan).used_percent, + 42.0 + ); + // Included API and Automatic stay separate choices (upstream #4072). + let included_api = metric_settings(ProviderId::Mistral, MetricPreference::Session); + assert_eq!( + selected_usage_window(&snapshot, &included_api).used_percent, + 80.0 + ); + assert_eq!( + selected_usage_window(&snapshot, &Settings::default()).used_percent, + 80.0 + ); + let presentation = + crate::commands::ProviderUsagePresentationSnapshot::new(snapshot, &monthly_plan); + assert_eq!(presentation.selected_metric.used_percent, 42.0); + } + + #[test] + fn mistral_monthly_plan_falls_back_to_included_api_without_a_known_plan() { + let settings = metric_settings(ProviderId::Mistral, MetricPreference::MonthlyPlan); + let unknown_plan = RateWindowSnapshot { + is_informational: true, + ..window(0.0) + }; + + for snapshot in [mistral_snapshot(None), mistral_snapshot(Some(unknown_plan))] { + let selected = selected_usage_window(&snapshot, &settings); + assert_eq!(selected.used_percent, 2.0); + assert!(!selected.is_informational); + } + } + + #[test] + fn mistral_monthly_plan_never_selects_a_spend_window() { + let mut snapshot = mistral_snapshot(None); + snapshot.cost = Some(crate::commands::CostSnapshotBridge { + used: 45.0, + limit: Some(50.0), + remaining: Some(5.0), + currency_code: "EUR".to_string(), + currency_symbol: Some("€".to_string()), + period: "month".to_string(), + resets_at: None, + formatted_used: "€45.00".to_string(), + formatted_limit: Some("€50.00".to_string()), + balance: None, + balance_updated_at: None, + account_id: None, + formatted_balance: None, + daily: Vec::new(), + always_visible: false, + }); + let settings = metric_settings(ProviderId::Mistral, MetricPreference::MonthlyPlan); + + assert_eq!( + selected_usage_window(&snapshot, &settings).used_percent, + 2.0 + ); + } + + #[test] + fn monthly_plan_without_a_provider_plan_window_falls_through_to_automatic() { + let mut snapshot = snapshot(); + snapshot.extra_rate_windows = vec![crate::commands::NamedRateWindowSnapshot { + id: codexbar::providers::mistral::MONTHLY_PLAN_WINDOW_ID.to_string(), + title: "Monthly Plan".to_string(), + window: window(90.0), + fallback_lane: false, + }]; + let settings = metric_settings(ProviderId::Codex, MetricPreference::MonthlyPlan); + + // Codex declares no plan window, so a same-named lane is not special + // and the choice resolves like Automatic (highest real window). + assert_eq!( + selected_usage_window(&snapshot, &settings).used_percent, + selected_usage_window(&snapshot, &Settings::default()).used_percent + ); + assert!(monthly_plan_window(&snapshot, Some(ProviderId::Codex)).is_none()); + } + #[test] fn presentation_payload_flattens_the_snapshot_and_selected_metric() { let presentation = crate::commands::ProviderUsagePresentationSnapshot::new( diff --git a/apps/desktop-tauri/src/i18n/keys.ts b/apps/desktop-tauri/src/i18n/keys.ts index 195620e3bf..fb9dc170d3 100644 --- a/apps/desktop-tauri/src/i18n/keys.ts +++ b/apps/desktop-tauri/src/i18n/keys.ts @@ -896,6 +896,8 @@ export const ALL_LOCALE_KEYS = [ // Mistral PAYG monthly spend (#2821, #2947) "MistralMonthlySpend", "MistralMonthlySpendHelper", + // Mistral Monthly Plan menu bar metric (upstream 0.70.0 #4072) + "MetricMonthlyPlan", // Menu cost-summary display style (#2976) "CostSummaryDisplayStyle", diff --git a/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.test.tsx b/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.test.tsx index 46f0e21cd2..a9dd63f3e8 100644 --- a/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.test.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.test.tsx @@ -36,6 +36,25 @@ function provider(extra = true): ProviderDetail { }; } +function mistral(observed = true): ProviderDetail { + return { + ...provider(false), + id: "mistral", + displayName: "Mistral", + primaryMetricLabel: "Included API", + monthlyPlanWindowId: "mistral-monthly-plan", + session: observed ? rateWindow(2) : null, + extraRateWindows: observed + ? [{ id: "mistral-monthly-plan", title: "Monthly Plan", window: rateWindow(42) }] + : [], + hasSnapshot: observed, + }; +} + +function optionNames() { + return screen.getAllByRole("option").map((option) => option.textContent); +} + function rateWindow(usedPercent: number) { return { usedPercent, @@ -122,5 +141,62 @@ describe("MenuBarMetricSection", () => { expect(onChange).toHaveBeenCalledWith({ providerMetrics: { copilot: "extraUsage" }, }); + expect(screen.queryByRole("option", { name: "MetricMonthlyPlan" })).toBeNull(); + }); + + it("offers Included API and Monthly Plan for a provider plan window", () => { + const onChange = vi.fn(); + render( + key} + onChange={onChange} + />, + ); + + // Upstream 0.70.0: Automatic, Included API and Monthly Plan only. The + // plan window does not add a separate Extra usage choice. + expect(optionNames()).toEqual(["Automatic", "Included API", "MetricMonthlyPlan"]); + fireEvent.change(screen.getByRole("combobox"), { target: { value: "monthlyPlan" } }); + expect(onChange).toHaveBeenCalledWith({ + providerMetrics: { mistral: "monthlyPlan" }, + }); + }); + + it("offers Monthly Plan before the first Mistral snapshot", () => { + render( + key} + onChange={vi.fn()} + />, + ); + + expect(optionNames()).toEqual(["Automatic", "Included API", "MetricMonthlyPlan"]); + expect(screen.getByRole("combobox")).toHaveValue("monthlyPlan"); + }); + + it("keeps a saved choice the provider no longer offers readable", () => { + render( + key} + onChange={vi.fn()} + />, + ); + + expect(optionNames()).toEqual([ + "Automatic", + "Included API", + "MetricMonthlyPlan", + "ExtraUsage", + ]); + expect(screen.getByRole("combobox")).toHaveValue("extraUsage"); }); }); diff --git a/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.tsx b/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.tsx index 7bc89e0d7d..e151249acd 100644 --- a/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.tsx +++ b/apps/desktop-tauri/src/surfaces/settings/providers/sections/MenuBarMetricSection.tsx @@ -78,7 +78,12 @@ function metricOptions( ): MetricOption[] { const options: MetricOption[] = [ { value: "automatic", label: t("Automatic") }, - { value: "session", label: t("ProviderSessionLabel") }, + { + value: "session", + // Upstream 0.70.0: a primary lane that is not a session window keeps + // its provider name in the picker (Mistral's "Included API"). + label: provider.primaryMetricLabel ?? t("ProviderSessionLabel"), + }, ]; if (provider.weekly) { @@ -95,21 +100,36 @@ function metricOptions( ), }); } - if (provider.id === "cursor" || provider.extraRateWindows.length > 0) { + // The monthly plan window has its own choice below, so it does not make + // Extra usage available on its own. + const extraWindows = provider.extraRateWindows.filter( + (extra) => extra.id !== provider.monthlyPlanWindowId, + ); + if (provider.id === "cursor" || extraWindows.length > 0) { options.push({ value: "extraUsage", label: t("ExtraUsage") }); } - if (provider.id === "mistral") { - options.push({ value: "monthlyPlan", label: t("MistralMonthlySpend") }); + // Upstream 0.70.0 (#4072): providers that publish a monthly plan + // allowance window offer it for the menu bar and widgets. + if (provider.monthlyPlanWindowId) { + options.push({ value: "monthlyPlan", label: t("MetricMonthlyPlan") }); } if (provider.id === "gemini" && provider.weekly) { options.push({ value: "average", label: t("Average") }); } if (!options.some((option) => option.value === selected)) { + const labelKey = SAVED_ONLY_LABEL_KEYS[selected]; options.push({ value: selected, - label: selected === "credits" ? t("CreditsLabel") : selected, + label: labelKey ? t(labelKey) : selected, }); } return options; } + +/** Labels for saved choices the provider no longer offers. */ +const SAVED_ONLY_LABEL_KEYS: Partial> = { + credits: "CreditsLabel", + extraUsage: "ExtraUsage", + monthlyPlan: "MetricMonthlyPlan", +}; diff --git a/apps/desktop-tauri/src/types/bridge.ts b/apps/desktop-tauri/src/types/bridge.ts index 53d8c843ea..197f3c337d 100644 --- a/apps/desktop-tauri/src/types/bridge.ts +++ b/apps/desktop-tauri/src/types/bridge.ts @@ -963,6 +963,10 @@ export interface ProviderDetail { tertiary: RateWindowSnapshot | null; /** Locale key for the tertiary metric lane when it carries a semantic label (upstream F5). */ tertiaryLabelKey?: string | null; + /** Extra rate window id holding the monthly plan allowance; offers the Monthly Plan metric (upstream 0.70.0). */ + monthlyPlanWindowId?: string | null; + /** Metric picker label for the primary lane when it is not a session window. */ + primaryMetricLabel?: string | null; extraRateWindows: Array<{ id: string; title: string; diff --git a/rust/src/core/provider.rs b/rust/src/core/provider.rs index cc954839f7..a2c8ca39f9 100755 --- a/rust/src/core/provider.rs +++ b/rust/src/core/provider.rs @@ -892,6 +892,21 @@ pub trait Provider: Send + Sync { true } + /// Id of the extra rate window that holds this provider's monthly plan + /// allowance. The `MonthlyPlan` menu bar metric selects that window + /// (upstream 0.70.0 #4072). `None` means the provider offers no Monthly + /// Plan metric. + fn monthly_plan_window_id(&self) -> Option<&'static str> { + None + } + + /// Label for the primary lane in the menu bar metric picker when the + /// lane is not a session window (upstream `menuBarLayoutPrimaryLabel`). + /// `None` keeps the generic session label. + fn menu_bar_primary_label(&self) -> Option<&'static str> { + None + } + /// Whether browser-cookie discovery/recovery is owned by the provider. fn owns_browser_cookie_resolution(&self) -> bool { false diff --git a/rust/src/locale.rs b/rust/src/locale.rs index e6ec918ac4..ebc3084476 100644 --- a/rust/src/locale.rs +++ b/rust/src/locale.rs @@ -1176,6 +1176,8 @@ locale_keys! { // Mistral PAYG monthly spend (#2821, #2947) MistralMonthlySpend, MistralMonthlySpendHelper, + // Mistral Monthly Plan menu bar metric (upstream 0.70.0 #4072) + MetricMonthlyPlan, // Menu cost-summary display style (#2976) CostSummaryDisplayStyle, diff --git a/rust/src/locale/en-US.ftl b/rust/src/locale/en-US.ftl index fb96ecd4b8..74d685987b 100644 --- a/rust/src/locale/en-US.ftl +++ b/rust/src/locale/en-US.ftl @@ -826,6 +826,7 @@ DeepSeekPricingEffective = Effective local time: DeepSeekPricingAdvice = Official schedule: peak 01:00-04:00 and 06:00-10:00 UTC; off-peak is half-price. MistralMonthlySpend = Monthly API spend MistralMonthlySpendHelper = Current-month API usage for pay-as-you-go Mistral accounts. +MetricMonthlyPlan = Monthly Plan CostSummaryDisplayStyle = Cost summary display CostSummaryDisplayStyleHelper = Choose how cost is shown on every provider card. CostSummaryStyleCompact = Compact diff --git a/rust/src/locale/ru-RU.ftl b/rust/src/locale/ru-RU.ftl index f978d540e7..3420e2ea1f 100644 --- a/rust/src/locale/ru-RU.ftl +++ b/rust/src/locale/ru-RU.ftl @@ -742,6 +742,7 @@ DeepSeekPricingEffective = Effective local time: DeepSeekPricingAdvice = Official schedule: peak 01:00-04:00 and 06:00-10:00 UTC; off-peak is half-price. MistralMonthlySpend = Расход на API за месяц MistralMonthlySpendHelper = Использование API за текущий месяц для аккаунтов Mistral с оплатой по мере использования. +MetricMonthlyPlan = Ежемесячный план CostSummaryDisplayStyle = Стиль отображения стоимости CostSummaryDisplayStyleHelper = Выберите, как стоимость отображается на каждой карте провайдера. CostSummaryStyleCompact = Компактный diff --git a/rust/src/locale/tr-TR.ftl b/rust/src/locale/tr-TR.ftl index 08de23e2e3..86e879ba52 100644 --- a/rust/src/locale/tr-TR.ftl +++ b/rust/src/locale/tr-TR.ftl @@ -753,6 +753,7 @@ Sub2ApiBaseUrlHelp = GET /v1/usage için dağıtım URL'si. Yerel geliştirme i PromoteTrayIconLabel = Görev Çubuğuna Sabitle (Windows 11) PromoteTrayIconHelper = CodexBar simgesini taşma şevronunun içinde değil, her zaman görev çubuğunda göster PromoteTrayIconUnsupportedHint = Bu işletim sisteminde kullanılamaz. Elle sabitlemek için simgeyi Windows Ayarlar > Görev Çubuğu'ndaki ^ taşma alanının dışına sürükleyin. +MetricMonthlyPlan = Aylık plan UsageSpendShare = Paylaşım kartını dışa aktar (PNG) UsageSpendShareEmpty = Henüz dışa aktarılacak bir şey yok. UsageSpendShareFailed = Paylaşım kartı görseli oluşturulamadı. diff --git a/rust/src/locale/zh-TW.ftl b/rust/src/locale/zh-TW.ftl index 31bb3bcf02..29d9b73366 100644 --- a/rust/src/locale/zh-TW.ftl +++ b/rust/src/locale/zh-TW.ftl @@ -763,6 +763,7 @@ DeepSeekPricingEffective = Effective local time: DeepSeekPricingAdvice = Official schedule: peak 01:00-04:00 and 06:00-10:00 UTC; off-peak is half-price. MistralMonthlySpend = 本月 API 花費 MistralMonthlySpendHelper = 按量付費 Mistral 帳戶的當月 API 用量。 +MetricMonthlyPlan = 月租方案 CostSummaryDisplayStyle = 費用摘要顯示方式 CostSummaryDisplayStyleHelper = 選擇在每個提供商卡片上如何顯示費用。 CostSummaryStyleCompact = 緊湊 diff --git a/rust/src/providers/mistral/mod.rs b/rust/src/providers/mistral/mod.rs index 33f82bc908..561a96c093 100644 --- a/rust/src/providers/mistral/mod.rs +++ b/rust/src/providers/mistral/mod.rs @@ -27,6 +27,12 @@ const CLIENT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); /// `/subscription` render can never stall the refresh; degraded enrichment is /// logged and skipped, never fatal. const SUBSCRIPTION_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(4); +/// Extra rate window holding the Vibe monthly plan allowance. The Monthly Plan +/// menu bar metric selects it (upstream 0.70.0 #4072). +pub const MONTHLY_PLAN_WINDOW_ID: &str = "mistral-monthly-plan"; +/// Picker label for the primary lane, the included API allowance (upstream +/// `menuBarLayoutPrimaryLabel`). +const INCLUDED_API_LABEL: &str = "Included API"; #[derive(Debug, Deserialize)] struct BillingResponse { @@ -417,11 +423,11 @@ impl MistralProvider { if let Some(budgets) = budgets { if let Some(api) = budgets.api { usage.primary = Self::budget_window(&api); - usage.primary_label = Some("Included API".to_string()); + usage.primary_label = Some(INCLUDED_API_LABEL.to_string()); } if let Some(vibe) = budgets.vibe { usage.extra_rate_windows.push(NamedRateWindow::new( - "mistral-monthly-plan", + MONTHLY_PLAN_WINDOW_ID, "Monthly Plan", Self::budget_window(&vibe), )); @@ -576,6 +582,14 @@ impl Provider for MistralProvider { fn supports_web(&self) -> bool { true } + + fn monthly_plan_window_id(&self) -> Option<&'static str> { + Some(MONTHLY_PLAN_WINDOW_ID) + } + + fn menu_bar_primary_label(&self) -> Option<&'static str> { + Some(INCLUDED_API_LABEL) + } } #[cfg(test)] @@ -627,6 +641,17 @@ mod tests { ); } + #[test] + fn monthly_plan_metric_targets_the_published_vibe_window() { + let provider = MistralProvider::new(); + + assert_eq!( + provider.monthly_plan_window_id(), + Some("mistral-monthly-plan") + ); + assert_eq!(provider.menu_bar_primary_label(), Some("Included API")); + } + #[test] fn attaches_subscription_allowances_without_replacing_billing_cost() { let summary = MistralUsageSummary { diff --git a/rust/src/settings/types.rs b/rust/src/settings/types.rs index 6113969b15..670012b167 100644 --- a/rust/src/settings/types.rs +++ b/rust/src/settings/types.rs @@ -280,8 +280,8 @@ pub enum MetricPreference { Credits, #[serde(rename = "extraUsage", alias = "extrausage")] ExtraUsage, - /// Current-month plan spend for PAYG providers (e.g. Mistral) that have - /// cost data but no rate-limit window (#2821, #2947). + /// The provider's monthly plan allowance window, such as Mistral's Vibe + /// plan (upstream 0.70.0 #4072). MonthlyPlan, Average, } @@ -312,7 +312,7 @@ impl MetricPreference { MetricPreference::Tertiary => "Tertiary", MetricPreference::Credits => "Credits", MetricPreference::ExtraUsage => "Extra usage", - MetricPreference::MonthlyPlan => "Monthly plan spend", + MetricPreference::MonthlyPlan => "Monthly plan", MetricPreference::Average => "Average", } } @@ -327,7 +327,7 @@ impl MetricPreference { MetricPreference::Tertiary => "Tertiary usage limit", MetricPreference::Credits => "Credit balance", MetricPreference::ExtraUsage => "On-demand or extra usage budget", - MetricPreference::MonthlyPlan => "Current-month plan spend (PAYG)", + MetricPreference::MonthlyPlan => "Monthly plan allowance", MetricPreference::Average => "Average across metrics", } }