mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-16 21:03:46 +00:00
fix: fix checking if provider is authenticated
This commit is contained in:
parent
b76d83577a
commit
80ce95f886
13 changed files with 801 additions and 6 deletions
|
|
@ -63,7 +63,9 @@ pub fn extract_claude_credentials(
|
|||
];
|
||||
|
||||
for path in config_paths {
|
||||
let data = read_json_file(&path)?;
|
||||
let Some(data) = read_json_file(&path) else {
|
||||
continue;
|
||||
};
|
||||
for key_path in &key_paths {
|
||||
if let Some(key) = read_string_field(&data, key_path) {
|
||||
if key.starts_with("sk-ant-") {
|
||||
|
|
|
|||
|
|
@ -21,10 +21,14 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::{json, Value};
|
||||
use tokio::sync::{broadcast, Mutex};
|
||||
use tokio::time::interval;
|
||||
use tracing::warn;
|
||||
use utoipa::{IntoParams, OpenApi, ToSchema};
|
||||
|
||||
use crate::router::{AgentModelInfo, AppState, CreateSessionRequest, PermissionReply};
|
||||
use sandbox_agent_agent_management::agents::AgentId;
|
||||
use sandbox_agent_agent_management::credentials::{
|
||||
extract_all_credentials, CredentialExtractionOptions, ExtractedCredentials,
|
||||
};
|
||||
use sandbox_agent_error::SandboxError;
|
||||
use sandbox_agent_universal_agent_schema::{
|
||||
ContentPart, FileAction, ItemDeltaData, ItemEventData, ItemKind, ItemRole, ItemStatus,
|
||||
|
|
@ -233,6 +237,8 @@ struct OpenCodeModelCache {
|
|||
group_names: HashMap<String, String>,
|
||||
default_group: String,
|
||||
default_model: String,
|
||||
/// Group IDs that have valid credentials available
|
||||
connected: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct OpenCodeState {
|
||||
|
|
@ -637,6 +643,21 @@ async fn opencode_model_cache(state: &OpenCodeAppState) -> OpenCodeModelCache {
|
|||
}
|
||||
|
||||
async fn build_opencode_model_cache(state: &OpenCodeAppState) -> OpenCodeModelCache {
|
||||
// Check credentials upfront
|
||||
let credentials = match tokio::task::spawn_blocking(|| {
|
||||
extract_all_credentials(&CredentialExtractionOptions::new())
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(creds) => creds,
|
||||
Err(err) => {
|
||||
warn!("Failed to extract credentials for model cache: {err}");
|
||||
ExtractedCredentials::default()
|
||||
}
|
||||
};
|
||||
let has_anthropic = credentials.anthropic.is_some();
|
||||
let has_openai = credentials.openai.is_some();
|
||||
|
||||
let mut entries = Vec::new();
|
||||
let mut model_lookup = HashMap::new();
|
||||
let mut ambiguous_models = HashSet::new();
|
||||
|
|
@ -735,6 +756,28 @@ async fn build_opencode_model_cache(state: &OpenCodeAppState) -> OpenCodeModelCa
|
|||
}
|
||||
}
|
||||
|
||||
// Build connected list based on credential availability
|
||||
let mut connected = Vec::new();
|
||||
for group_id in group_names.keys() {
|
||||
let is_connected = match group_agents.get(group_id) {
|
||||
Some(AgentId::Claude) | Some(AgentId::Amp) => has_anthropic,
|
||||
Some(AgentId::Codex) => has_openai,
|
||||
Some(AgentId::Opencode) => {
|
||||
// Check the specific provider for opencode groups (e.g., "opencode:anthropic")
|
||||
match opencode_group_provider(group_id) {
|
||||
Some("anthropic") => has_anthropic,
|
||||
Some("openai") => has_openai,
|
||||
_ => has_anthropic || has_openai,
|
||||
}
|
||||
}
|
||||
Some(AgentId::Mock) => true,
|
||||
None => false,
|
||||
};
|
||||
if is_connected {
|
||||
connected.push(group_id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
OpenCodeModelCache {
|
||||
entries,
|
||||
model_lookup,
|
||||
|
|
@ -743,6 +786,7 @@ async fn build_opencode_model_cache(state: &OpenCodeAppState) -> OpenCodeModelCa
|
|||
group_names,
|
||||
default_group,
|
||||
default_model,
|
||||
connected,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3962,7 +4006,6 @@ async fn oc_provider_list(State(state): State<Arc<OpenCodeAppState>>) -> impl In
|
|||
}
|
||||
let mut providers = Vec::new();
|
||||
let mut defaults = serde_json::Map::new();
|
||||
let mut connected = Vec::new();
|
||||
for (group_id, entries) in grouped {
|
||||
let mut models = serde_json::Map::new();
|
||||
for entry in entries {
|
||||
|
|
@ -3982,12 +4025,12 @@ async fn oc_provider_list(State(state): State<Arc<OpenCodeAppState>>) -> impl In
|
|||
if let Some(default_model) = cache.group_defaults.get(&group_id) {
|
||||
defaults.insert(group_id.clone(), Value::String(default_model.clone()));
|
||||
}
|
||||
connected.push(group_id);
|
||||
}
|
||||
// Use the connected list from cache (based on credential availability)
|
||||
let providers = json!({
|
||||
"all": providers,
|
||||
"default": Value::Object(defaults),
|
||||
"connected": connected
|
||||
"connected": cache.connected
|
||||
});
|
||||
(StatusCode::OK, Json(providers))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1798,8 +1798,14 @@ impl SessionManager {
|
|||
agent: AgentId,
|
||||
) -> Result<AgentModelsResponse, SandboxError> {
|
||||
match agent {
|
||||
AgentId::Claude => self.fetch_claude_models().await,
|
||||
AgentId::Codex => self.fetch_codex_models().await,
|
||||
AgentId::Claude => match self.fetch_claude_models().await {
|
||||
Ok(response) if !response.models.is_empty() => Ok(response),
|
||||
_ => Ok(claude_fallback_models()),
|
||||
},
|
||||
AgentId::Codex => match self.fetch_codex_models().await {
|
||||
Ok(response) if !response.models.is_empty() => Ok(response),
|
||||
_ => Ok(codex_fallback_models()),
|
||||
},
|
||||
AgentId::Opencode => match self.fetch_opencode_models().await {
|
||||
Ok(models) => Ok(models),
|
||||
Err(_) => Ok(AgentModelsResponse {
|
||||
|
|
@ -3927,6 +3933,8 @@ pub struct ServerStatusInfo {
|
|||
pub struct AgentInfo {
|
||||
pub id: String,
|
||||
pub installed: bool,
|
||||
/// Whether the agent's required provider credentials are available
|
||||
pub credentials_available: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -4194,6 +4202,10 @@ async fn list_agents(
|
|||
|
||||
let agents =
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let credentials = extract_all_credentials(&CredentialExtractionOptions::new());
|
||||
let has_anthropic = credentials.anthropic.is_some();
|
||||
let has_openai = credentials.openai.is_some();
|
||||
|
||||
all_agents()
|
||||
.into_iter()
|
||||
.map(|agent_id| {
|
||||
|
|
@ -4202,6 +4214,13 @@ async fn list_agents(
|
|||
let path = manager.resolve_binary(agent_id).ok();
|
||||
let capabilities = agent_capabilities_for(agent_id);
|
||||
|
||||
let credentials_available = match agent_id {
|
||||
AgentId::Claude | AgentId::Amp => has_anthropic,
|
||||
AgentId::Codex => has_openai,
|
||||
AgentId::Opencode => has_anthropic || has_openai,
|
||||
AgentId::Mock => true,
|
||||
};
|
||||
|
||||
// Add server_status for agents with shared processes
|
||||
let server_status =
|
||||
if capabilities.shared_process {
|
||||
|
|
@ -4221,6 +4240,7 @@ async fn list_agents(
|
|||
AgentInfo {
|
||||
id: agent_id.as_str().to_string(),
|
||||
installed,
|
||||
credentials_available,
|
||||
version,
|
||||
path: path.map(|path| path.to_string_lossy().to_string()),
|
||||
capabilities,
|
||||
|
|
@ -4742,6 +4762,38 @@ fn mock_models_response() -> AgentModelsResponse {
|
|||
}
|
||||
}
|
||||
|
||||
fn claude_fallback_models() -> AgentModelsResponse {
|
||||
let models = ["claude-sonnet-4-20250514", "claude-opus-4-20250514"]
|
||||
.into_iter()
|
||||
.map(|id| AgentModelInfo {
|
||||
id: id.to_string(),
|
||||
name: None,
|
||||
variants: None,
|
||||
default_variant: None,
|
||||
})
|
||||
.collect();
|
||||
AgentModelsResponse {
|
||||
models,
|
||||
default_model: Some("claude-sonnet-4-20250514".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn codex_fallback_models() -> AgentModelsResponse {
|
||||
let models = ["gpt-4o", "o3", "o4-mini"]
|
||||
.into_iter()
|
||||
.map(|id| AgentModelInfo {
|
||||
id: id.to_string(),
|
||||
name: None,
|
||||
variants: Some(codex_variants()),
|
||||
default_variant: Some("medium".to_string()),
|
||||
})
|
||||
.collect();
|
||||
AgentModelsResponse {
|
||||
models,
|
||||
default_model: Some("gpt-4o".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn amp_variants() -> Vec<String> {
|
||||
vec!["medium", "high", "xhigh"]
|
||||
.into_iter()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue