feat: move goose2 provider catalog behind ACP layer (#9030)
Signed-off-by: Kalvin Chau <kalvin@block.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Matt Toohey <contact@matttoohey.com>
This commit is contained in:
@@ -427,7 +427,7 @@ pub struct ProviderConfigChangeResponse {
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderCatalogEntryDto {
|
||||
pub struct ProviderTemplateCatalogEntryDto {
|
||||
pub provider_id: String,
|
||||
pub name: String,
|
||||
pub format: String,
|
||||
@@ -437,6 +437,73 @@ pub struct ProviderCatalogEntryDto {
|
||||
pub env_var: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProviderSetupCategoryDto {
|
||||
Agent,
|
||||
#[default]
|
||||
Model,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProviderSetupMethodDto {
|
||||
None,
|
||||
SingleApiKey,
|
||||
ConfigFields,
|
||||
HostWithOauthFallback,
|
||||
OauthBrowser,
|
||||
OauthDeviceCode,
|
||||
CloudCredentials,
|
||||
Local,
|
||||
CliAuth,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProviderSetupGroupDto {
|
||||
Default,
|
||||
Additional,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderSetupFieldDto {
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
pub secret: bool,
|
||||
pub required: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub placeholder: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default_value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderSetupCatalogEntryDto {
|
||||
pub provider_id: String,
|
||||
pub name: String,
|
||||
pub category: ProviderSetupCategoryDto,
|
||||
pub description: String,
|
||||
pub setup_method: ProviderSetupMethodDto,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub native_connect_query: Option<String>,
|
||||
#[serde(default)]
|
||||
pub fields: Vec<ProviderSetupFieldDto>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub binary_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc_url: Option<String>,
|
||||
pub group: ProviderSetupGroupDto,
|
||||
pub show_only_when_installed: bool,
|
||||
#[serde(default)]
|
||||
pub aliases: Vec<String>,
|
||||
pub supports_install: bool,
|
||||
pub supports_auth: bool,
|
||||
pub supports_auth_status: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderTemplateCapabilitiesDto {
|
||||
@@ -484,7 +551,22 @@ pub struct ProviderCatalogListRequest {
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderCatalogListResponse {
|
||||
pub providers: Vec<ProviderCatalogEntryDto>,
|
||||
pub providers: Vec<ProviderTemplateCatalogEntryDto>,
|
||||
}
|
||||
|
||||
/// List provider setup catalog entries
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/providers/setup/catalog/list",
|
||||
response = ProviderSetupCatalogListResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderSetupCatalogListRequest {}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProviderSetupCatalogListResponse {
|
||||
pub providers: Vec<ProviderSetupCatalogEntryDto>,
|
||||
}
|
||||
|
||||
/// Return the editable template for one catalog provider.
|
||||
@@ -949,6 +1031,8 @@ pub struct ProviderInventoryEntryDto {
|
||||
pub configured: bool,
|
||||
/// Provider classification such as `Preferred`, `Builtin`, `Declarative`, or `Custom`.
|
||||
pub provider_type: String,
|
||||
/// Whether this inventory entry represents an agent provider or a model provider.
|
||||
pub category: ProviderSetupCategoryDto,
|
||||
/// Required configuration keys and setup metadata.
|
||||
pub config_keys: Vec<ProviderConfigKey>,
|
||||
/// Step-by-step setup instructions, when present.
|
||||
|
||||
@@ -70,6 +70,11 @@
|
||||
"requestType": "ProviderCatalogListRequest",
|
||||
"responseType": "ProviderCatalogListResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/providers/setup/catalog/list",
|
||||
"requestType": "ProviderSetupCatalogListRequest",
|
||||
"responseType": "ProviderSetupCatalogListResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/providers/catalog/template",
|
||||
"requestType": "ProviderCatalogTemplateRequest",
|
||||
|
||||
@@ -351,6 +351,10 @@
|
||||
"type": "string",
|
||||
"description": "Provider classification such as `Preferred`, `Builtin`, `Declarative`, or `Custom`."
|
||||
},
|
||||
"category": {
|
||||
"$ref": "#/$defs/ProviderSetupCategoryDto",
|
||||
"description": "Whether this inventory entry represents an agent provider or a model provider."
|
||||
},
|
||||
"configKeys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -420,6 +424,7 @@
|
||||
"defaultModel",
|
||||
"configured",
|
||||
"providerType",
|
||||
"category",
|
||||
"configKeys",
|
||||
"setupSteps",
|
||||
"supportsRefresh",
|
||||
@@ -429,6 +434,13 @@
|
||||
],
|
||||
"description": "Provider inventory entry."
|
||||
},
|
||||
"ProviderSetupCategoryDto": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"agent",
|
||||
"model"
|
||||
]
|
||||
},
|
||||
"ProviderConfigKey": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -533,7 +545,7 @@
|
||||
"providers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/ProviderCatalogEntryDto"
|
||||
"$ref": "#/$defs/ProviderTemplateCatalogEntryDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -543,7 +555,7 @@
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/providers/catalog/list"
|
||||
},
|
||||
"ProviderCatalogEntryDto": {
|
||||
"ProviderTemplateCatalogEntryDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
@@ -579,6 +591,163 @@
|
||||
"envVar"
|
||||
]
|
||||
},
|
||||
"ProviderSetupCatalogListRequest": {
|
||||
"type": "object",
|
||||
"description": "List provider setup catalog entries",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/providers/setup/catalog/list"
|
||||
},
|
||||
"ProviderSetupCatalogListResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"providers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/ProviderSetupCatalogEntryDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providers"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/providers/setup/catalog/list"
|
||||
},
|
||||
"ProviderSetupCatalogEntryDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"category": {
|
||||
"$ref": "#/$defs/ProviderSetupCategoryDto"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"setupMethod": {
|
||||
"$ref": "#/$defs/ProviderSetupMethodDto"
|
||||
},
|
||||
"nativeConnectQuery": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/ProviderSetupFieldDto"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"binaryName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"docUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"group": {
|
||||
"$ref": "#/$defs/ProviderSetupGroupDto"
|
||||
},
|
||||
"showOnlyWhenInstalled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"aliases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"supportsInstall": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"supportsAuth": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"supportsAuthStatus": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId",
|
||||
"name",
|
||||
"category",
|
||||
"description",
|
||||
"setupMethod",
|
||||
"group",
|
||||
"showOnlyWhenInstalled",
|
||||
"supportsInstall",
|
||||
"supportsAuth",
|
||||
"supportsAuthStatus"
|
||||
]
|
||||
},
|
||||
"ProviderSetupMethodDto": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"single_api_key",
|
||||
"config_fields",
|
||||
"host_with_oauth_fallback",
|
||||
"oauth_browser",
|
||||
"oauth_device_code",
|
||||
"cloud_credentials",
|
||||
"local",
|
||||
"cli_auth"
|
||||
]
|
||||
},
|
||||
"ProviderSetupFieldDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"defaultValue": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
"label",
|
||||
"secret",
|
||||
"required"
|
||||
]
|
||||
},
|
||||
"ProviderSetupGroupDto": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"additional"
|
||||
]
|
||||
},
|
||||
"ProviderCatalogTemplateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2265,6 +2434,15 @@
|
||||
"description": "Params for _goose/providers/catalog/list",
|
||||
"title": "ProviderCatalogListRequest"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ProviderSetupCatalogListRequest"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/providers/setup/catalog/list",
|
||||
"title": "ProviderSetupCatalogListRequest"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
@@ -2683,6 +2861,14 @@
|
||||
],
|
||||
"title": "ProviderCatalogListResponse"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ProviderSetupCatalogListResponse"
|
||||
}
|
||||
],
|
||||
"title": "ProviderSetupCatalogListResponse"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -3969,6 +3969,7 @@ print(\"hello, world\")
|
||||
default_model: "unused".to_string(),
|
||||
configured: true,
|
||||
provider_type: crate::providers::base::ProviderType::Builtin,
|
||||
category: crate::providers::catalog::ProviderSetupCategory::Model,
|
||||
config_keys: vec![],
|
||||
setup_steps: vec![],
|
||||
supports_refresh: true,
|
||||
|
||||
@@ -120,6 +120,14 @@ impl GooseAcpAgent {
|
||||
self.on_list_provider_catalog(req).await
|
||||
}
|
||||
|
||||
#[custom_method(ProviderSetupCatalogListRequest)]
|
||||
async fn dispatch_list_provider_setup_catalog(
|
||||
&self,
|
||||
req: ProviderSetupCatalogListRequest,
|
||||
) -> Result<ProviderSetupCatalogListResponse, sacp::Error> {
|
||||
self.on_list_provider_setup_catalog(req).await
|
||||
}
|
||||
|
||||
#[custom_method(ProviderCatalogTemplateRequest)]
|
||||
async fn dispatch_get_provider_catalog_template(
|
||||
&self,
|
||||
|
||||
@@ -11,6 +11,7 @@ fn inventory_entry_to_dto(entry: ProviderInventoryEntry) -> ProviderInventoryEnt
|
||||
default_model: entry.default_model,
|
||||
configured: entry.configured,
|
||||
provider_type: format!("{:?}", entry.provider_type),
|
||||
category: provider_setup_category_to_dto(entry.category),
|
||||
config_keys: entry
|
||||
.config_keys
|
||||
.into_iter()
|
||||
@@ -114,8 +115,8 @@ fn provider_config_field_value(
|
||||
|
||||
fn provider_catalog_entry_to_dto(
|
||||
entry: crate::providers::catalog::ProviderCatalogEntry,
|
||||
) -> ProviderCatalogEntryDto {
|
||||
ProviderCatalogEntryDto {
|
||||
) -> ProviderTemplateCatalogEntryDto {
|
||||
ProviderTemplateCatalogEntryDto {
|
||||
provider_id: entry.id,
|
||||
name: entry.name,
|
||||
format: entry.format,
|
||||
@@ -126,6 +127,87 @@ fn provider_catalog_entry_to_dto(
|
||||
}
|
||||
}
|
||||
|
||||
fn provider_setup_category_to_dto(
|
||||
category: crate::providers::catalog::ProviderSetupCategory,
|
||||
) -> ProviderSetupCategoryDto {
|
||||
match category {
|
||||
crate::providers::catalog::ProviderSetupCategory::Agent => ProviderSetupCategoryDto::Agent,
|
||||
crate::providers::catalog::ProviderSetupCategory::Model => ProviderSetupCategoryDto::Model,
|
||||
}
|
||||
}
|
||||
|
||||
fn provider_setup_method_to_dto(
|
||||
method: crate::providers::catalog::ProviderSetupMethod,
|
||||
) -> ProviderSetupMethodDto {
|
||||
match method {
|
||||
crate::providers::catalog::ProviderSetupMethod::None => ProviderSetupMethodDto::None,
|
||||
crate::providers::catalog::ProviderSetupMethod::SingleApiKey => {
|
||||
ProviderSetupMethodDto::SingleApiKey
|
||||
}
|
||||
crate::providers::catalog::ProviderSetupMethod::ConfigFields => {
|
||||
ProviderSetupMethodDto::ConfigFields
|
||||
}
|
||||
crate::providers::catalog::ProviderSetupMethod::HostWithOauthFallback => {
|
||||
ProviderSetupMethodDto::HostWithOauthFallback
|
||||
}
|
||||
crate::providers::catalog::ProviderSetupMethod::OauthBrowser => {
|
||||
ProviderSetupMethodDto::OauthBrowser
|
||||
}
|
||||
crate::providers::catalog::ProviderSetupMethod::OauthDeviceCode => {
|
||||
ProviderSetupMethodDto::OauthDeviceCode
|
||||
}
|
||||
crate::providers::catalog::ProviderSetupMethod::CloudCredentials => {
|
||||
ProviderSetupMethodDto::CloudCredentials
|
||||
}
|
||||
crate::providers::catalog::ProviderSetupMethod::Local => ProviderSetupMethodDto::Local,
|
||||
crate::providers::catalog::ProviderSetupMethod::CliAuth => ProviderSetupMethodDto::CliAuth,
|
||||
}
|
||||
}
|
||||
|
||||
fn provider_setup_group_to_dto(
|
||||
group: crate::providers::catalog::ProviderSetupGroup,
|
||||
) -> ProviderSetupGroupDto {
|
||||
match group {
|
||||
crate::providers::catalog::ProviderSetupGroup::Default => ProviderSetupGroupDto::Default,
|
||||
crate::providers::catalog::ProviderSetupGroup::Additional => {
|
||||
ProviderSetupGroupDto::Additional
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn provider_setup_entry_to_dto(
|
||||
entry: crate::providers::catalog::ProviderSetupCatalogEntry,
|
||||
) -> ProviderSetupCatalogEntryDto {
|
||||
ProviderSetupCatalogEntryDto {
|
||||
provider_id: entry.provider_id,
|
||||
name: entry.display_name,
|
||||
category: provider_setup_category_to_dto(entry.category),
|
||||
description: entry.description,
|
||||
setup_method: provider_setup_method_to_dto(entry.setup_method),
|
||||
native_connect_query: entry.native_connect_query,
|
||||
fields: entry
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|field| ProviderSetupFieldDto {
|
||||
key: field.key,
|
||||
label: field.label,
|
||||
secret: field.secret,
|
||||
required: field.required,
|
||||
placeholder: field.placeholder,
|
||||
default_value: field.default_value,
|
||||
})
|
||||
.collect(),
|
||||
binary_name: entry.binary_name,
|
||||
doc_url: entry.docs_url,
|
||||
group: provider_setup_group_to_dto(entry.group),
|
||||
show_only_when_installed: entry.show_only_when_installed,
|
||||
aliases: entry.aliases,
|
||||
supports_install: entry.setup_capabilities.install,
|
||||
supports_auth: entry.setup_capabilities.auth,
|
||||
supports_auth_status: entry.setup_capabilities.auth_status,
|
||||
}
|
||||
}
|
||||
|
||||
fn provider_template_to_dto(
|
||||
template: crate::providers::catalog::ProviderTemplate,
|
||||
) -> ProviderTemplateDto {
|
||||
@@ -380,6 +462,18 @@ impl GooseAcpAgent {
|
||||
Ok(ProviderCatalogListResponse { providers })
|
||||
}
|
||||
|
||||
pub(super) async fn on_list_provider_setup_catalog(
|
||||
&self,
|
||||
_req: ProviderSetupCatalogListRequest,
|
||||
) -> Result<ProviderSetupCatalogListResponse, sacp::Error> {
|
||||
let providers = crate::providers::catalog::get_setup_catalog_entries()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(provider_setup_entry_to_dto)
|
||||
.collect();
|
||||
Ok(ProviderSetupCatalogListResponse { providers })
|
||||
}
|
||||
|
||||
pub(super) async fn on_get_provider_catalog_template(
|
||||
&self,
|
||||
req: ProviderCatalogTemplateRequest,
|
||||
|
||||
@@ -2,7 +2,10 @@ use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::canonical::CanonicalModelRegistry;
|
||||
use super::{
|
||||
base::{ConfigKey, ProviderMetadata},
|
||||
canonical::CanonicalModelRegistry,
|
||||
};
|
||||
|
||||
const PROVIDER_METADATA_JSON: &str = include_str!("canonical/data/provider_metadata.json");
|
||||
|
||||
@@ -110,6 +113,784 @@ pub struct ModelCapabilities {
|
||||
pub temperature: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProviderSetupCategory {
|
||||
Agent,
|
||||
Model,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProviderSetupMethod {
|
||||
None,
|
||||
SingleApiKey,
|
||||
ConfigFields,
|
||||
HostWithOauthFallback,
|
||||
OauthBrowser,
|
||||
OauthDeviceCode,
|
||||
CloudCredentials,
|
||||
Local,
|
||||
CliAuth,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProviderSetupGroup {
|
||||
Default,
|
||||
Additional,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProviderSetupField {
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
pub secret: bool,
|
||||
pub required: bool,
|
||||
pub placeholder: Option<String>,
|
||||
pub default_value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct ProviderSetupCapabilities {
|
||||
pub install: bool,
|
||||
pub auth: bool,
|
||||
pub auth_status: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProviderSetupCatalogEntry {
|
||||
pub provider_id: String,
|
||||
pub display_name: String,
|
||||
pub category: ProviderSetupCategory,
|
||||
pub description: String,
|
||||
pub setup_method: ProviderSetupMethod,
|
||||
pub docs_url: Option<String>,
|
||||
pub group: ProviderSetupGroup,
|
||||
pub fields: Vec<ProviderSetupField>,
|
||||
pub aliases: Vec<String>,
|
||||
pub native_connect_query: Option<String>,
|
||||
pub binary_name: Option<String>,
|
||||
pub setup_capabilities: ProviderSetupCapabilities,
|
||||
pub show_only_when_installed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct CuratedSetupMetadata {
|
||||
provider_id: &'static str,
|
||||
category: ProviderSetupCategory,
|
||||
setup_method: ProviderSetupMethod,
|
||||
group: ProviderSetupGroup,
|
||||
display_name: Option<&'static str>,
|
||||
description: Option<&'static str>,
|
||||
docs_url: Option<&'static str>,
|
||||
aliases: &'static [&'static str],
|
||||
native_connect_query: Option<&'static str>,
|
||||
binary_name: Option<&'static str>,
|
||||
setup_capabilities: ProviderSetupCapabilities,
|
||||
show_only_when_installed: bool,
|
||||
synthetic: bool,
|
||||
secret_field_default: Option<CuratedFieldMetadata>,
|
||||
field_overrides: &'static [CuratedFieldMetadata],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct CuratedFieldMetadata {
|
||||
key: &'static str,
|
||||
label: &'static str,
|
||||
placeholder: Option<&'static str>,
|
||||
default_value: Option<&'static str>,
|
||||
}
|
||||
|
||||
const fn setup_capabilities(
|
||||
install: bool,
|
||||
auth: bool,
|
||||
auth_status: bool,
|
||||
) -> ProviderSetupCapabilities {
|
||||
ProviderSetupCapabilities {
|
||||
install,
|
||||
auth,
|
||||
auth_status,
|
||||
}
|
||||
}
|
||||
|
||||
const API_KEY_FIELD: CuratedFieldMetadata = CuratedFieldMetadata {
|
||||
key: "",
|
||||
label: "API Key",
|
||||
placeholder: Some("Paste your API key"),
|
||||
default_value: None,
|
||||
};
|
||||
|
||||
const SETUP_METADATA: &[CuratedSetupMetadata] = &[
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "goose",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::None,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("Goose"),
|
||||
description: Some("Block's open-source coding agent"),
|
||||
docs_url: None,
|
||||
aliases: &["goose"],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: true,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "claude-acp",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::CliAuth,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("Claude Code"),
|
||||
description: Some("Anthropic's agentic coding tool"),
|
||||
docs_url: Some("https://docs.anthropic.com/en/docs/claude-code"),
|
||||
aliases: &["claude-acp", "claude_code", "claude"],
|
||||
native_connect_query: None,
|
||||
binary_name: Some("claude-agent-acp"),
|
||||
setup_capabilities: setup_capabilities(true, true, true),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "codex-acp",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::CliAuth,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("Codex"),
|
||||
description: Some("OpenAI's coding agent"),
|
||||
docs_url: Some("https://github.com/openai/codex"),
|
||||
aliases: &["codex-acp", "codex_cli", "codex"],
|
||||
native_connect_query: None,
|
||||
binary_name: Some("codex-acp"),
|
||||
setup_capabilities: setup_capabilities(true, true, true),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "copilot-acp",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::CliAuth,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("GitHub Copilot"),
|
||||
description: Some("GitHub's AI pair programmer"),
|
||||
docs_url: Some("https://docs.github.com/en/copilot/github-copilot-in-the-cli"),
|
||||
aliases: &["copilot-acp", "github_copilot", "github_copilot_cli"],
|
||||
native_connect_query: None,
|
||||
binary_name: Some("copilot"),
|
||||
setup_capabilities: setup_capabilities(true, true, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "amp-acp",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::CliAuth,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Sourcegraph's coding agent"),
|
||||
docs_url: Some("https://ampcode.com"),
|
||||
aliases: &["amp-acp", "amp"],
|
||||
native_connect_query: None,
|
||||
binary_name: Some("amp-acp"),
|
||||
setup_capabilities: setup_capabilities(true, true, true),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "cursor-agent",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::CliAuth,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Cursor's AI agent"),
|
||||
docs_url: Some("https://docs.cursor.com/en/cli/overview"),
|
||||
aliases: &["cursor-agent", "cursor_agent", "cursor"],
|
||||
native_connect_query: None,
|
||||
binary_name: Some("cursor-agent"),
|
||||
setup_capabilities: setup_capabilities(true, true, true),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "pi-acp",
|
||||
category: ProviderSetupCategory::Agent,
|
||||
setup_method: ProviderSetupMethod::CliAuth,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Open-source AI coding agent"),
|
||||
docs_url: Some("https://github.com/badlogic/pi-mono"),
|
||||
aliases: &["pi-acp", "pi"],
|
||||
native_connect_query: None,
|
||||
binary_name: Some("pi-acp"),
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: true,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "anthropic",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Claude models"),
|
||||
docs_url: Some("https://console.anthropic.com/settings/keys"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "google",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("Google Gemini"),
|
||||
description: Some("Gemini models"),
|
||||
docs_url: Some("https://aistudio.google.com/apikey"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "chatgpt_codex",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::OauthDeviceCode,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("ChatGPT"),
|
||||
description: Some("OpenAI via ChatGPT subscription"),
|
||||
docs_url: Some("https://chatgpt.com"),
|
||||
aliases: &[],
|
||||
native_connect_query: Some("ChatGPT Codex"),
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, true, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "openai",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::ConfigFields,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("GPT and o-series models"),
|
||||
docs_url: Some("https://platform.openai.com/api-keys"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "mistral",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: None,
|
||||
docs_url: Some("https://console.mistral.ai/api-keys"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "ollama",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::ConfigFields,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Run local or self-hosted models"),
|
||||
docs_url: Some("https://ollama.com"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[CuratedFieldMetadata {
|
||||
key: "OLLAMA_HOST",
|
||||
label: "Host",
|
||||
placeholder: Some("localhost or http://localhost:11434"),
|
||||
default_value: Some("http://localhost:11434"),
|
||||
}],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "openrouter",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Unified API for many models"),
|
||||
docs_url: Some("https://openrouter.ai/keys"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "databricks",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::HostWithOauthFallback,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: None,
|
||||
description: Some("Databricks Foundation Models"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, true, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[
|
||||
CuratedFieldMetadata {
|
||||
key: "DATABRICKS_HOST",
|
||||
label: "Host URL",
|
||||
placeholder: Some("https://dbc-...cloud.databricks.com"),
|
||||
default_value: None,
|
||||
},
|
||||
CuratedFieldMetadata {
|
||||
key: "DATABRICKS_TOKEN",
|
||||
label: "Access Token",
|
||||
placeholder: Some("Paste your access token"),
|
||||
default_value: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "github_copilot",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::OauthDeviceCode,
|
||||
group: ProviderSetupGroup::Default,
|
||||
display_name: Some("GitHub Copilot Models"),
|
||||
description: Some("Models via GitHub Copilot subscription"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: Some("GitHub Copilot"),
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, true, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "custom_deepseek",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: Some("DeepSeek chat and reasoning models"),
|
||||
docs_url: Some("https://platform.deepseek.com/api_keys"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "xai",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: Some("Grok models"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "groq",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: Some("Groq"),
|
||||
description: None,
|
||||
docs_url: Some("https://console.groq.com/keys"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "azure_openai",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::ConfigFields,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: None,
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[
|
||||
CuratedFieldMetadata {
|
||||
key: "AZURE_OPENAI_ENDPOINT",
|
||||
label: "Endpoint",
|
||||
placeholder: Some("https://your-resource.openai.azure.com"),
|
||||
default_value: None,
|
||||
},
|
||||
CuratedFieldMetadata {
|
||||
key: "AZURE_OPENAI_DEPLOYMENT_NAME",
|
||||
label: "Deployment",
|
||||
placeholder: Some("gpt-4o"),
|
||||
default_value: None,
|
||||
},
|
||||
CuratedFieldMetadata {
|
||||
key: "AZURE_OPENAI_API_KEY",
|
||||
label: "API Key",
|
||||
placeholder: Some("Paste your API key"),
|
||||
default_value: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "aws_bedrock",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::CloudCredentials,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: Some("AWS Bedrock"),
|
||||
description: Some("Models on AWS"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[CuratedFieldMetadata {
|
||||
key: "AWS_REGION",
|
||||
label: "AWS Region",
|
||||
placeholder: Some("us-west-2"),
|
||||
default_value: None,
|
||||
}],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "gcp_vertex_ai",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::CloudCredentials,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: Some("Models on Google Cloud"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[
|
||||
CuratedFieldMetadata {
|
||||
key: "GCP_PROJECT_ID",
|
||||
label: "Project ID",
|
||||
placeholder: Some("my-gcp-project"),
|
||||
default_value: None,
|
||||
},
|
||||
CuratedFieldMetadata {
|
||||
key: "GCP_LOCATION",
|
||||
label: "Location",
|
||||
placeholder: Some("us-central1"),
|
||||
default_value: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "litellm",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::ConfigFields,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: Some("LiteLLM proxy gateway"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[
|
||||
CuratedFieldMetadata {
|
||||
key: "LITELLM_HOST",
|
||||
label: "Host URL",
|
||||
placeholder: Some("https://your-proxy.example.com"),
|
||||
default_value: None,
|
||||
},
|
||||
CuratedFieldMetadata {
|
||||
key: "LITELLM_API_KEY",
|
||||
label: "API Key",
|
||||
placeholder: Some("Paste your API key"),
|
||||
default_value: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "lmstudio",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::ConfigFields,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: None,
|
||||
docs_url: Some("https://lmstudio.ai/docs/app/api"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[CuratedFieldMetadata {
|
||||
key: "LMSTUDIO_HOST",
|
||||
label: "Host URL",
|
||||
placeholder: Some("http://localhost:1234/v1/chat/completions"),
|
||||
default_value: None,
|
||||
}],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "nvidia",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: None,
|
||||
docs_url: Some("https://build.nvidia.com/models"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "cerebras",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::SingleApiKey,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: None,
|
||||
docs_url: Some("https://cloud.cerebras.ai/platform"),
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: Some(API_KEY_FIELD),
|
||||
field_overrides: &[],
|
||||
},
|
||||
CuratedSetupMetadata {
|
||||
provider_id: "snowflake",
|
||||
category: ProviderSetupCategory::Model,
|
||||
setup_method: ProviderSetupMethod::ConfigFields,
|
||||
group: ProviderSetupGroup::Additional,
|
||||
display_name: None,
|
||||
description: Some("Snowflake Cortex"),
|
||||
docs_url: None,
|
||||
aliases: &[],
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: setup_capabilities(false, false, false),
|
||||
show_only_when_installed: false,
|
||||
synthetic: false,
|
||||
secret_field_default: None,
|
||||
field_overrides: &[
|
||||
CuratedFieldMetadata {
|
||||
key: "SNOWFLAKE_HOST",
|
||||
label: "Host URL",
|
||||
placeholder: Some("https://your-account.snowflakecomputing.com"),
|
||||
default_value: None,
|
||||
},
|
||||
CuratedFieldMetadata {
|
||||
key: "SNOWFLAKE_TOKEN",
|
||||
label: "Access Token",
|
||||
placeholder: Some("Paste your access token"),
|
||||
default_value: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
fn field_label(key: &str) -> String {
|
||||
let label = key
|
||||
.strip_prefix("GOOSE_")
|
||||
.unwrap_or(key)
|
||||
.replace('_', " ")
|
||||
.to_lowercase();
|
||||
label
|
||||
.split_whitespace()
|
||||
.map(|word| {
|
||||
if matches!(
|
||||
word,
|
||||
"api" | "url" | "id" | "openai" | "aws" | "gcp" | "llm" | "oauth"
|
||||
) {
|
||||
word.to_uppercase()
|
||||
} else {
|
||||
let mut chars = word.chars();
|
||||
match chars.next() {
|
||||
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
|
||||
None => String::new(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
fn field_override<'a>(
|
||||
key: &str,
|
||||
config_key: &ConfigKey,
|
||||
curated: &'a CuratedSetupMetadata,
|
||||
) -> Option<&'a CuratedFieldMetadata> {
|
||||
if let Some(field) = curated
|
||||
.field_overrides
|
||||
.iter()
|
||||
.find(|field| field.key == key)
|
||||
{
|
||||
return Some(field);
|
||||
}
|
||||
|
||||
if config_key.secret {
|
||||
return curated.secret_field_default.as_ref();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn setup_field(config_key: &ConfigKey, curated: &CuratedSetupMetadata) -> ProviderSetupField {
|
||||
let field_override = field_override(&config_key.name, config_key, curated);
|
||||
ProviderSetupField {
|
||||
key: config_key.name.clone(),
|
||||
label: field_override
|
||||
.map(|field| field.label.to_string())
|
||||
.unwrap_or_else(|| field_label(&config_key.name)),
|
||||
secret: config_key.secret,
|
||||
required: config_key.required,
|
||||
placeholder: field_override.and_then(|field| field.placeholder.map(str::to_string)),
|
||||
default_value: field_override
|
||||
.and_then(|field| field.default_value.map(str::to_string))
|
||||
.or_else(|| config_key.default.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_entry_from_metadata(
|
||||
curated: &CuratedSetupMetadata,
|
||||
metadata: &ProviderMetadata,
|
||||
) -> ProviderSetupCatalogEntry {
|
||||
ProviderSetupCatalogEntry {
|
||||
provider_id: curated.provider_id.to_string(),
|
||||
display_name: curated
|
||||
.display_name
|
||||
.unwrap_or(metadata.display_name.as_str())
|
||||
.to_string(),
|
||||
category: curated.category,
|
||||
description: curated
|
||||
.description
|
||||
.unwrap_or(metadata.description.as_str())
|
||||
.to_string(),
|
||||
setup_method: curated.setup_method,
|
||||
docs_url: curated.docs_url.map(str::to_string).or_else(|| {
|
||||
(!metadata.model_doc_link.is_empty()).then(|| metadata.model_doc_link.clone())
|
||||
}),
|
||||
group: curated.group,
|
||||
fields: metadata
|
||||
.config_keys
|
||||
.iter()
|
||||
.filter(|key| key.primary)
|
||||
.map(|key| setup_field(key, curated))
|
||||
.collect(),
|
||||
aliases: curated
|
||||
.aliases
|
||||
.iter()
|
||||
.map(|alias| alias.to_string())
|
||||
.collect(),
|
||||
native_connect_query: curated.native_connect_query.map(str::to_string),
|
||||
binary_name: curated.binary_name.map(str::to_string),
|
||||
setup_capabilities: curated.setup_capabilities,
|
||||
show_only_when_installed: curated.show_only_when_installed,
|
||||
}
|
||||
}
|
||||
|
||||
fn synthetic_goose_setup_entry(curated: &CuratedSetupMetadata) -> ProviderSetupCatalogEntry {
|
||||
ProviderSetupCatalogEntry {
|
||||
provider_id: curated.provider_id.to_string(),
|
||||
display_name: curated.display_name.unwrap_or("Goose").to_string(),
|
||||
category: ProviderSetupCategory::Agent,
|
||||
description: curated.description.unwrap_or_default().to_string(),
|
||||
setup_method: ProviderSetupMethod::None,
|
||||
docs_url: curated.docs_url.map(str::to_string),
|
||||
group: curated.group,
|
||||
fields: Vec::new(),
|
||||
aliases: curated
|
||||
.aliases
|
||||
.iter()
|
||||
.map(|alias| alias.to_string())
|
||||
.collect(),
|
||||
native_connect_query: None,
|
||||
binary_name: None,
|
||||
setup_capabilities: curated.setup_capabilities,
|
||||
show_only_when_installed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_providers_by_format(format: ProviderFormat) -> Vec<ProviderCatalogEntry> {
|
||||
let native_provider_ids = super::init::providers()
|
||||
.await
|
||||
@@ -154,6 +935,34 @@ pub async fn get_providers_by_format(format: ProviderFormat) -> Vec<ProviderCata
|
||||
entries
|
||||
}
|
||||
|
||||
pub async fn get_setup_catalog_entries() -> Vec<ProviderSetupCatalogEntry> {
|
||||
let registry_metadata = super::providers()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|(metadata, _)| (metadata.name.clone(), metadata))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
SETUP_METADATA
|
||||
.iter()
|
||||
.filter_map(|curated| {
|
||||
if curated.synthetic {
|
||||
return Some(synthetic_goose_setup_entry(curated));
|
||||
}
|
||||
|
||||
registry_metadata
|
||||
.get(curated.provider_id)
|
||||
.map(|metadata| setup_entry_from_metadata(curated, metadata))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_provider_setup_category(provider_id: &str) -> Option<ProviderSetupCategory> {
|
||||
SETUP_METADATA
|
||||
.iter()
|
||||
.find(|curated| curated.provider_id == provider_id)
|
||||
.map(|curated| curated.category)
|
||||
}
|
||||
|
||||
pub fn get_provider_template(provider_id: &str) -> Option<ProviderTemplate> {
|
||||
let metadata = PROVIDER_METADATA.get(provider_id)?;
|
||||
|
||||
@@ -242,4 +1051,62 @@ mod tests {
|
||||
"z.ai template should have models"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn setup_catalog_includes_goose_and_curated_fields() {
|
||||
let entries = get_setup_catalog_entries().await;
|
||||
|
||||
let goose = entries
|
||||
.iter()
|
||||
.find(|entry| entry.provider_id == "goose")
|
||||
.expect("setup catalog should include synthetic goose");
|
||||
assert_eq!(goose.category, ProviderSetupCategory::Agent);
|
||||
assert_eq!(goose.setup_method, ProviderSetupMethod::None);
|
||||
assert!(goose.fields.is_empty());
|
||||
|
||||
let ollama = entries
|
||||
.iter()
|
||||
.find(|entry| entry.provider_id == "ollama")
|
||||
.expect("setup catalog should include ollama");
|
||||
assert_eq!(ollama.setup_method, ProviderSetupMethod::ConfigFields);
|
||||
assert_eq!(ollama.fields.len(), 1);
|
||||
assert_eq!(ollama.fields[0].key, "OLLAMA_HOST");
|
||||
assert_eq!(ollama.fields[0].label, "Host");
|
||||
assert_eq!(
|
||||
ollama.fields[0].default_value.as_deref(),
|
||||
Some("http://localhost:11434")
|
||||
);
|
||||
|
||||
let databricks = entries
|
||||
.iter()
|
||||
.find(|entry| entry.provider_id == "databricks")
|
||||
.expect("setup catalog should include databricks");
|
||||
assert_eq!(
|
||||
databricks.setup_method,
|
||||
ProviderSetupMethod::HostWithOauthFallback
|
||||
);
|
||||
assert_eq!(
|
||||
databricks
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.key.as_str())
|
||||
.collect::<Vec<_>>(),
|
||||
["DATABRICKS_HOST", "DATABRICKS_TOKEN"]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn setup_catalog_excludes_uncurated_deprecated_providers() {
|
||||
let provider_ids = get_setup_catalog_entries()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|entry| entry.provider_id)
|
||||
.collect::<std::collections::HashSet<_>>();
|
||||
|
||||
assert!(provider_ids.contains("claude-acp"));
|
||||
assert!(provider_ids.contains("codex-acp"));
|
||||
assert!(!provider_ids.contains("claude_code"));
|
||||
assert!(!provider_ids.contains("codex"));
|
||||
assert!(!provider_ids.contains("gemini_cli"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::base::{ConfigKey, ModelInfo, ProviderType};
|
||||
use super::canonical::{map_provider_name, map_to_canonical_model, CanonicalModelRegistry};
|
||||
use super::catalog::ProviderSetupCategory;
|
||||
use crate::config::declarative_providers::{DeclarativeProviderConfig, ProviderEngine};
|
||||
use crate::config::Config;
|
||||
use crate::session::session_manager::SessionStorage;
|
||||
@@ -23,6 +24,7 @@ pub struct ProviderInventoryEntry {
|
||||
pub default_model: String,
|
||||
pub configured: bool,
|
||||
pub provider_type: ProviderType,
|
||||
pub category: ProviderSetupCategory,
|
||||
pub config_keys: Vec<ConfigKey>,
|
||||
pub setup_steps: Vec<String>,
|
||||
pub supports_refresh: bool,
|
||||
@@ -248,6 +250,7 @@ struct ProviderDescriptor {
|
||||
identity: InventoryIdentity,
|
||||
configured: bool,
|
||||
provider_type: ProviderType,
|
||||
category: ProviderSetupCategory,
|
||||
config_keys: Vec<ConfigKey>,
|
||||
setup_steps: Vec<String>,
|
||||
supports_refresh: bool,
|
||||
@@ -289,6 +292,7 @@ impl ProviderInventoryService {
|
||||
default_model: descriptor.default_model,
|
||||
configured: descriptor.configured,
|
||||
provider_type: descriptor.provider_type,
|
||||
category: descriptor.category,
|
||||
config_keys: descriptor.config_keys,
|
||||
setup_steps: descriptor.setup_steps,
|
||||
supports_refresh: descriptor.supports_refresh,
|
||||
@@ -582,6 +586,8 @@ impl ProviderInventoryService {
|
||||
identity,
|
||||
configured: entry.inventory_configured(),
|
||||
provider_type: entry.provider_type(),
|
||||
category: crate::providers::catalog::get_provider_setup_category(&metadata.name)
|
||||
.unwrap_or(ProviderSetupCategory::Model),
|
||||
config_keys: metadata.config_keys.clone(),
|
||||
setup_steps: metadata.setup_steps.clone(),
|
||||
supports_refresh: entry.supports_inventory_refresh(),
|
||||
|
||||
@@ -72,6 +72,69 @@ fn acp_catalog_and_custom_provider_methods_use_core_provider_store() {
|
||||
"OpenAI-compatible catalog should include z.ai"
|
||||
);
|
||||
|
||||
let setup_catalog = send_custom(
|
||||
conn.cx(),
|
||||
"_goose/providers/setup/catalog/list",
|
||||
serde_json::json!({}),
|
||||
)
|
||||
.await
|
||||
.expect("provider setup catalog list should succeed");
|
||||
let setup_providers = setup_catalog
|
||||
.get("providers")
|
||||
.and_then(|providers| providers.as_array())
|
||||
.expect("setup catalog response should include providers");
|
||||
for provider_id in [
|
||||
"goose",
|
||||
"anthropic",
|
||||
"openai",
|
||||
"claude-acp",
|
||||
"codex-acp",
|
||||
"copilot-acp",
|
||||
"amp-acp",
|
||||
"cursor-agent",
|
||||
"pi-acp",
|
||||
] {
|
||||
assert!(
|
||||
setup_providers
|
||||
.iter()
|
||||
.any(|provider| provider.get("providerId")
|
||||
== Some(&serde_json::json!(provider_id))),
|
||||
"setup catalog should include {provider_id}"
|
||||
);
|
||||
}
|
||||
for provider_id in ["codex", "claude_code", "gemini_cli"] {
|
||||
assert!(
|
||||
setup_providers
|
||||
.iter()
|
||||
.all(|provider| provider.get("providerId")
|
||||
!= Some(&serde_json::json!(provider_id))),
|
||||
"setup catalog should exclude deprecated provider {provider_id}"
|
||||
);
|
||||
}
|
||||
let codex_setup = setup_providers
|
||||
.iter()
|
||||
.find(|provider| provider.get("providerId") == Some(&serde_json::json!("codex-acp")))
|
||||
.expect("setup catalog should include codex-acp");
|
||||
assert_eq!(
|
||||
codex_setup.get("category"),
|
||||
Some(&serde_json::json!("agent"))
|
||||
);
|
||||
assert_eq!(
|
||||
codex_setup.get("setupMethod"),
|
||||
Some(&serde_json::json!("cli_auth"))
|
||||
);
|
||||
assert_eq!(
|
||||
codex_setup.get("supportsInstall"),
|
||||
Some(&serde_json::json!(true))
|
||||
);
|
||||
assert!(
|
||||
codex_setup
|
||||
.get("aliases")
|
||||
.and_then(|aliases| aliases.as_array())
|
||||
.is_some_and(|aliases| aliases.contains(&serde_json::json!("codex"))),
|
||||
"codex-acp setup aliases should include codex"
|
||||
);
|
||||
|
||||
let template = send_custom(
|
||||
conn.cx(),
|
||||
"_goose/providers/catalog/template",
|
||||
|
||||
Reference in New Issue
Block a user