agent: Show the usage limits returned from the API (#29236)

This PR updates the usage banners in the Agent panel to use the limits
returned from the API instead of basing it off the plan.

This will allow limits to be updated from the server rather than being
embedded in the client.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-04-22 17:01:23 -04:00 committed by GitHub
parent 338a6a3b7e
commit fcc6a86c90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 79 additions and 23 deletions

View file

@ -1550,7 +1550,7 @@ impl AssistantPanel {
fn render_usage_banner(&self, cx: &mut Context<Self>) -> Option<AnyElement> { fn render_usage_banner(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
let usage = self.thread.read(cx).last_usage()?; let usage = self.thread.read(cx).last_usage()?;
Some(UsageBanner::new(zed_llm_client::Plan::ZedProTrial, usage.amount).into_any_element()) Some(UsageBanner::new(zed_llm_client::Plan::ZedProTrial, usage).into_any_element())
} }
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> { fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {

View file

@ -1,31 +1,30 @@
use client::zed_urls; use client::zed_urls;
use language_model::RequestUsage;
use ui::{Banner, ProgressBar, Severity, prelude::*}; use ui::{Banner, ProgressBar, Severity, prelude::*};
use zed_llm_client::{Plan, UsageLimit}; use zed_llm_client::{Plan, UsageLimit};
#[derive(IntoElement, RegisterComponent)] #[derive(IntoElement, RegisterComponent)]
pub struct UsageBanner { pub struct UsageBanner {
plan: Plan, plan: Plan,
requests: i32, usage: RequestUsage,
} }
impl UsageBanner { impl UsageBanner {
pub fn new(plan: Plan, requests: i32) -> Self { pub fn new(plan: Plan, usage: RequestUsage) -> Self {
Self { plan, requests } Self { plan, usage }
} }
} }
impl RenderOnce for UsageBanner { impl RenderOnce for UsageBanner {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let request_limit = self.plan.model_requests_limit(); let used_percentage = match self.usage.limit {
UsageLimit::Limited(limit) => Some((self.usage.amount as f32 / limit as f32) * 100.),
let used_percentage = match request_limit {
UsageLimit::Limited(limit) => Some((self.requests as f32 / limit as f32) * 100.),
UsageLimit::Unlimited => None, UsageLimit::Unlimited => None,
}; };
let (severity, message) = match request_limit { let (severity, message) = match self.usage.limit {
UsageLimit::Limited(limit) => { UsageLimit::Limited(limit) => {
if self.requests >= limit { if self.usage.amount >= limit {
let message = match self.plan { let message = match self.plan {
Plan::ZedPro => "Monthly request limit reached", Plan::ZedPro => "Monthly request limit reached",
Plan::ZedProTrial => "Trial request limit reached", Plan::ZedProTrial => "Trial request limit reached",
@ -33,7 +32,7 @@ impl RenderOnce for UsageBanner {
}; };
(Severity::Error, message) (Severity::Error, message)
} else if (self.requests as f32 / limit as f32) >= 0.9 { } else if (self.usage.amount as f32 / limit as f32) >= 0.9 {
(Severity::Warning, "Approaching request limit") (Severity::Warning, "Approaching request limit")
} else { } else {
let message = match self.plan { let message = match self.plan {
@ -81,11 +80,11 @@ impl RenderOnce for UsageBanner {
.child(ProgressBar::new("usage", percent, 100., cx)) .child(ProgressBar::new("usage", percent, 100., cx))
})) }))
.child( .child(
Label::new(match request_limit { Label::new(match self.usage.limit {
UsageLimit::Limited(limit) => { UsageLimit::Limited(limit) => {
format!("{} / {limit}", self.requests) format!("{} / {limit}", self.usage.amount)
} }
UsageLimit::Unlimited => format!("{} / ∞", self.requests), UsageLimit::Unlimited => format!("{} / ∞", self.usage.amount),
}) })
.size(LabelSize::Small) .size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
@ -104,74 +103,131 @@ impl Component for UsageBanner {
} }
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> { fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
let trial_limit = Plan::ZedProTrial.model_requests_limit();
let trial_examples = vec![ let trial_examples = vec![
single_example( single_example(
"Zed Pro Trial - New User", "Zed Pro Trial - New User",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::ZedProTrial, 10)) .child(UsageBanner::new(
Plan::ZedProTrial,
RequestUsage {
limit: trial_limit,
amount: 10,
},
))
.into_any_element(), .into_any_element(),
), ),
single_example( single_example(
"Zed Pro Trial - Approaching Limit", "Zed Pro Trial - Approaching Limit",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::ZedProTrial, 135)) .child(UsageBanner::new(
Plan::ZedProTrial,
RequestUsage {
limit: trial_limit,
amount: 135,
},
))
.into_any_element(), .into_any_element(),
), ),
single_example( single_example(
"Zed Pro Trial - Request Limit Reached", "Zed Pro Trial - Request Limit Reached",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::ZedProTrial, 150)) .child(UsageBanner::new(
Plan::ZedProTrial,
RequestUsage {
limit: trial_limit,
amount: 150,
},
))
.into_any_element(), .into_any_element(),
), ),
]; ];
let free_limit = Plan::Free.model_requests_limit();
let free_examples = vec![ let free_examples = vec![
single_example( single_example(
"Free - Normal Usage", "Free - Normal Usage",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::Free, 25)) .child(UsageBanner::new(
Plan::Free,
RequestUsage {
limit: free_limit,
amount: 25,
},
))
.into_any_element(), .into_any_element(),
), ),
single_example( single_example(
"Free - Approaching Limit", "Free - Approaching Limit",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::Free, 45)) .child(UsageBanner::new(
Plan::Free,
RequestUsage {
limit: free_limit,
amount: 45,
},
))
.into_any_element(), .into_any_element(),
), ),
single_example( single_example(
"Free - Request Limit Reached", "Free - Request Limit Reached",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::Free, 50)) .child(UsageBanner::new(
Plan::Free,
RequestUsage {
limit: free_limit,
amount: 50,
},
))
.into_any_element(), .into_any_element(),
), ),
]; ];
let zed_pro_limit = Plan::ZedPro.model_requests_limit();
let zed_pro_examples = vec![ let zed_pro_examples = vec![
single_example( single_example(
"Zed Pro - Normal Usage", "Zed Pro - Normal Usage",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::ZedPro, 250)) .child(UsageBanner::new(
Plan::ZedPro,
RequestUsage {
limit: zed_pro_limit,
amount: 250,
},
))
.into_any_element(), .into_any_element(),
), ),
single_example( single_example(
"Zed Pro - Approaching Limit", "Zed Pro - Approaching Limit",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::ZedPro, 450)) .child(UsageBanner::new(
Plan::ZedPro,
RequestUsage {
limit: zed_pro_limit,
amount: 450,
},
))
.into_any_element(), .into_any_element(),
), ),
single_example( single_example(
"Zed Pro - Request Limit Reached", "Zed Pro - Request Limit Reached",
div() div()
.size_full() .size_full()
.child(UsageBanner::new(Plan::ZedPro, 500)) .child(UsageBanner::new(
Plan::ZedPro,
RequestUsage {
limit: zed_pro_limit,
amount: 500,
},
))
.into_any_element(), .into_any_element(),
), ),
]; ];