Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/goose-cli/src/commands/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2093,7 +2093,7 @@ fn add_provider() -> anyhow::Result<()> {
engine: provider_type.to_string(),
display_name: display_name.clone(),
api_url,
api_key,
api_key: requires_auth.then_some(api_key),
models,
supports_streaming: Some(supports_streaming),
headers,
Expand Down
201 changes: 201 additions & 0 deletions crates/goose-sdk/src/custom_requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,207 @@ pub struct ProviderConfigChangeResponse {
pub refresh: RefreshProviderInventoryResponse,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ProviderCatalogEntryDto {
pub provider_id: String,
pub name: String,
pub format: String,
pub api_url: String,
pub model_count: usize,
pub doc_url: String,
pub env_var: String,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ProviderTemplateCapabilitiesDto {
pub tool_call: bool,
pub reasoning: bool,
pub attachment: bool,
pub temperature: bool,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ProviderTemplateModelDto {
pub id: String,
pub name: String,
pub context_limit: usize,
pub capabilities: ProviderTemplateCapabilitiesDto,
pub deprecated: bool,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ProviderTemplateDto {
pub provider_id: String,
pub name: String,
pub format: String,
pub api_url: String,
pub models: Vec<ProviderTemplateModelDto>,
pub supports_streaming: bool,
pub env_var: String,
pub doc_url: String,
}

/// List custom-provider catalog entries. Omit `format` to list all formats.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/providers/catalog/list",
response = ProviderCatalogListResponse
)]
#[serde(rename_all = "camelCase")]
pub struct ProviderCatalogListRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct ProviderCatalogListResponse {
pub providers: Vec<ProviderCatalogEntryDto>,
}

/// Return the editable template for one catalog provider.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/providers/catalog/template",
response = ProviderCatalogTemplateResponse
)]
#[serde(rename_all = "camelCase")]
pub struct ProviderCatalogTemplateRequest {
pub provider_id: String,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct ProviderCatalogTemplateResponse {
pub template: ProviderTemplateDto,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderConfigDto {
pub provider_id: String,
pub engine: String,
pub display_name: String,
pub api_url: String,
#[serde(default)]
pub models: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supports_streaming: Option<bool>,
#[serde(default)]
pub headers: HashMap<String, String>,
pub requires_auth: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub catalog_provider_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api_key_env: Option<String>,
pub api_key_set: bool,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderUpsertDto {
pub engine: String,
pub display_name: String,
pub api_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api_key: Option<String>,
#[serde(default)]
pub models: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supports_streaming: Option<bool>,
#[serde(default)]
pub headers: HashMap<String, String>,
pub requires_auth: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub catalog_provider_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_path: Option<String>,
}

/// Create a custom provider backed by Goose's declarative provider store.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/providers/custom/create",
response = CustomProviderCreateResponse
)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderCreateRequest {
#[serde(flatten)]
pub provider: CustomProviderUpsertDto,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderCreateResponse {
pub provider_id: String,
pub status: ProviderConfigStatusDto,
pub refresh: RefreshProviderInventoryResponse,
}

/// Read a declarative provider config. Custom configs are editable; bundled configs are read-only.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/providers/custom/read",
response = CustomProviderReadResponse
)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderReadRequest {
pub provider_id: String,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderReadResponse {
pub provider: CustomProviderConfigDto,
pub editable: bool,
pub status: ProviderConfigStatusDto,
}

/// Update a custom provider backed by Goose's declarative provider store.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/providers/custom/update",
response = CustomProviderUpdateResponse
)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderUpdateRequest {
pub provider_id: String,
#[serde(flatten)]
pub provider: CustomProviderUpsertDto,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderUpdateResponse {
pub provider_id: String,
pub status: ProviderConfigStatusDto,
pub refresh: RefreshProviderInventoryResponse,
}

/// Delete a custom provider from Goose's declarative provider store.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/providers/custom/delete",
response = CustomProviderDeleteResponse
)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderDeleteRequest {
pub provider_id: String,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct CustomProviderDeleteResponse {
pub provider_id: String,
pub refresh: RefreshProviderInventoryResponse,
}

/// The type of source entity.
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
Expand Down
9 changes: 7 additions & 2 deletions crates/goose-server/src/routes/config_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ fn default_requires_auth() -> bool {
true
}

fn normalize_custom_provider_api_key(api_key: String) -> Option<String> {
let api_key = api_key.trim().to_string();
(!api_key.is_empty()).then_some(api_key)
}

#[derive(Deserialize, ToSchema)]
pub struct CheckProviderRequest {
pub provider: String,
Expand Down Expand Up @@ -583,7 +588,7 @@ pub async fn create_custom_provider(
engine: request.engine,
display_name: request.display_name,
api_url: request.api_url,
api_key: request.api_key,
api_key: normalize_custom_provider_api_key(request.api_key),
models: request.models,
supports_streaming: request.supports_streaming,
headers: request.headers,
Expand Down Expand Up @@ -675,7 +680,7 @@ pub async fn update_custom_provider(
engine: request.engine,
display_name: request.display_name,
api_url: request.api_url,
api_key: request.api_key,
api_key: normalize_custom_provider_api_key(request.api_key),
models: request.models,
supports_streaming: request.supports_streaming,
headers: request.headers,
Expand Down
30 changes: 30 additions & 0 deletions crates/goose/acp-meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,36 @@
"requestType": "ListProvidersRequest",
"responseType": "ListProvidersResponse"
},
{
"method": "_goose/providers/catalog/list",
"requestType": "ProviderCatalogListRequest",
"responseType": "ProviderCatalogListResponse"
},
{
"method": "_goose/providers/catalog/template",
"requestType": "ProviderCatalogTemplateRequest",
"responseType": "ProviderCatalogTemplateResponse"
},
{
"method": "_goose/providers/custom/create",
"requestType": "CustomProviderCreateRequest",
"responseType": "CustomProviderCreateResponse"
},
{
"method": "_goose/providers/custom/read",
"requestType": "CustomProviderReadRequest",
"responseType": "CustomProviderReadResponse"
},
{
"method": "_goose/providers/custom/update",
"requestType": "CustomProviderUpdateRequest",
"responseType": "CustomProviderUpdateResponse"
},
{
"method": "_goose/providers/custom/delete",
"requestType": "CustomProviderDeleteRequest",
"responseType": "CustomProviderDeleteResponse"
},
{
"method": "_goose/providers/inventory/refresh",
"requestType": "RefreshProviderInventoryRequest",
Expand Down
Loading
Loading