language_models: Improve subscription states in the Agent configuration view (#30252)
This PR improves the subscription states in the Agent configuration view to the new billing system. Zed Free (legacy): <img width="638" alt="Screenshot 2025-05-08 at 8 42 59 AM" src="https://github.com/user-attachments/assets/7b62d4c1-2a9c-4c6a-aa8f-060730b6d7b3" /> Zed Free (new): <img width="640" alt="Screenshot 2025-05-08 at 8 43 56 AM" src="https://github.com/user-attachments/assets/8a48448e-813e-4633-955d-623d3e6d603c" /> Zed Pro trial: <img width="641" alt="Screenshot 2025-05-08 at 8 45 52 AM" src="https://github.com/user-attachments/assets/1ec7ee62-e954-48e7-8447-4584527307c9" /> Zed Pro: <img width="636" alt="Screenshot 2025-05-08 at 8 47 21 AM" src="https://github.com/user-attachments/assets/f934b2e3-0943-4b78-b8dc-0a31e731d8fb" /> Release Notes: - agent: Improved the subscription-related information in the configuration view.
This commit is contained in:
parent
ce6e82cd7e
commit
f14322741e
@ -11,7 +11,7 @@ use postage::{sink::Sink, watch};
|
||||
use rpc::proto::{RequestMessage, UsersResponse};
|
||||
use std::sync::{Arc, Weak};
|
||||
use text::ReplicaId;
|
||||
use util::TryFutureExt as _;
|
||||
use util::{TryFutureExt as _, maybe};
|
||||
|
||||
pub type UserId = u64;
|
||||
|
||||
@ -101,6 +101,7 @@ pub struct UserStore {
|
||||
participant_indices: HashMap<u64, ParticipantIndex>,
|
||||
update_contacts_tx: mpsc::UnboundedSender<UpdateContacts>,
|
||||
current_plan: Option<proto::Plan>,
|
||||
subscription_period: Option<(DateTime<Utc>, DateTime<Utc>)>,
|
||||
trial_started_at: Option<DateTime<Utc>>,
|
||||
model_request_usage_amount: Option<u32>,
|
||||
model_request_usage_limit: Option<proto::UsageLimit>,
|
||||
@ -166,6 +167,7 @@ impl UserStore {
|
||||
by_github_login: Default::default(),
|
||||
current_user: current_user_rx,
|
||||
current_plan: None,
|
||||
subscription_period: None,
|
||||
trial_started_at: None,
|
||||
model_request_usage_amount: None,
|
||||
model_request_usage_limit: None,
|
||||
@ -333,6 +335,13 @@ impl UserStore {
|
||||
) -> Result<()> {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.current_plan = Some(message.payload.plan());
|
||||
this.subscription_period = maybe!({
|
||||
let period = message.payload.subscription_period?;
|
||||
let started_at = DateTime::from_timestamp(period.started_at as i64, 0)?;
|
||||
let ended_at = DateTime::from_timestamp(period.ended_at as i64, 0)?;
|
||||
|
||||
Some((started_at, ended_at))
|
||||
});
|
||||
this.trial_started_at = message
|
||||
.payload
|
||||
.trial_started_at
|
||||
@ -713,6 +722,10 @@ impl UserStore {
|
||||
self.current_plan
|
||||
}
|
||||
|
||||
pub fn subscription_period(&self) -> Option<(DateTime<Utc>, DateTime<Utc>)> {
|
||||
self.subscription_period
|
||||
}
|
||||
|
||||
pub fn trial_started_at(&self) -> Option<DateTime<Utc>> {
|
||||
self.trial_started_at
|
||||
}
|
||||
|
@ -2702,7 +2702,7 @@ async fn update_user_plan(user_id: UserId, session: &Session) -> Result<()> {
|
||||
let billing_customer = db.get_billing_customer_by_user_id(user_id).await?;
|
||||
let billing_preferences = db.get_billing_preferences(user_id).await?;
|
||||
|
||||
let usage = if let Some(llm_db) = session.app_state.llm_db.clone() {
|
||||
let (subscription_period, usage) = if let Some(llm_db) = session.app_state.llm_db.clone() {
|
||||
let subscription = db.get_active_billing_subscription(user_id).await?;
|
||||
|
||||
let subscription_period = maybe!({
|
||||
@ -2713,15 +2713,17 @@ async fn update_user_plan(user_id: UserId, session: &Session) -> Result<()> {
|
||||
Some((period_start_at, period_end_at))
|
||||
});
|
||||
|
||||
if let Some((period_start_at, period_end_at)) = subscription_period {
|
||||
let usage = if let Some((period_start_at, period_end_at)) = subscription_period {
|
||||
llm_db
|
||||
.get_subscription_usage_for_period(user_id, period_start_at, period_end_at)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
(subscription_period, usage)
|
||||
} else {
|
||||
None
|
||||
(None, None)
|
||||
};
|
||||
|
||||
session
|
||||
@ -2739,6 +2741,12 @@ async fn update_user_plan(user_id: UserId, session: &Session) -> Result<()> {
|
||||
billing_preferences
|
||||
.map(|preferences| preferences.model_request_overages_enabled)
|
||||
},
|
||||
subscription_period: subscription_period.map(|(started_at, ended_at)| {
|
||||
proto::SubscriptionPeriod {
|
||||
started_at: started_at.timestamp() as u64,
|
||||
ended_at: ended_at.timestamp() as u64,
|
||||
}
|
||||
}),
|
||||
usage: usage.map(|usage| {
|
||||
let plan = match plan {
|
||||
proto::Plan::Free => zed_llm_client::Plan::Free,
|
||||
|
@ -2,7 +2,7 @@ use anthropic::{AnthropicModelMode, parse_prompt_too_long};
|
||||
use anyhow::{Result, anyhow};
|
||||
use client::{Client, UserStore, zed_urls};
|
||||
use collections::BTreeMap;
|
||||
use feature_flags::{FeatureFlagAppExt, LlmClosedBetaFeatureFlag, ZedProFeatureFlag};
|
||||
use feature_flags::{FeatureFlagAppExt, LlmClosedBetaFeatureFlag};
|
||||
use futures::{
|
||||
AsyncBufReadExt, FutureExt, Stream, StreamExt, future::BoxFuture, stream::BoxStream,
|
||||
};
|
||||
@ -1035,48 +1035,56 @@ impl ConfigurationView {
|
||||
|
||||
impl Render for ConfigurationView {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
const ZED_AI_URL: &str = "https://zed.dev/ai";
|
||||
const ZED_PRICING_URL: &str = "https://zed.dev/pricing";
|
||||
|
||||
let is_connected = !self.state.read(cx).is_signed_out();
|
||||
let plan = self.state.read(cx).user_store.read(cx).current_plan();
|
||||
let user_store = self.state.read(cx).user_store.read(cx);
|
||||
let plan = user_store.current_plan();
|
||||
let subscription_period = user_store.subscription_period();
|
||||
let eligible_for_trial = user_store.trial_started_at().is_none();
|
||||
let has_accepted_terms = self.state.read(cx).has_accepted_terms_of_service(cx);
|
||||
|
||||
let is_pro = plan == Some(proto::Plan::ZedPro);
|
||||
let subscription_text = Label::new(if is_pro {
|
||||
"You have access to Zed's hosted LLMs through your Zed Pro subscription."
|
||||
let subscription_text = match (plan, subscription_period) {
|
||||
(Some(proto::Plan::ZedPro), Some(_)) => {
|
||||
"You have access to Zed's hosted LLMs through your Zed Pro subscription."
|
||||
}
|
||||
(Some(proto::Plan::ZedProTrial), Some(_)) => {
|
||||
"You have access to Zed's hosted LLMs through your Zed Pro trial."
|
||||
}
|
||||
(Some(proto::Plan::Free), Some(_)) => {
|
||||
"You have basic access to Zed's hosted LLMs through your Zed Free subscription."
|
||||
}
|
||||
_ => {
|
||||
if eligible_for_trial {
|
||||
"Subscribe for access to Zed's hosted LLMs. Start with a 14 day free trial."
|
||||
} else {
|
||||
"Subscribe for access to Zed's hosted LLMs."
|
||||
}
|
||||
}
|
||||
};
|
||||
let manage_subscription_buttons = if is_pro {
|
||||
h_flex().child(
|
||||
Button::new("manage_settings", "Manage Subscription")
|
||||
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.on_click(cx.listener(|_, _, _, cx| cx.open_url(&zed_urls::account_url(cx)))),
|
||||
)
|
||||
} else {
|
||||
"You have basic access to models from Anthropic through the Zed AI Free plan."
|
||||
});
|
||||
let manage_subscription_button = if is_pro {
|
||||
Some(
|
||||
h_flex().child(
|
||||
Button::new("manage_settings", "Manage Subscription")
|
||||
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("learn_more", "Learn more")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.on_click(cx.listener(|_, _, _, cx| cx.open_url(ZED_PRICING_URL))),
|
||||
)
|
||||
.child(
|
||||
Button::new("upgrade", "Upgrade")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.color(Color::Accent)
|
||||
.on_click(
|
||||
cx.listener(|_, _, _, cx| cx.open_url(&zed_urls::account_url(cx))),
|
||||
),
|
||||
),
|
||||
)
|
||||
} else if cx.has_flag::<ZedProFeatureFlag>() {
|
||||
Some(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("learn_more", "Learn more")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.on_click(cx.listener(|_, _, _, cx| cx.open_url(ZED_AI_URL))),
|
||||
)
|
||||
.child(
|
||||
Button::new("upgrade", "Upgrade")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.color(Color::Accent)
|
||||
.on_click(
|
||||
cx.listener(|_, _, _, cx| cx.open_url(&zed_urls::account_url(cx))),
|
||||
),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
)
|
||||
};
|
||||
|
||||
if is_connected {
|
||||
@ -1090,7 +1098,7 @@ impl Render for ConfigurationView {
|
||||
))
|
||||
.when(has_accepted_terms, |this| {
|
||||
this.child(subscription_text)
|
||||
.children(manage_subscription_button)
|
||||
.child(manage_subscription_buttons)
|
||||
})
|
||||
} else {
|
||||
v_flex()
|
||||
|
@ -26,6 +26,12 @@ message UpdateUserPlan {
|
||||
optional uint64 trial_started_at = 2;
|
||||
optional bool is_usage_based_billing_enabled = 3;
|
||||
optional SubscriptionUsage usage = 4;
|
||||
optional SubscriptionPeriod subscription_period = 5;
|
||||
}
|
||||
|
||||
message SubscriptionPeriod {
|
||||
uint64 started_at = 1;
|
||||
uint64 ended_at = 2;
|
||||
}
|
||||
|
||||
message SubscriptionUsage {
|
||||
|
Loading…
x
Reference in New Issue
Block a user