assistant: Require user to accept TOS for cloud provider (#16111)
This adds the requirement for users to accept the terms of service the first time they send a message with the Cloud provider. Once this is out and in a nightly, we need to add the check to the server side too, to authenticate access to the models. Demo: https://github.com/user-attachments/assets/0edebf74-8120-4fa2-b801-bb76f04e8a17 Release Notes: - N/A
This commit is contained in:
parent
98f314ba21
commit
fbb533b3e0
14 changed files with 297 additions and 9 deletions
|
@ -9,7 +9,9 @@ pub mod settings;
|
|||
use anyhow::Result;
|
||||
use client::{Client, UserStore};
|
||||
use futures::{future::BoxFuture, stream::BoxStream};
|
||||
use gpui::{AnyView, AppContext, AsyncAppContext, Model, SharedString, Task, WindowContext};
|
||||
use gpui::{
|
||||
AnyElement, AnyView, AppContext, AsyncAppContext, Model, SharedString, Task, WindowContext,
|
||||
};
|
||||
pub use model::*;
|
||||
use project::Fs;
|
||||
use proto::Plan;
|
||||
|
@ -114,6 +116,12 @@ pub trait LanguageModelProvider: 'static {
|
|||
fn is_authenticated(&self, cx: &AppContext) -> bool;
|
||||
fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>>;
|
||||
fn configuration_view(&self, cx: &mut WindowContext) -> AnyView;
|
||||
fn must_accept_terms(&self, _cx: &AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
fn render_accept_terms(&self, _cx: &mut WindowContext) -> Option<AnyElement> {
|
||||
None
|
||||
}
|
||||
fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>>;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,10 @@ use client::{Client, PerformCompletionParams, UserStore, EXPIRED_LLM_TOKEN_HEADE
|
|||
use collections::BTreeMap;
|
||||
use feature_flags::{FeatureFlagAppExt, LanguageModels};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, AsyncBufReadExt, FutureExt, StreamExt};
|
||||
use gpui::{AnyView, AppContext, AsyncAppContext, Model, ModelContext, Subscription, Task};
|
||||
use gpui::{
|
||||
AnyElement, AnyView, AppContext, AsyncAppContext, FontWeight, Model, ModelContext,
|
||||
Subscription, Task,
|
||||
};
|
||||
use http_client::{AsyncBody, HttpClient, Method, Response};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -62,6 +65,7 @@ pub struct State {
|
|||
client: Arc<Client>,
|
||||
user_store: Model<UserStore>,
|
||||
status: client::Status,
|
||||
accept_terms: Option<Task<Result<()>>>,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
|
@ -77,6 +81,26 @@ impl State {
|
|||
this.update(&mut cx, |_, cx| cx.notify())
|
||||
})
|
||||
}
|
||||
|
||||
fn has_accepted_terms_of_service(&self, cx: &AppContext) -> bool {
|
||||
self.user_store
|
||||
.read(cx)
|
||||
.current_user_has_accepted_terms()
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn accept_terms_of_service(&mut self, cx: &mut ModelContext<Self>) {
|
||||
let user_store = self.user_store.clone();
|
||||
self.accept_terms = Some(cx.spawn(move |this, mut cx| async move {
|
||||
let _ = user_store
|
||||
.update(&mut cx, |store, cx| store.accept_terms_of_service(cx))?
|
||||
.await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.accept_terms = None;
|
||||
cx.notify()
|
||||
})
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
impl CloudLanguageModelProvider {
|
||||
|
@ -88,6 +112,7 @@ impl CloudLanguageModelProvider {
|
|||
client: client.clone(),
|
||||
user_store,
|
||||
status,
|
||||
accept_terms: None,
|
||||
_subscription: cx.observe_global::<SettingsStore>(|_, cx| {
|
||||
cx.notify();
|
||||
}),
|
||||
|
@ -223,6 +248,57 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn must_accept_terms(&self, cx: &AppContext) -> bool {
|
||||
!self.state.read(cx).has_accepted_terms_of_service(cx)
|
||||
}
|
||||
|
||||
fn render_accept_terms(&self, cx: &mut WindowContext) -> Option<AnyElement> {
|
||||
let state = self.state.read(cx);
|
||||
|
||||
let terms = [(
|
||||
"anthropic_terms_of_service",
|
||||
"Anthropic Terms of Service",
|
||||
"https://www.anthropic.com/legal/consumer-terms",
|
||||
)]
|
||||
.map(|(id, label, url)| {
|
||||
Button::new(id, label)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon(IconName::ExternalLink)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.on_click(move |_, cx| cx.open_url(url))
|
||||
});
|
||||
|
||||
if state.has_accepted_terms_of_service(cx) {
|
||||
None
|
||||
} else {
|
||||
let disabled = state.accept_terms.is_some();
|
||||
Some(
|
||||
v_flex()
|
||||
.child(Label::new("Terms & Conditions").weight(FontWeight::SEMIBOLD))
|
||||
.child("Please read and accept the terms and conditions of Zed AI and our provider partners to continue.")
|
||||
.child(v_flex().m_2().gap_1().children(terms))
|
||||
.child(
|
||||
h_flex().justify_end().mt_1().child(
|
||||
Button::new("accept_terms", "Accept")
|
||||
.disabled(disabled)
|
||||
.on_click({
|
||||
let state = self.state.downgrade();
|
||||
move |_, cx| {
|
||||
state
|
||||
.update(cx, |state, cx| {
|
||||
state.accept_terms_of_service(cx)
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.into_any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_credentials(&self, _cx: &mut AppContext) -> Task<Result<()>> {
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
|
@ -766,6 +842,7 @@ impl Render for ConfigurationView {
|
|||
|
||||
let is_connected = !self.state.read(cx).is_signed_out();
|
||||
let plan = self.state.read(cx).user_store.read(cx).current_plan();
|
||||
let must_accept_terms = !self.state.read(cx).has_accepted_terms_of_service(cx);
|
||||
|
||||
let is_pro = plan == Some(proto::Plan::ZedPro);
|
||||
|
||||
|
@ -773,6 +850,11 @@ impl Render for ConfigurationView {
|
|||
v_flex()
|
||||
.gap_3()
|
||||
.max_w_4_5()
|
||||
.when(must_accept_terms, |this| {
|
||||
this.child(Label::new(
|
||||
"You must accept the terms of service to use this provider.",
|
||||
))
|
||||
})
|
||||
.child(Label::new(
|
||||
if is_pro {
|
||||
"You have full access to Zed's hosted models from Anthropic, OpenAI, Google with faster speeds and higher limits through Zed Pro."
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue