1use std::{error::Error, fmt, path::Path, rc::Rc, sync::Arc};
2
3use agent_client_protocol::{self as acp};
4use anyhow::Result;
5use gpui::{AsyncApp, Entity, Task};
6use language_model::LanguageModel;
7use project::Project;
8use ui::App;
9
10use crate::AcpThread;
11
12/// Trait for agents that support listing, selecting, and querying language models.
13///
14/// This is an optional capability; agents indicate support via [AgentConnection::model_selector].
15pub trait ModelSelector: 'static {
16 /// Lists all available language models for this agent.
17 ///
18 /// # Parameters
19 /// - `cx`: The GPUI app context for async operations and global access.
20 ///
21 /// # Returns
22 /// A task resolving to the list of models or an error (e.g., if no models are configured).
23 fn list_models(&self, cx: &mut AsyncApp) -> Task<Result<Vec<Arc<dyn LanguageModel>>>>;
24
25 /// Selects a model for a specific session (thread).
26 ///
27 /// This sets the default model for future interactions in the session.
28 /// If the session doesn't exist or the model is invalid, it returns an error.
29 ///
30 /// # Parameters
31 /// - `session_id`: The ID of the session (thread) to apply the model to.
32 /// - `model`: The model to select (should be one from [list_models]).
33 /// - `cx`: The GPUI app context.
34 ///
35 /// # Returns
36 /// A task resolving to `Ok(())` on success or an error.
37 fn select_model(
38 &self,
39 session_id: acp::SessionId,
40 model: Arc<dyn LanguageModel>,
41 cx: &mut AsyncApp,
42 ) -> Task<Result<()>>;
43
44 /// Retrieves the currently selected model for a specific session (thread).
45 ///
46 /// # Parameters
47 /// - `session_id`: The ID of the session (thread) to query.
48 /// - `cx`: The GPUI app context.
49 ///
50 /// # Returns
51 /// A task resolving to the selected model (always set) or an error (e.g., session not found).
52 fn selected_model(
53 &self,
54 session_id: &acp::SessionId,
55 cx: &mut AsyncApp,
56 ) -> Task<Result<Arc<dyn LanguageModel>>>;
57}
58
59pub trait AgentConnection {
60 fn new_thread(
61 self: Rc<Self>,
62 project: Entity<Project>,
63 cwd: &Path,
64 cx: &mut AsyncApp,
65 ) -> Task<Result<Entity<AcpThread>>>;
66
67 fn auth_methods(&self) -> &[acp::AuthMethod];
68
69 fn authenticate(&self, method: acp::AuthMethodId, cx: &mut App) -> Task<Result<()>>;
70
71 fn prompt(&self, params: acp::PromptRequest, cx: &mut App)
72 -> Task<Result<acp::PromptResponse>>;
73
74 fn cancel(&self, session_id: &acp::SessionId, cx: &mut App);
75
76 /// Returns this agent as an [Rc<dyn ModelSelector>] if the model selection capability is supported.
77 ///
78 /// If the agent does not support model selection, returns [None].
79 /// This allows sharing the selector in UI components.
80 fn model_selector(&self) -> Option<Rc<dyn ModelSelector>> {
81 None // Default impl for agents that don't support it
82 }
83}
84
85#[derive(Debug)]
86pub struct AuthRequired;
87
88impl Error for AuthRequired {}
89impl fmt::Display for AuthRequired {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "AuthRequired")
92 }
93}