Split provider inventory out of providers (#9826)
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
use crate::config::paths::Paths;
|
||||
use crate::config::Config;
|
||||
use crate::providers::anthropic::AnthropicProvider;
|
||||
use crate::providers::base::{ModelInfo, ProviderDef, ProviderType};
|
||||
use crate::providers::base::{ModelInfo, ProviderType};
|
||||
use crate::providers::huggingface::HuggingFaceProvider;
|
||||
use crate::providers::huggingface_auth;
|
||||
use crate::providers::inventory::declarative_inventory_identity;
|
||||
use crate::providers::ollama::OllamaProvider;
|
||||
use crate::providers::openai::OpenAiProvider;
|
||||
@@ -665,7 +666,7 @@ fn huggingface_declarative_inventory_configured(config: &DeclarativeProviderConf
|
||||
huggingface_declarative_inventory_configured_from_sources(
|
||||
config,
|
||||
|key| Config::global().get_secret::<String>(key).is_ok(),
|
||||
HuggingFaceProvider::inventory_configured,
|
||||
|| huggingface_auth::has_configured_token().unwrap_or(false),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::config::search_path::SearchPaths;
|
||||
use crate::providers::inventory::InventoryIdentityInput;
|
||||
use anyhow::Result;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -7,10 +6,8 @@ pub fn acp_adapter_installed(command: &str) -> bool {
|
||||
resolve_acp_command(command).is_ok()
|
||||
}
|
||||
|
||||
pub fn acp_inventory_identity(provider_id: &str, command: &str) -> Result<InventoryIdentityInput> {
|
||||
let resolved_command = resolve_acp_command(command)?;
|
||||
Ok(InventoryIdentityInput::new(provider_id, provider_id)
|
||||
.with_public("command", resolved_command.display().to_string()))
|
||||
pub fn resolved_acp_command(command: &str) -> Result<PathBuf> {
|
||||
resolve_acp_command(command)
|
||||
}
|
||||
|
||||
fn resolve_acp_command(command: &str) -> Result<PathBuf> {
|
||||
|
||||
@@ -9,13 +9,11 @@ use crate::acp::{
|
||||
use crate::config::search_path::SearchPaths;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::acp_tooling::{acp_adapter_installed, acp_inventory_identity};
|
||||
use crate::providers::base::{current_working_dir, ProviderDef, ProviderMetadata};
|
||||
use crate::providers::inventory::InventoryIdentityInput;
|
||||
|
||||
const AMP_ACP_PROVIDER_NAME: &str = "amp-acp";
|
||||
pub(crate) const AMP_ACP_PROVIDER_NAME: &str = "amp-acp";
|
||||
const AMP_ACP_DOC_URL: &str = "https://ampcode.com";
|
||||
const AMP_ACP_BINARY: &str = "amp-acp";
|
||||
pub(crate) const AMP_ACP_BINARY: &str = "amp-acp";
|
||||
|
||||
pub struct AmpAcpProvider;
|
||||
|
||||
@@ -84,16 +82,4 @@ impl ProviderDef for AmpAcpProvider {
|
||||
AcpProvider::connect(metadata.name, model, goose_mode, provider_config).await
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
acp_inventory_identity(AMP_ACP_PROVIDER_NAME, AMP_ACP_BINARY)
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
acp_adapter_installed(AMP_ACP_BINARY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ use super::formats::anthropic::{
|
||||
create_request_with_options_for_provider, response_to_streaming_message, thinking_type,
|
||||
AnthropicFormatOptions, ThinkingType, ANTHROPIC_PROVIDER_NAME,
|
||||
};
|
||||
use super::inventory::{config_secret_value, serialize_string_map, InventoryIdentityInput};
|
||||
use super::openai_compatible::handle_status;
|
||||
use super::openai_compatible::map_http_error_to_provider_error;
|
||||
use super::retry::ProviderRetry;
|
||||
@@ -267,33 +266,6 @@ impl ProviderDef for AnthropicProvider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
let config = crate::config::Config::global();
|
||||
let mut identity =
|
||||
InventoryIdentityInput::new(ANTHROPIC_PROVIDER_NAME, ANTHROPIC_PROVIDER_NAME)
|
||||
.with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("ANTHROPIC_HOST")
|
||||
.unwrap_or_else(|_| "https://api.anthropic.com".to_string()),
|
||||
);
|
||||
|
||||
if let Some(api_key) = config_secret_value(config, "ANTHROPIC_API_KEY") {
|
||||
identity = identity.with_secret("api_key", api_key);
|
||||
}
|
||||
if let Ok(headers) = config
|
||||
.get_secret::<std::collections::HashMap<String, String>>("ANTHROPIC_CUSTOM_HEADERS")
|
||||
{
|
||||
identity = identity.with_secret("headers", serialize_string_map(&headers)?);
|
||||
}
|
||||
|
||||
Ok(identity)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -15,10 +15,9 @@ use serde::{Deserialize, Serialize};
|
||||
pub const DEFAULT_PROVIDER_TIMEOUT_SECS: u64 = 600;
|
||||
|
||||
use super::canonical::{map_to_canonical_model, CanonicalModelRegistry};
|
||||
use super::inventory::{default_inventory_identity, InventoryIdentityInput};
|
||||
use super::retry::RetryConfig;
|
||||
use crate::config::base::ConfigValue;
|
||||
use crate::config::{Config, ExtensionConfig, GooseMode};
|
||||
use crate::config::{ExtensionConfig, GooseMode};
|
||||
use crate::conversation::message::{Message, MessageContent};
|
||||
use crate::conversation::Conversation;
|
||||
use crate::model::ModelConfig;
|
||||
@@ -421,34 +420,6 @@ pub trait ProviderDef: Send + Sync {
|
||||
// Non-subprocess providers can rely on the default because cwd is irrelevant.
|
||||
Self::from_env(model, extensions)
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let metadata = Self::metadata();
|
||||
Ok(default_inventory_identity(
|
||||
&metadata.name,
|
||||
&metadata.name,
|
||||
&metadata.config_keys,
|
||||
Config::global(),
|
||||
))
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let metadata = Self::metadata();
|
||||
super::inventory::default_inventory_configured(&metadata.config_keys, Config::global())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
|
||||
@@ -319,7 +319,7 @@ struct TokenData {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TokenCache {
|
||||
pub(crate) struct TokenCache {
|
||||
cache_path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ fn get_cache_path() -> PathBuf {
|
||||
}
|
||||
|
||||
impl TokenCache {
|
||||
fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
let cache_path = get_cache_path();
|
||||
if let Some(parent) = cache_path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
@@ -343,6 +343,9 @@ impl TokenCache {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub(crate) fn has_token(&self) -> bool {
|
||||
self.load().is_some()
|
||||
}
|
||||
|
||||
fn save(&self, token_data: &TokenData) -> Result<()> {
|
||||
if let Some(parent) = self.cache_path.parent() {
|
||||
@@ -976,10 +979,6 @@ impl ProviderDef for ChatGptCodexProvider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
TokenCache::new().load().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -1098,7 +1097,7 @@ mod tests {
|
||||
let _guard = env_lock::lock_env([("GOOSE_PATH_ROOT", Some(root_path.as_str()))]);
|
||||
|
||||
TokenCache::new().clear();
|
||||
assert!(!ChatGptCodexProvider::inventory_configured());
|
||||
assert!(!TokenCache::new().has_token());
|
||||
|
||||
TokenCache::new()
|
||||
.save(&TokenData {
|
||||
@@ -1110,7 +1109,7 @@ mod tests {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(ChatGptCodexProvider::inventory_configured());
|
||||
assert!(TokenCache::new().has_token());
|
||||
}
|
||||
|
||||
#[test_case(
|
||||
|
||||
@@ -9,13 +9,11 @@ use crate::acp::{
|
||||
use crate::config::search_path::SearchPaths;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::acp_tooling::{acp_adapter_installed, acp_inventory_identity};
|
||||
use crate::providers::base::{current_working_dir, ProviderDef, ProviderMetadata};
|
||||
use crate::providers::inventory::InventoryIdentityInput;
|
||||
|
||||
const CLAUDE_ACP_PROVIDER_NAME: &str = "claude-acp";
|
||||
pub(crate) const CLAUDE_ACP_PROVIDER_NAME: &str = "claude-acp";
|
||||
const CLAUDE_ACP_DOC_URL: &str = "https://github.com/agentclientprotocol/claude-agent-acp";
|
||||
const CLAUDE_ACP_BINARY: &str = "claude-agent-acp";
|
||||
pub(crate) const CLAUDE_ACP_BINARY: &str = "claude-agent-acp";
|
||||
|
||||
pub struct ClaudeAcpProvider;
|
||||
|
||||
@@ -88,16 +86,4 @@ impl ProviderDef for ClaudeAcpProvider {
|
||||
AcpProvider::connect(metadata.name, model, goose_mode, provider_config).await
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
acp_inventory_identity(CLAUDE_ACP_PROVIDER_NAME, CLAUDE_ACP_BINARY)
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
acp_adapter_installed(CLAUDE_ACP_BINARY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,9 @@ use crate::acp::{
|
||||
use crate::config::search_path::SearchPaths;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::acp_tooling::{acp_adapter_installed, acp_inventory_identity};
|
||||
use crate::providers::base::{current_working_dir, ProviderDef, ProviderMetadata};
|
||||
use crate::providers::inventory::InventoryIdentityInput;
|
||||
|
||||
const CODEX_ACP_PROVIDER_NAME: &str = "codex-acp";
|
||||
pub(crate) const CODEX_ACP_PROVIDER_NAME: &str = "codex-acp";
|
||||
const CODEX_ACP_DOC_URL: &str = "https://github.com/zed-industries/codex-acp";
|
||||
|
||||
pub struct CodexAcpProvider;
|
||||
@@ -107,18 +105,6 @@ impl ProviderDef for CodexAcpProvider {
|
||||
AcpProvider::connect(metadata.name, model, goose_mode, provider_config).await
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
acp_inventory_identity(CODEX_ACP_PROVIDER_NAME, CODEX_ACP_PROVIDER_NAME)
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
acp_adapter_installed(CODEX_ACP_PROVIDER_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
// Codex sandbox scope determines what needs approval: operations within the
|
||||
|
||||
@@ -9,13 +9,11 @@ use crate::acp::{
|
||||
use crate::config::search_path::SearchPaths;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::acp_tooling::{acp_adapter_installed, acp_inventory_identity};
|
||||
use crate::providers::base::{current_working_dir, ProviderDef, ProviderMetadata};
|
||||
use crate::providers::inventory::InventoryIdentityInput;
|
||||
|
||||
const COPILOT_ACP_PROVIDER_NAME: &str = "copilot-acp";
|
||||
pub(crate) const COPILOT_ACP_PROVIDER_NAME: &str = "copilot-acp";
|
||||
const COPILOT_ACP_DOC_URL: &str = "https://github.com/github/copilot-cli";
|
||||
const COPILOT_ACP_BINARY: &str = "copilot";
|
||||
pub(crate) const COPILOT_ACP_BINARY: &str = "copilot";
|
||||
|
||||
const MODE_AGENT: &str = "https://agentclientprotocol.com/protocol/session-modes#agent";
|
||||
const MODE_PLAN: &str = "https://agentclientprotocol.com/protocol/session-modes#plan";
|
||||
@@ -94,16 +92,4 @@ impl ProviderDef for CopilotAcpProvider {
|
||||
AcpProvider::connect(metadata.name, model, goose_mode, provider_config).await
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
acp_inventory_identity(COPILOT_ACP_PROVIDER_NAME, COPILOT_ACP_BINARY)
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
acp_adapter_installed(COPILOT_ACP_BINARY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,10 +613,6 @@ impl ProviderDef for DatabricksProvider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -369,10 +369,6 @@ impl ProviderDef for DatabricksV2Provider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -9,7 +9,6 @@ use goose_providers::errors::ProviderError;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::base::{ConfigKey, Provider, ProviderDef, ProviderMetadata};
|
||||
use crate::providers::formats::google::{create_request, response_to_streaming_message};
|
||||
use crate::providers::inventory::{config_secret_value, InventoryIdentityInput};
|
||||
use anyhow::Result;
|
||||
use async_stream::try_stream;
|
||||
use async_trait::async_trait;
|
||||
@@ -23,7 +22,7 @@ use tokio_stream::StreamExt;
|
||||
use tokio_util::codec::{FramedRead, LinesCodec};
|
||||
use tokio_util::io::StreamReader;
|
||||
|
||||
const GOOGLE_PROVIDER_NAME: &str = "google";
|
||||
pub(crate) const GOOGLE_PROVIDER_NAME: &str = "google";
|
||||
pub const GOOGLE_API_HOST: &str = "https://generativelanguage.googleapis.com";
|
||||
pub const GOOGLE_DEFAULT_MODEL: &str = "gemini-2.5-pro";
|
||||
pub const GOOGLE_DEFAULT_FAST_MODEL: &str = "gemini-2.5-flash";
|
||||
@@ -136,27 +135,6 @@ impl ProviderDef for GoogleProvider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
let config = crate::config::Config::global();
|
||||
let mut identity = InventoryIdentityInput::new(GOOGLE_PROVIDER_NAME, GOOGLE_PROVIDER_NAME)
|
||||
.with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("GOOGLE_HOST")
|
||||
.unwrap_or_else(|_| GOOGLE_API_HOST.to_string()),
|
||||
);
|
||||
|
||||
if let Some(api_key) = config_secret_value(config, "GOOGLE_API_KEY") {
|
||||
identity = identity.with_secret("api_key", api_key);
|
||||
}
|
||||
|
||||
Ok(identity)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -4,7 +4,6 @@ use super::base::{
|
||||
DEFAULT_PROVIDER_TIMEOUT_SECS,
|
||||
};
|
||||
use super::huggingface_auth;
|
||||
use super::inventory::{default_inventory_identity, InventoryIdentityInput};
|
||||
use super::openai_compatible::OpenAiCompatibleProvider;
|
||||
use crate::config::declarative_providers::DeclarativeProviderConfig;
|
||||
use crate::config::{Config, ConfigError};
|
||||
@@ -229,20 +228,6 @@ impl ProviderDef for HuggingFaceProvider {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
let metadata = Self::metadata();
|
||||
Ok(default_inventory_identity(
|
||||
&metadata.name,
|
||||
&metadata.name,
|
||||
&metadata.config_keys,
|
||||
Config::global(),
|
||||
))
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
huggingface_auth::has_configured_token().unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_token_error() -> anyhow::Error {
|
||||
|
||||
@@ -55,42 +55,86 @@ static REGISTRY: OnceCell<RwLock<ProviderRegistry>> = OnceCell::const_new();
|
||||
|
||||
async fn init_registry() -> RwLock<ProviderRegistry> {
|
||||
let mut registry = ProviderRegistry::new().with_providers(|registry| {
|
||||
registry.register::<AmpAcpProvider>(false);
|
||||
registry.register::<AnthropicProvider>(true);
|
||||
use super::inventory::registrations;
|
||||
|
||||
registry.register_with_inventory::<AmpAcpProvider>(
|
||||
false,
|
||||
Some(registrations::amp_acp_inventory()),
|
||||
);
|
||||
registry.register_with_inventory::<AnthropicProvider>(
|
||||
true,
|
||||
Some(registrations::anthropic_inventory()),
|
||||
);
|
||||
registry.register::<AvianProvider>(false);
|
||||
registry.register::<AzureProvider>(false);
|
||||
#[cfg(feature = "aws-providers")]
|
||||
registry.register::<BedrockProvider>(false);
|
||||
#[cfg(feature = "local-inference")]
|
||||
registry.register::<LocalInferenceProvider>(false);
|
||||
registry.register::<ChatGptCodexProvider>(true);
|
||||
registry.register::<ClaudeAcpProvider>(false);
|
||||
registry.register_with_inventory::<ChatGptCodexProvider>(
|
||||
true,
|
||||
Some(registrations::chatgpt_codex_inventory()),
|
||||
);
|
||||
registry.register_with_inventory::<ClaudeAcpProvider>(
|
||||
false,
|
||||
Some(registrations::claude_acp_inventory()),
|
||||
);
|
||||
registry.register::<ClaudeCodeProvider>(true);
|
||||
registry.register::<CodexAcpProvider>(false);
|
||||
registry.register::<CopilotAcpProvider>(false);
|
||||
registry.register_with_inventory::<CodexAcpProvider>(
|
||||
false,
|
||||
Some(registrations::codex_acp_inventory()),
|
||||
);
|
||||
registry.register_with_inventory::<CopilotAcpProvider>(
|
||||
false,
|
||||
Some(registrations::copilot_acp_inventory()),
|
||||
);
|
||||
registry.register::<CodexProvider>(true);
|
||||
registry.register::<CursorAgentProvider>(false);
|
||||
registry.register::<DatabricksProvider>(true);
|
||||
registry.register::<DatabricksV2Provider>(false);
|
||||
registry.register_with_inventory::<DatabricksProvider>(
|
||||
true,
|
||||
Some(registrations::refresh_only()),
|
||||
);
|
||||
registry.register_with_inventory::<DatabricksV2Provider>(
|
||||
false,
|
||||
Some(registrations::refresh_only()),
|
||||
);
|
||||
registry.register::<GcpVertexAIProvider>(false);
|
||||
registry.register::<GeminiCliProvider>(false);
|
||||
registry.register::<GeminiOAuthProvider>(true);
|
||||
registry.register::<GithubCopilotProvider>(false);
|
||||
registry.register::<GoogleProvider>(true);
|
||||
registry.register::<HuggingFaceProvider>(true);
|
||||
registry.register_with_inventory::<GoogleProvider>(
|
||||
true,
|
||||
Some(registrations::google_inventory()),
|
||||
);
|
||||
registry.register_with_inventory::<HuggingFaceProvider>(
|
||||
true,
|
||||
Some(registrations::huggingface_inventory()),
|
||||
);
|
||||
registry.register::<KimiCodeProvider>(true);
|
||||
registry.register::<LiteLLMProvider>(false);
|
||||
registry.register::<NanoGptProvider>(true);
|
||||
registry.register::<OllamaProvider>(true);
|
||||
registry.register::<OpenAiProvider>(true);
|
||||
registry.register_with_inventory::<OllamaProvider>(
|
||||
true,
|
||||
Some(registrations::ollama_inventory()),
|
||||
);
|
||||
registry.register_with_inventory::<OpenAiProvider>(
|
||||
true,
|
||||
Some(registrations::openai_inventory()),
|
||||
);
|
||||
registry.register::<OpenRouterProvider>(true);
|
||||
registry.register::<PiAcpProvider>(false);
|
||||
registry.register_with_inventory::<PiAcpProvider>(
|
||||
false,
|
||||
Some(registrations::pi_acp_inventory()),
|
||||
);
|
||||
#[cfg(feature = "aws-providers")]
|
||||
registry.register::<SageMakerTgiProvider>(false);
|
||||
registry.register::<SnowflakeProvider>(false);
|
||||
registry.register::<TetrateProvider>(true);
|
||||
registry.register::<XaiProvider>(false);
|
||||
registry.register::<XaiOAuthProvider>(true);
|
||||
registry.register_with_inventory::<XaiOAuthProvider>(
|
||||
true,
|
||||
Some(registrations::xai_oauth_inventory()),
|
||||
);
|
||||
});
|
||||
// Register cleanup functions for providers with cached state
|
||||
registry.set_cleanup(
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
pub mod registrations;
|
||||
mod resolver;
|
||||
|
||||
pub use resolver::{
|
||||
default_inventory_identity_resolver, InventoryConfiguredResolver, InventoryIdentityResolver,
|
||||
InventoryRegistration, InventoryResolvers,
|
||||
};
|
||||
|
||||
use super::base::{ConfigKey, ModelInfo, Provider, ProviderType};
|
||||
use super::canonical::{map_provider_name, map_to_canonical_model, CanonicalModelRegistry};
|
||||
use super::catalog::ProviderSetupCategory;
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
use super::{
|
||||
config_secret_value, default_inventory_identity, default_inventory_identity_resolver,
|
||||
serialize_string_map, InventoryIdentityInput, InventoryRegistration,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::providers::acp_tooling::{acp_adapter_installed, resolved_acp_command};
|
||||
use crate::providers::amp_acp::{AMP_ACP_BINARY, AMP_ACP_PROVIDER_NAME};
|
||||
use crate::providers::base::ProviderDef;
|
||||
use crate::providers::chatgpt_codex::TokenCache as ChatGptCodexTokenCache;
|
||||
use crate::providers::claude_acp::{CLAUDE_ACP_BINARY, CLAUDE_ACP_PROVIDER_NAME};
|
||||
use crate::providers::codex_acp::CODEX_ACP_PROVIDER_NAME;
|
||||
use crate::providers::copilot_acp::{COPILOT_ACP_BINARY, COPILOT_ACP_PROVIDER_NAME};
|
||||
use crate::providers::formats::anthropic::ANTHROPIC_PROVIDER_NAME;
|
||||
use crate::providers::google::{GOOGLE_API_HOST, GOOGLE_PROVIDER_NAME};
|
||||
use crate::providers::huggingface::HuggingFaceProvider;
|
||||
use crate::providers::huggingface_auth;
|
||||
use crate::providers::ollama::{ollama_host_configured, OLLAMA_PROVIDER_NAME};
|
||||
use crate::providers::openai::{OPEN_AI_DEFAULT_BASE_PATH, OPEN_AI_PROVIDER_NAME};
|
||||
use crate::providers::pi_acp::{PI_ACP_BINARY, PI_ACP_PROVIDER_NAME};
|
||||
use crate::providers::xai_oauth::TokenCache as XaiOAuthTokenCache;
|
||||
|
||||
pub fn openai_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration::new(true, || {
|
||||
let config = Config::global();
|
||||
let mut identity =
|
||||
InventoryIdentityInput::new(OPEN_AI_PROVIDER_NAME, OPEN_AI_PROVIDER_NAME)
|
||||
.with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("OPENAI_HOST")
|
||||
.unwrap_or_else(|_| "https://api.openai.com".to_string()),
|
||||
)
|
||||
.with_public(
|
||||
"base_path",
|
||||
config
|
||||
.get_param::<String>("OPENAI_BASE_PATH")
|
||||
.unwrap_or_else(|_| OPEN_AI_DEFAULT_BASE_PATH.to_string()),
|
||||
);
|
||||
|
||||
if let Ok(organization) = config.get_param::<String>("OPENAI_ORGANIZATION") {
|
||||
identity = identity.with_public("organization", organization);
|
||||
}
|
||||
if let Ok(project) = config.get_param::<String>("OPENAI_PROJECT") {
|
||||
identity = identity.with_public("project", project);
|
||||
}
|
||||
if let Some(api_key) = config_secret_value(config, "OPENAI_API_KEY") {
|
||||
identity = identity.with_secret("api_key", api_key);
|
||||
}
|
||||
if let Some(custom_headers) = config_secret_value(config, "OPENAI_CUSTOM_HEADERS") {
|
||||
identity = identity.with_secret("custom_headers", custom_headers);
|
||||
}
|
||||
|
||||
Ok(identity)
|
||||
})
|
||||
.with_configured(|| {
|
||||
let config = Config::global();
|
||||
if let Ok(host) = config.get_param::<String>("OPENAI_HOST") {
|
||||
if host != "https://api.openai.com" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
config
|
||||
.get_secret::<serde_json::Value>("OPENAI_API_KEY")
|
||||
.is_ok()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn anthropic_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration::new(true, || {
|
||||
let config = Config::global();
|
||||
let mut identity =
|
||||
InventoryIdentityInput::new(ANTHROPIC_PROVIDER_NAME, ANTHROPIC_PROVIDER_NAME)
|
||||
.with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("ANTHROPIC_HOST")
|
||||
.unwrap_or_else(|_| "https://api.anthropic.com".to_string()),
|
||||
);
|
||||
|
||||
if let Some(api_key) = config_secret_value(config, "ANTHROPIC_API_KEY") {
|
||||
identity = identity.with_secret("api_key", api_key);
|
||||
}
|
||||
if let Ok(headers) = config
|
||||
.get_secret::<std::collections::HashMap<String, String>>("ANTHROPIC_CUSTOM_HEADERS")
|
||||
{
|
||||
identity = identity.with_secret("headers", serialize_string_map(&headers)?);
|
||||
}
|
||||
Ok(identity)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn google_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration::new(true, || {
|
||||
let config = Config::global();
|
||||
let mut identity = InventoryIdentityInput::new(GOOGLE_PROVIDER_NAME, GOOGLE_PROVIDER_NAME)
|
||||
.with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("GOOGLE_HOST")
|
||||
.unwrap_or_else(|_| GOOGLE_API_HOST.to_string()),
|
||||
);
|
||||
if let Some(api_key) = config_secret_value(config, "GOOGLE_API_KEY") {
|
||||
identity = identity.with_secret("api_key", api_key);
|
||||
}
|
||||
Ok(identity)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ollama_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration::new(true, || {
|
||||
let config = Config::global();
|
||||
Ok(
|
||||
InventoryIdentityInput::new(OLLAMA_PROVIDER_NAME, OLLAMA_PROVIDER_NAME).with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("OLLAMA_HOST")
|
||||
.unwrap_or_else(|_| "http://localhost:11434".to_string()),
|
||||
),
|
||||
)
|
||||
})
|
||||
.with_configured(|| ollama_host_configured(Config::global()))
|
||||
}
|
||||
|
||||
pub fn huggingface_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration::new(false, || {
|
||||
let metadata = HuggingFaceProvider::metadata();
|
||||
Ok(default_inventory_identity(
|
||||
&metadata.name,
|
||||
&metadata.name,
|
||||
&metadata.config_keys,
|
||||
Config::global(),
|
||||
))
|
||||
})
|
||||
.with_configured(|| huggingface_auth::has_configured_token().unwrap_or(false))
|
||||
}
|
||||
|
||||
pub fn refresh_only() -> InventoryRegistration {
|
||||
InventoryRegistration {
|
||||
supports_refresh: true,
|
||||
identity: default_inventory_identity_resolver(),
|
||||
configured: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chatgpt_codex_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration {
|
||||
supports_refresh: false,
|
||||
identity: default_inventory_identity_resolver(),
|
||||
configured: None,
|
||||
}
|
||||
.with_configured(|| ChatGptCodexTokenCache::new().has_token())
|
||||
}
|
||||
|
||||
pub fn xai_oauth_inventory() -> InventoryRegistration {
|
||||
InventoryRegistration {
|
||||
supports_refresh: false,
|
||||
identity: default_inventory_identity_resolver(),
|
||||
configured: None,
|
||||
}
|
||||
.with_configured(|| XaiOAuthTokenCache::new().has_token())
|
||||
}
|
||||
|
||||
pub fn acp_inventory(
|
||||
provider_id: &'static str,
|
||||
command: &'static str,
|
||||
supports_refresh: bool,
|
||||
) -> InventoryRegistration {
|
||||
InventoryRegistration::new(supports_refresh, move || {
|
||||
let resolved_command = resolved_acp_command(command)?;
|
||||
Ok(InventoryIdentityInput::new(provider_id, provider_id)
|
||||
.with_public("command", resolved_command.display().to_string()))
|
||||
})
|
||||
.with_configured(move || acp_adapter_installed(command))
|
||||
}
|
||||
|
||||
pub fn amp_acp_inventory() -> InventoryRegistration {
|
||||
acp_inventory(AMP_ACP_PROVIDER_NAME, AMP_ACP_BINARY, false)
|
||||
}
|
||||
|
||||
pub fn claude_acp_inventory() -> InventoryRegistration {
|
||||
acp_inventory(CLAUDE_ACP_PROVIDER_NAME, CLAUDE_ACP_BINARY, true)
|
||||
}
|
||||
|
||||
pub fn codex_acp_inventory() -> InventoryRegistration {
|
||||
acp_inventory(CODEX_ACP_PROVIDER_NAME, CODEX_ACP_PROVIDER_NAME, true)
|
||||
}
|
||||
|
||||
pub fn copilot_acp_inventory() -> InventoryRegistration {
|
||||
acp_inventory(COPILOT_ACP_PROVIDER_NAME, COPILOT_ACP_BINARY, true)
|
||||
}
|
||||
|
||||
pub fn pi_acp_inventory() -> InventoryRegistration {
|
||||
acp_inventory(PI_ACP_PROVIDER_NAME, PI_ACP_BINARY, false)
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
use super::{default_inventory_configured, default_inventory_identity, InventoryIdentityInput};
|
||||
use crate::config::Config;
|
||||
use crate::providers::base::ProviderMetadata;
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
|
||||
static DEFAULT_INVENTORY_IDENTITY_RESOLVER: Lazy<InventoryIdentityResolver> =
|
||||
Lazy::new(|| Arc::new(|| unreachable!("default inventory identity resolver marker")));
|
||||
|
||||
pub fn default_inventory_identity_resolver() -> InventoryIdentityResolver {
|
||||
Arc::clone(&DEFAULT_INVENTORY_IDENTITY_RESOLVER)
|
||||
}
|
||||
|
||||
pub type InventoryIdentityResolver = Arc<dyn Fn() -> Result<InventoryIdentityInput> + Send + Sync>;
|
||||
pub type InventoryConfiguredResolver = Arc<dyn Fn() -> bool + Send + Sync>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InventoryRegistration {
|
||||
pub supports_refresh: bool,
|
||||
pub identity: InventoryIdentityResolver,
|
||||
pub configured: Option<InventoryConfiguredResolver>,
|
||||
}
|
||||
|
||||
impl InventoryRegistration {
|
||||
pub fn new<G>(supports_refresh: bool, identity: G) -> Self
|
||||
where
|
||||
G: Fn() -> Result<InventoryIdentityInput> + Send + Sync + 'static,
|
||||
{
|
||||
Self {
|
||||
supports_refresh,
|
||||
identity: Arc::new(identity),
|
||||
configured: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_configured<H>(mut self, configured: H) -> Self
|
||||
where
|
||||
H: Fn() -> bool + Send + Sync + 'static,
|
||||
{
|
||||
self.configured = Some(Arc::new(configured));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InventoryResolvers {
|
||||
pub supports_refresh: bool,
|
||||
pub identity: InventoryIdentityResolver,
|
||||
pub configured: InventoryConfiguredResolver,
|
||||
}
|
||||
|
||||
impl InventoryResolvers {
|
||||
pub fn for_metadata(
|
||||
metadata: &ProviderMetadata,
|
||||
registration: Option<InventoryRegistration>,
|
||||
) -> Self {
|
||||
let metadata_for_identity = metadata.clone();
|
||||
let default_identity = Arc::new(move || {
|
||||
Ok(default_inventory_identity(
|
||||
&metadata_for_identity.name,
|
||||
&metadata_for_identity.name,
|
||||
&metadata_for_identity.config_keys,
|
||||
Config::global(),
|
||||
))
|
||||
});
|
||||
|
||||
let config_keys = metadata.config_keys.clone();
|
||||
let default_configured =
|
||||
Arc::new(move || default_inventory_configured(&config_keys, Config::global()));
|
||||
|
||||
match registration {
|
||||
Some(registration) => Self {
|
||||
supports_refresh: registration.supports_refresh,
|
||||
identity: if Arc::ptr_eq(
|
||||
®istration.identity,
|
||||
&default_inventory_identity_resolver(),
|
||||
) {
|
||||
default_identity
|
||||
} else {
|
||||
registration.identity
|
||||
},
|
||||
configured: registration.configured.unwrap_or(default_configured),
|
||||
},
|
||||
None => Self {
|
||||
supports_refresh: false,
|
||||
identity: default_identity,
|
||||
configured: default_configured,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ use super::base::{
|
||||
ConfigKey, MessageStream, Provider, ProviderDef, ProviderMetadata,
|
||||
DEFAULT_PROVIDER_TIMEOUT_SECS,
|
||||
};
|
||||
use super::inventory::InventoryIdentityInput;
|
||||
use super::openai_compatible::handle_status;
|
||||
use super::retry::{ProviderRetry, RetryConfig};
|
||||
use super::utils::RequestLog;
|
||||
@@ -29,7 +28,7 @@ use tokio_util::codec::{FramedRead, LinesCodec};
|
||||
use tokio_util::io::StreamReader;
|
||||
use url::Url;
|
||||
|
||||
const OLLAMA_PROVIDER_NAME: &str = "ollama";
|
||||
pub(crate) const OLLAMA_PROVIDER_NAME: &str = "ollama";
|
||||
pub const OLLAMA_HOST: &str = "localhost";
|
||||
pub const OLLAMA_TIMEOUT: u64 = DEFAULT_PROVIDER_TIMEOUT_SECS;
|
||||
pub const OLLAMA_DEFAULT_PORT: u16 = 11434;
|
||||
@@ -127,7 +126,7 @@ fn apply_ollama_options(payload: &mut Value, model_config: &ModelConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
fn ollama_host_configured(config: &crate::config::Config) -> bool {
|
||||
pub(crate) fn ollama_host_configured(config: &crate::config::Config) -> bool {
|
||||
config.get_param::<String>("OLLAMA_HOST").is_ok()
|
||||
}
|
||||
|
||||
@@ -266,26 +265,6 @@ impl ProviderDef for OllamaProvider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
ollama_host_configured(crate::config::Config::global())
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
let config = crate::config::Config::global();
|
||||
Ok(
|
||||
InventoryIdentityInput::new(OLLAMA_PROVIDER_NAME, OLLAMA_PROVIDER_NAME).with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("OLLAMA_HOST")
|
||||
.unwrap_or_else(|_| OLLAMA_HOST.to_string()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -6,7 +6,6 @@ use super::embedding::{EmbeddingCapable, EmbeddingRequest, EmbeddingResponse};
|
||||
use super::formats::openai_responses::{
|
||||
create_responses_request, get_responses_usage, responses_api_to_message, ResponsesApiResponse,
|
||||
};
|
||||
use super::inventory::{config_secret_value, InventoryIdentityInput};
|
||||
use super::openai_compatible::{
|
||||
handle_response_openai_compat, handle_status, stream_openai_compat, stream_responses_compat,
|
||||
};
|
||||
@@ -31,8 +30,8 @@ use crate::providers::base::MessageStream;
|
||||
use crate::providers::utils::RequestLog;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
const OPEN_AI_PROVIDER_NAME: &str = "openai";
|
||||
const OPEN_AI_DEFAULT_BASE_PATH: &str = "v1/chat/completions";
|
||||
pub(crate) const OPEN_AI_PROVIDER_NAME: &str = "openai";
|
||||
pub(crate) const OPEN_AI_DEFAULT_BASE_PATH: &str = "v1/chat/completions";
|
||||
const OPEN_AI_VERSIONLESS_BASE_PATH: &str = "chat/completions";
|
||||
const OPEN_AI_DEFAULT_RESPONSES_PATH: &str = "v1/responses";
|
||||
const OPEN_AI_DEFAULT_MODELS_PATH: &str = "v1/models";
|
||||
@@ -772,58 +771,6 @@ impl ProviderDef for OpenAiProvider {
|
||||
) -> BoxFuture<'static, Result<Self::Provider>> {
|
||||
Box::pin(Self::from_env(model))
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
let config = crate::config::Config::global();
|
||||
// If the host is explicitly set to something non-default, trust the user's
|
||||
// custom setup (e.g. a local server that doesn't require an API key).
|
||||
if let Ok(host) = config.get_param::<String>("OPENAI_HOST") {
|
||||
if host != "https://api.openai.com" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Standard OpenAI endpoint requires an API key.
|
||||
config
|
||||
.get_secret::<serde_json::Value>("OPENAI_API_KEY")
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
let config = crate::config::Config::global();
|
||||
let mut identity =
|
||||
InventoryIdentityInput::new(OPEN_AI_PROVIDER_NAME, OPEN_AI_PROVIDER_NAME)
|
||||
.with_public(
|
||||
"host",
|
||||
config
|
||||
.get_param::<String>("OPENAI_HOST")
|
||||
.unwrap_or_else(|_| "https://api.openai.com".to_string()),
|
||||
)
|
||||
.with_public(
|
||||
"base_path",
|
||||
config
|
||||
.get_param::<String>("OPENAI_BASE_PATH")
|
||||
.unwrap_or_else(|_| OPEN_AI_DEFAULT_BASE_PATH.to_string()),
|
||||
);
|
||||
|
||||
if let Ok(organization) = config.get_param::<String>("OPENAI_ORGANIZATION") {
|
||||
identity = identity.with_public("organization", organization);
|
||||
}
|
||||
if let Ok(project) = config.get_param::<String>("OPENAI_PROJECT") {
|
||||
identity = identity.with_public("project", project);
|
||||
}
|
||||
if let Some(api_key) = config_secret_value(config, "OPENAI_API_KEY") {
|
||||
identity = identity.with_secret("api_key", api_key);
|
||||
}
|
||||
if let Some(custom_headers) = config_secret_value(config, "OPENAI_CUSTOM_HEADERS") {
|
||||
identity = identity.with_secret("custom_headers", custom_headers);
|
||||
}
|
||||
|
||||
Ok(identity)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -9,13 +9,11 @@ use crate::acp::{
|
||||
use crate::config::search_path::SearchPaths;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::acp_tooling::{acp_adapter_installed, acp_inventory_identity};
|
||||
use crate::providers::base::{current_working_dir, ProviderDef, ProviderMetadata};
|
||||
use crate::providers::inventory::InventoryIdentityInput;
|
||||
|
||||
const PI_ACP_PROVIDER_NAME: &str = "pi-acp";
|
||||
pub(crate) const PI_ACP_PROVIDER_NAME: &str = "pi-acp";
|
||||
const PI_ACP_DOC_URL: &str = "https://github.com/anthropics/pi";
|
||||
const PI_ACP_BINARY: &str = "pi-acp";
|
||||
pub(crate) const PI_ACP_BINARY: &str = "pi-acp";
|
||||
|
||||
pub struct PiAcpProvider;
|
||||
|
||||
@@ -81,16 +79,4 @@ impl ProviderDef for PiAcpProvider {
|
||||
AcpProvider::connect(metadata.name, model, goose_mode, provider_config).await
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_inventory_refresh() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn inventory_identity() -> Result<InventoryIdentityInput> {
|
||||
acp_inventory_identity(PI_ACP_PROVIDER_NAME, PI_ACP_BINARY)
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
acp_adapter_installed(PI_ACP_BINARY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::base::{ConfigKey, ModelInfo, Provider, ProviderDef, ProviderMetadata, ProviderType};
|
||||
use super::inventory::InventoryIdentityInput;
|
||||
use super::inventory::{InventoryIdentityInput, InventoryRegistration, InventoryResolvers};
|
||||
use crate::config::{DeclarativeProviderConfig, ExtensionConfig};
|
||||
use crate::model::ModelConfig;
|
||||
use anyhow::Result;
|
||||
@@ -20,17 +20,12 @@ pub type ProviderConstructor = Arc<
|
||||
|
||||
pub type ProviderCleanup = Arc<dyn Fn() -> BoxFuture<'static, Result<()>> + Send + Sync>;
|
||||
|
||||
pub type ProviderInventoryIdentityResolver =
|
||||
Arc<dyn Fn() -> Result<InventoryIdentityInput> + Send + Sync>;
|
||||
|
||||
pub type ProviderInventoryConfiguredResolver = Arc<dyn Fn() -> bool + Send + Sync>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProviderEntry {
|
||||
metadata: ProviderMetadata,
|
||||
pub(crate) constructor: ProviderConstructor,
|
||||
pub(crate) inventory_identity: ProviderInventoryIdentityResolver,
|
||||
pub(crate) inventory_configured: ProviderInventoryConfiguredResolver,
|
||||
pub(crate) inventory_identity: super::inventory::InventoryIdentityResolver,
|
||||
pub(crate) inventory_configured: super::inventory::InventoryConfiguredResolver,
|
||||
pub(crate) cleanup: Option<ProviderCleanup>,
|
||||
provider_type: ProviderType,
|
||||
supports_inventory_refresh: bool,
|
||||
@@ -118,10 +113,22 @@ impl ProviderRegistry {
|
||||
pub fn register<F>(&mut self, preferred: bool)
|
||||
where
|
||||
F: ProviderDef + 'static,
|
||||
{
|
||||
self.register_with_inventory::<F>(preferred, None);
|
||||
}
|
||||
|
||||
pub fn register_with_inventory<F>(
|
||||
&mut self,
|
||||
preferred: bool,
|
||||
inventory_registration: Option<InventoryRegistration>,
|
||||
) where
|
||||
F: ProviderDef + 'static,
|
||||
{
|
||||
let metadata = F::metadata();
|
||||
let name = metadata.name.clone();
|
||||
|
||||
let inventory = InventoryResolvers::for_metadata(&metadata, inventory_registration);
|
||||
|
||||
self.entries.insert(
|
||||
name,
|
||||
ProviderEntry {
|
||||
@@ -137,15 +144,15 @@ impl ProviderRegistry {
|
||||
Ok(Arc::new(provider) as Arc<dyn Provider>)
|
||||
})
|
||||
}),
|
||||
inventory_identity: Arc::new(F::inventory_identity),
|
||||
inventory_configured: Arc::new(F::inventory_configured),
|
||||
inventory_identity: inventory.identity,
|
||||
inventory_configured: inventory.configured,
|
||||
cleanup: None,
|
||||
provider_type: if preferred {
|
||||
ProviderType::Preferred
|
||||
} else {
|
||||
ProviderType::Builtin
|
||||
},
|
||||
supports_inventory_refresh: F::supports_inventory_refresh(),
|
||||
supports_inventory_refresh: inventory.supports_refresh,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -203,7 +210,7 @@ impl ProviderRegistry {
|
||||
supports_inventory_refresh: bool,
|
||||
constructor: F,
|
||||
inventory_identity: G,
|
||||
inventory_configured: Option<ProviderInventoryConfiguredResolver>,
|
||||
inventory_configured: Option<super::inventory::InventoryConfiguredResolver>,
|
||||
) where
|
||||
P: ProviderDef + 'static,
|
||||
F: Fn(ModelConfig) -> Result<P::Provider> + Send + Sync + 'static,
|
||||
|
||||
@@ -72,7 +72,7 @@ struct XaiAuthState {
|
||||
}
|
||||
|
||||
impl XaiAuthState {
|
||||
fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
oauth_mutex: TokioMutex::new(()),
|
||||
refresh_mutex: TokioMutex::new(()),
|
||||
@@ -97,7 +97,7 @@ struct TokenData {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TokenCache {
|
||||
pub(crate) struct TokenCache {
|
||||
cache_path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ fn get_cache_path() -> PathBuf {
|
||||
}
|
||||
|
||||
impl TokenCache {
|
||||
fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
let cache_path = get_cache_path();
|
||||
if let Some(parent) = cache_path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
@@ -118,6 +118,9 @@ impl TokenCache {
|
||||
let contents = std::fs::read_to_string(&self.cache_path).ok()?;
|
||||
serde_json::from_str(&contents).ok()
|
||||
}
|
||||
pub(crate) fn has_token(&self) -> bool {
|
||||
self.load().is_some()
|
||||
}
|
||||
|
||||
fn save(&self, token_data: &TokenData) -> Result<()> {
|
||||
if let Some(parent) = self.cache_path.parent() {
|
||||
@@ -805,10 +808,6 @@ impl ProviderDef for XaiOAuthProvider {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inventory_configured() -> bool {
|
||||
TokenCache::new().load().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter so the same `XaiOAuthAuthProvider` can be both owned by the
|
||||
|
||||
Reference in New Issue
Block a user