cloud_llm_client: Add new Plan variants (#37810)

This PR adds new variants to the `Plan` enum.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers
2025-09-09 00:18:43 +00:00
committed by GitHub
parent 8c93112869
commit ea4073e50e
14 changed files with 68 additions and 44 deletions
Generated
+1
View File
@@ -482,6 +482,7 @@ dependencies = [
"client",
"cloud_llm_client",
"component",
"feature_flags",
"gpui",
"language_model",
"serde",
+1
View File
@@ -5005,6 +5005,7 @@ impl AcpThreadView {
cloud_llm_client::Plan::ZedProTrial | cloud_llm_client::Plan::ZedFree => {
"Upgrade to Zed Pro for more prompts."
}
cloud_llm_client::Plan::ZedProV2 | cloud_llm_client::Plan::ZedProTrialV2 => "",
};
Callout::new()
+4 -2
View File
@@ -516,8 +516,10 @@ impl AgentConfiguration {
let (plan_name, label_color, bg_color) = match plan {
Plan::ZedFree => ("Free", Color::Default, free_chip_bg),
Plan::ZedProTrial => ("Pro Trial", Color::Accent, pro_chip_bg),
Plan::ZedPro => ("Pro", Color::Accent, pro_chip_bg),
Plan::ZedProTrial | Plan::ZedProTrialV2 => {
("Pro Trial", Color::Accent, pro_chip_bg)
}
Plan::ZedPro | Plan::ZedProV2 => ("Pro", Color::Accent, pro_chip_bg),
};
Chip::new(plan_name.to_string())
+1
View File
@@ -3518,6 +3518,7 @@ impl AgentPanel {
let error_message = match plan {
Plan::ZedPro => "Upgrade to usage-based billing for more prompts.",
Plan::ZedProTrial | Plan::ZedFree => "Upgrade to Zed Pro for more prompts.",
Plan::ZedProV2 | Plan::ZedProTrialV2 => "",
};
Callout::new()
+3 -4
View File
@@ -2,6 +2,7 @@ use std::sync::Arc;
use ai_onboarding::{AgentPanelOnboardingCard, PlanDefinitions};
use client::zed_urls;
use feature_flags::{BillingV2FeatureFlag, FeatureFlagAppExt as _};
use gpui::{AnyElement, App, IntoElement, RenderOnce, Window};
use ui::{Divider, Tooltip, prelude::*};
@@ -18,8 +19,6 @@ impl EndTrialUpsell {
impl RenderOnce for EndTrialUpsell {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let plan_definitions = PlanDefinitions;
let pro_section = v_flex()
.gap_1()
.child(
@@ -33,7 +32,7 @@ impl RenderOnce for EndTrialUpsell {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.pro_plan(false))
.child(PlanDefinitions.pro_plan(cx.has_flag::<BillingV2FeatureFlag>(), false))
.child(
Button::new("cta-button", "Upgrade to Zed Pro")
.full_width()
@@ -64,7 +63,7 @@ impl RenderOnce for EndTrialUpsell {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.free_plan());
.child(PlanDefinitions.free_plan(cx.has_flag::<BillingV2FeatureFlag>()));
AgentPanelOnboardingCard::new()
.child(Headline::new("Your Zed Pro Trial has expired"))
@@ -45,13 +45,13 @@ impl RenderOnce for UsageCallout {
"Upgrade",
zed_urls::account_url(cx),
),
Plan::ZedProTrial => (
Plan::ZedProTrial | Plan::ZedProTrialV2 => (
"Out of trial prompts",
"Upgrade to Zed Pro to continue, or switch to API key.".to_string(),
"Upgrade",
zed_urls::account_url(cx),
),
Plan::ZedPro => (
Plan::ZedPro | Plan::ZedProV2 => (
"Out of included prompts",
"Enable usage-based billing to continue.".to_string(),
"Manage",
+1
View File
@@ -18,6 +18,7 @@ default = []
client.workspace = true
cloud_llm_client.workspace = true
component.workspace = true
feature_flags.workspace = true
gpui.workspace = true
language_model.workspace = true
serde.workspace = true
+19 -20
View File
@@ -18,6 +18,7 @@ pub use young_account_banner::YoungAccountBanner;
use std::sync::Arc;
use client::{Client, UserStore, zed_urls};
use feature_flags::{BillingV2FeatureFlag, FeatureFlagAppExt as _};
use gpui::{AnyElement, Entity, IntoElement, ParentElement};
use ui::{Divider, RegisterComponent, Tooltip, prelude::*};
@@ -84,9 +85,8 @@ impl ZedAiOnboarding {
self
}
fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
fn render_sign_in_disclaimer(&self, cx: &mut App) -> AnyElement {
let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
let plan_definitions = PlanDefinitions;
v_flex()
.gap_1()
@@ -96,7 +96,7 @@ impl ZedAiOnboarding {
.color(Color::Muted)
.mb_2(),
)
.child(plan_definitions.pro_plan(false))
.child(PlanDefinitions.pro_plan(cx.has_flag::<BillingV2FeatureFlag>(), false))
.child(
Button::new("sign_in", "Try Zed Pro for Free")
.disabled(signing_in)
@@ -114,16 +114,13 @@ impl ZedAiOnboarding {
}
fn render_free_plan_state(&self, cx: &mut App) -> AnyElement {
let young_account_banner = YoungAccountBanner;
let plan_definitions = PlanDefinitions;
if self.account_too_young {
v_flex()
.relative()
.max_w_full()
.gap_1()
.child(Headline::new("Welcome to Zed AI"))
.child(young_account_banner)
.child(YoungAccountBanner)
.child(
v_flex()
.mt_2()
@@ -139,7 +136,9 @@ impl ZedAiOnboarding {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.pro_plan(true))
.child(
PlanDefinitions.pro_plan(cx.has_flag::<BillingV2FeatureFlag>(), true),
)
.child(
Button::new("pro", "Get Started")
.full_width()
@@ -182,7 +181,7 @@ impl ZedAiOnboarding {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.free_plan()),
.child(PlanDefinitions.free_plan(cx.has_flag::<BillingV2FeatureFlag>())),
)
.when_some(
self.dismiss_onboarding.as_ref(),
@@ -220,7 +219,9 @@ impl ZedAiOnboarding {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.pro_trial(true))
.child(
PlanDefinitions.pro_trial(cx.has_flag::<BillingV2FeatureFlag>(), true),
)
.child(
Button::new("pro", "Start Free Trial")
.full_width()
@@ -238,9 +239,7 @@ impl ZedAiOnboarding {
}
}
fn render_trial_state(&self, _cx: &mut App) -> AnyElement {
let plan_definitions = PlanDefinitions;
fn render_trial_state(&self, is_v2: bool, _cx: &mut App) -> AnyElement {
v_flex()
.relative()
.gap_1()
@@ -250,7 +249,7 @@ impl ZedAiOnboarding {
.color(Color::Muted)
.mb_2(),
)
.child(plan_definitions.pro_trial(false))
.child(PlanDefinitions.pro_trial(is_v2, false))
.when_some(
self.dismiss_onboarding.as_ref(),
|this, dismiss_callback| {
@@ -274,9 +273,7 @@ impl ZedAiOnboarding {
.into_any_element()
}
fn render_pro_plan_state(&self, _cx: &mut App) -> AnyElement {
let plan_definitions = PlanDefinitions;
fn render_pro_plan_state(&self, is_v2: bool, _cx: &mut App) -> AnyElement {
v_flex()
.gap_1()
.child(Headline::new("Welcome to Zed Pro"))
@@ -285,7 +282,7 @@ impl ZedAiOnboarding {
.color(Color::Muted)
.mb_2(),
)
.child(plan_definitions.pro_plan(false))
.child(PlanDefinitions.pro_plan(is_v2, false))
.when_some(
self.dismiss_onboarding.as_ref(),
|this, dismiss_callback| {
@@ -315,8 +312,10 @@ impl RenderOnce for ZedAiOnboarding {
if matches!(self.sign_in_status, SignInStatus::SignedIn) {
match self.plan {
None | Some(Plan::ZedFree) => self.render_free_plan_state(cx),
Some(Plan::ZedProTrial) => self.render_trial_state(cx),
Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
Some(Plan::ZedProTrial) => self.render_trial_state(false, cx),
Some(Plan::ZedProTrialV2) => self.render_trial_state(true, cx),
Some(Plan::ZedPro) => self.render_pro_plan_state(false, cx),
Some(Plan::ZedProV2) => self.render_pro_plan_state(true, cx),
}
} else {
self.render_sign_in_disclaimer(cx)
+12 -11
View File
@@ -2,6 +2,7 @@ use std::sync::Arc;
use client::{Client, UserStore, zed_urls};
use cloud_llm_client::Plan;
use feature_flags::{BillingV2FeatureFlag, FeatureFlagAppExt};
use gpui::{AnyElement, App, Entity, IntoElement, RenderOnce, Window};
use ui::{CommonAnimationExt, Divider, Vector, VectorName, prelude::*};
@@ -49,9 +50,6 @@ impl AiUpsellCard {
impl RenderOnce for AiUpsellCard {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let plan_definitions = PlanDefinitions;
let young_account_banner = YoungAccountBanner;
let pro_section = v_flex()
.flex_grow()
.w_full()
@@ -67,7 +65,7 @@ impl RenderOnce for AiUpsellCard {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.pro_plan(false));
.child(PlanDefinitions.pro_plan(cx.has_flag::<BillingV2FeatureFlag>(), false));
let free_section = v_flex()
.flex_grow()
@@ -84,7 +82,7 @@ impl RenderOnce for AiUpsellCard {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.free_plan());
.child(PlanDefinitions.free_plan(cx.has_flag::<BillingV2FeatureFlag>()));
let grid_bg = h_flex()
.absolute()
@@ -173,7 +171,7 @@ impl RenderOnce for AiUpsellCard {
.child(Label::new("Try Zed AI").size(LabelSize::Large))
.map(|this| {
if self.account_too_young {
this.child(young_account_banner).child(
this.child(YoungAccountBanner).child(
v_flex()
.mt_2()
.gap_1()
@@ -188,7 +186,10 @@ impl RenderOnce for AiUpsellCard {
)
.child(Divider::horizontal()),
)
.child(plan_definitions.pro_plan(true))
.child(
PlanDefinitions
.pro_plan(cx.has_flag::<BillingV2FeatureFlag>(), true),
)
.child(
Button::new("pro", "Get Started")
.full_width()
@@ -235,7 +236,7 @@ impl RenderOnce for AiUpsellCard {
)
}
}),
Some(Plan::ZedProTrial) => card
Some(plan @ Plan::ZedProTrial | plan @ Plan::ZedProTrialV2) => card
.child(pro_trial_stamp)
.child(Label::new("You're in the Zed Pro Trial").size(LabelSize::Large))
.child(
@@ -243,8 +244,8 @@ impl RenderOnce for AiUpsellCard {
.color(Color::Muted)
.mb_2(),
)
.child(plan_definitions.pro_trial(false)),
Some(Plan::ZedPro) => card
.child(PlanDefinitions.pro_trial(plan == Plan::ZedProTrialV2, false)),
Some(plan @ Plan::ZedPro | plan @ Plan::ZedProV2) => card
.child(certified_user_stamp)
.child(Label::new("You're in the Zed Pro plan").size(LabelSize::Large))
.child(
@@ -252,7 +253,7 @@ impl RenderOnce for AiUpsellCard {
.color(Color::Muted)
.mb_2(),
)
.child(plan_definitions.pro_plan(false)),
.child(PlanDefinitions.pro_plan(plan == Plan::ZedProV2, false)),
},
// Signed Out State
_ => card
+3 -3
View File
@@ -7,13 +7,13 @@ pub struct PlanDefinitions;
impl PlanDefinitions {
pub const AI_DESCRIPTION: &'static str = "Zed offers a complete agentic experience, with robust editing and reviewing features to collaborate with AI.";
pub fn free_plan(&self) -> impl IntoElement {
pub fn free_plan(&self, _is_v2: bool) -> impl IntoElement {
List::new()
.child(ListBulletItem::new("50 prompts with Claude models"))
.child(ListBulletItem::new("2,000 accepted edit predictions"))
}
pub fn pro_trial(&self, period: bool) -> impl IntoElement {
pub fn pro_trial(&self, _is_v2: bool, period: bool) -> impl IntoElement {
List::new()
.child(ListBulletItem::new("150 prompts with Claude models"))
.child(ListBulletItem::new(
@@ -26,7 +26,7 @@ impl PlanDefinitions {
})
}
pub fn pro_plan(&self, price: bool) -> impl IntoElement {
pub fn pro_plan(&self, _is_v2: bool, price: bool) -> impl IntoElement {
List::new()
.child(ListBulletItem::new("500 prompts with Claude models"))
.child(ListBulletItem::new(
@@ -82,8 +82,10 @@ pub enum Plan {
ZedFree,
#[serde(alias = "ZedPro")]
ZedPro,
ZedProV2,
#[serde(alias = "ZedProTrial")]
ZedProTrial,
ZedProTrialV2,
}
impl FromStr for Plan {
@@ -327,6 +329,12 @@ mod tests {
let plan = serde_json::from_value::<Plan>(json!("zed_pro_trial")).unwrap();
assert_eq!(plan, Plan::ZedProTrial);
let plan = serde_json::from_value::<Plan>(json!("zed_pro_v2")).unwrap();
assert_eq!(plan, Plan::ZedProV2);
let plan = serde_json::from_value::<Plan>(json!("zed_pro_trial_v2")).unwrap();
assert_eq!(plan, Plan::ZedProTrialV2);
}
#[test]
@@ -66,6 +66,12 @@ impl FeatureFlag for LlmClosedBetaFeatureFlag {
const NAME: &'static str = "llm-closed-beta";
}
pub struct BillingV2FeatureFlag {}
impl FeatureFlag for BillingV2FeatureFlag {
const NAME: &'static str = "billing-v2";
}
pub struct NotebookFeatureFlag;
impl FeatureFlag for NotebookFeatureFlag {
@@ -36,6 +36,7 @@ impl fmt::Display for ModelRequestLimitReachedError {
Plan::ZedProTrial => {
"Model request limit reached. Upgrade to Zed Pro for more requests."
}
Plan::ZedProV2 | Plan::ZedProTrialV2 => "Model request limit reached.",
};
write!(f, "{message}")
+6 -2
View File
@@ -660,8 +660,12 @@ impl TitleBar {
let (plan_name, label_color, bg_color) = match plan {
None | Some(Plan::ZedFree) => ("Free", Color::Default, free_chip_bg),
Some(Plan::ZedProTrial) => ("Pro Trial", Color::Accent, pro_chip_bg),
Some(Plan::ZedPro) => ("Pro", Color::Accent, pro_chip_bg),
Some(Plan::ZedProTrial | Plan::ZedProTrialV2) => {
("Pro Trial", Color::Accent, pro_chip_bg)
}
Some(Plan::ZedPro | Plan::ZedProV2) => {
("Pro", Color::Accent, pro_chip_bg)
}
};
menu.custom_entry(