consolidate provider ACP methods onto inventory (#8710)
Signed-off-by: Bradley Axen <baxen@squareup.com>
This commit is contained in:
@@ -45,16 +45,6 @@
|
|||||||
"requestType": "ListProvidersRequest",
|
"requestType": "ListProvidersRequest",
|
||||||
"responseType": "ListProvidersResponse"
|
"responseType": "ListProvidersResponse"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"method": "_goose/providers/details",
|
|
||||||
"requestType": "GetProviderDetailsRequest",
|
|
||||||
"responseType": "GetProviderDetailsResponse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "_goose/providers/inventory",
|
|
||||||
"requestType": "GetProviderInventoryRequest",
|
|
||||||
"responseType": "GetProviderInventoryResponse"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"method": "_goose/providers/inventory/refresh",
|
"method": "_goose/providers/inventory/refresh",
|
||||||
"requestType": "RefreshProviderInventoryRequest",
|
"requestType": "RefreshProviderInventoryRequest",
|
||||||
|
|||||||
@@ -196,173 +196,6 @@
|
|||||||
"x-method": "_goose/session/extensions"
|
"x-method": "_goose/session/extensions"
|
||||||
},
|
},
|
||||||
"ListProvidersRequest": {
|
"ListProvidersRequest": {
|
||||||
"type": "object",
|
|
||||||
"description": "List providers available through goose, including the config-default sentinel.",
|
|
||||||
"x-side": "agent",
|
|
||||||
"x-method": "_goose/providers/list"
|
|
||||||
},
|
|
||||||
"ListProvidersResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"providers": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/$defs/ProviderListEntry"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"providers"
|
|
||||||
],
|
|
||||||
"description": "Provider list response.",
|
|
||||||
"x-side": "agent",
|
|
||||||
"x-method": "_goose/providers/list"
|
|
||||||
},
|
|
||||||
"ProviderListEntry": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"id",
|
|
||||||
"label"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"GetProviderDetailsRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "List providers with full metadata (config keys, setup steps, etc.).",
|
|
||||||
"x-side": "agent",
|
|
||||||
"x-method": "_goose/providers/details"
|
|
||||||
},
|
|
||||||
"GetProviderDetailsResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"providers": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/$defs/ProviderDetailEntry"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"providers"
|
|
||||||
],
|
|
||||||
"description": "Provider details response.",
|
|
||||||
"x-side": "agent",
|
|
||||||
"x-method": "_goose/providers/details"
|
|
||||||
},
|
|
||||||
"ProviderDetailEntry": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"displayName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"defaultModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"isConfigured": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"providerType": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"configKeys": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/$defs/ProviderConfigKey"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"setupSteps": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"default": []
|
|
||||||
},
|
|
||||||
"knownModels": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/$defs/ModelEntry"
|
|
||||||
},
|
|
||||||
"default": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"name",
|
|
||||||
"displayName",
|
|
||||||
"description",
|
|
||||||
"defaultModel",
|
|
||||||
"isConfigured",
|
|
||||||
"providerType",
|
|
||||||
"configKeys"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ProviderConfigKey": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"required": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"secret": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"type": [
|
|
||||||
"string",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"default": null
|
|
||||||
},
|
|
||||||
"oauthFlow": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"deviceCodeFlow": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"primary": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"name",
|
|
||||||
"required",
|
|
||||||
"secret"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ModelEntry": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"contextLimit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"name",
|
|
||||||
"contextLimit"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"GetProviderInventoryRequest": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"providerIds": {
|
"providerIds": {
|
||||||
@@ -374,11 +207,11 @@
|
|||||||
"default": []
|
"default": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": "Read per-provider inventory. Always returns immediately from stored state.",
|
"description": "List providers with setup metadata and the current model inventory snapshot.",
|
||||||
"x-side": "agent",
|
"x-side": "agent",
|
||||||
"x-method": "_goose/providers/inventory"
|
"x-method": "_goose/providers/list"
|
||||||
},
|
},
|
||||||
"GetProviderInventoryResponse": {
|
"ListProvidersResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"entries": {
|
"entries": {
|
||||||
@@ -391,9 +224,9 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"entries"
|
"entries"
|
||||||
],
|
],
|
||||||
"description": "Provider inventory response.",
|
"description": "Provider list response.",
|
||||||
"x-side": "agent",
|
"x-side": "agent",
|
||||||
"x-method": "_goose/providers/inventory"
|
"x-method": "_goose/providers/list"
|
||||||
},
|
},
|
||||||
"ProviderInventoryEntryDto": {
|
"ProviderInventoryEntryDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -406,10 +239,36 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Human-readable provider name."
|
"description": "Human-readable provider name."
|
||||||
},
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Description of the provider's capabilities."
|
||||||
|
},
|
||||||
|
"defaultModel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The default/recommended model for this provider."
|
||||||
|
},
|
||||||
"configured": {
|
"configured": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether Goose has enough configuration to use this provider."
|
"description": "Whether Goose has enough configuration to use this provider."
|
||||||
},
|
},
|
||||||
|
"providerType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Provider classification such as `Preferred`, `Builtin`, `Declarative`, or `Custom`."
|
||||||
|
},
|
||||||
|
"configKeys": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ProviderConfigKey"
|
||||||
|
},
|
||||||
|
"description": "Required configuration keys and setup metadata."
|
||||||
|
},
|
||||||
|
"setupSteps": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "Step-by-step setup instructions, when present."
|
||||||
|
},
|
||||||
"supportsRefresh": {
|
"supportsRefresh": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether this provider supports background inventory refresh."
|
"description": "Whether this provider supports background inventory refresh."
|
||||||
@@ -461,7 +320,12 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"providerId",
|
"providerId",
|
||||||
"providerName",
|
"providerName",
|
||||||
|
"description",
|
||||||
|
"defaultModel",
|
||||||
"configured",
|
"configured",
|
||||||
|
"providerType",
|
||||||
|
"configKeys",
|
||||||
|
"setupSteps",
|
||||||
"supportsRefresh",
|
"supportsRefresh",
|
||||||
"refreshing",
|
"refreshing",
|
||||||
"models",
|
"models",
|
||||||
@@ -469,6 +333,44 @@
|
|||||||
],
|
],
|
||||||
"description": "Provider inventory entry."
|
"description": "Provider inventory entry."
|
||||||
},
|
},
|
||||||
|
"ProviderConfigKey": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"secret": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
|
"oauthFlow": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"deviceCodeFlow": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"primary": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"required",
|
||||||
|
"secret"
|
||||||
|
]
|
||||||
|
},
|
||||||
"ProviderInventoryModelDto": {
|
"ProviderInventoryModelDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -1504,24 +1406,6 @@
|
|||||||
"description": "Params for _goose/providers/list",
|
"description": "Params for _goose/providers/list",
|
||||||
"title": "ListProvidersRequest"
|
"title": "ListProvidersRequest"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/GetProviderDetailsRequest"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Params for _goose/providers/details",
|
|
||||||
"title": "GetProviderDetailsRequest"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/GetProviderInventoryRequest"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Params for _goose/providers/inventory",
|
|
||||||
"title": "GetProviderInventoryRequest"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
@@ -1825,22 +1709,6 @@
|
|||||||
],
|
],
|
||||||
"title": "ListProvidersResponse"
|
"title": "ListProvidersResponse"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/GetProviderDetailsResponse"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "GetProviderDetailsResponse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/GetProviderInventoryResponse"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "GetProviderInventoryResponse"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -426,7 +426,16 @@ fn inventory_entry_to_dto(entry: ProviderInventoryEntry) -> ProviderInventoryEnt
|
|||||||
ProviderInventoryEntryDto {
|
ProviderInventoryEntryDto {
|
||||||
provider_id: entry.provider_id,
|
provider_id: entry.provider_id,
|
||||||
provider_name: entry.provider_name,
|
provider_name: entry.provider_name,
|
||||||
|
description: entry.description,
|
||||||
|
default_model: entry.default_model,
|
||||||
configured: entry.configured,
|
configured: entry.configured,
|
||||||
|
provider_type: format!("{:?}", entry.provider_type),
|
||||||
|
config_keys: entry
|
||||||
|
.config_keys
|
||||||
|
.into_iter()
|
||||||
|
.map(provider_config_key_to_dto)
|
||||||
|
.collect(),
|
||||||
|
setup_steps: entry.setup_steps,
|
||||||
supports_refresh: entry.supports_refresh,
|
supports_refresh: entry.supports_refresh,
|
||||||
refreshing: entry.refreshing,
|
refreshing: entry.refreshing,
|
||||||
models: entry
|
models: entry
|
||||||
@@ -449,6 +458,18 @@ fn inventory_entry_to_dto(entry: ProviderInventoryEntry) -> ProviderInventoryEnt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn provider_config_key_to_dto(key: goose::providers::base::ConfigKey) -> ProviderConfigKey {
|
||||||
|
ProviderConfigKey {
|
||||||
|
name: key.name,
|
||||||
|
required: key.required,
|
||||||
|
secret: key.secret,
|
||||||
|
default: key.default,
|
||||||
|
oauth_flow: key.oauth_flow,
|
||||||
|
device_code_flow: key.device_code_flow,
|
||||||
|
primary: key.primary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_model_state(current_model: &str, inventory: &ProviderInventoryEntry) -> SessionModelState {
|
fn build_model_state(current_model: &str, inventory: &ProviderInventoryEntry) -> SessionModelState {
|
||||||
let mut available_models = inventory
|
let mut available_models = inventory
|
||||||
.models
|
.models
|
||||||
@@ -467,11 +488,16 @@ fn build_model_state(current_model: &str, inventory: &ProviderInventoryEntry) ->
|
|||||||
SessionModelState::new(ModelId::new(current_model), available_models)
|
SessionModelState::new(ModelId::new(current_model), available_models)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_provider_entries(current_provider: Option<&str>) -> Vec<ProviderListEntry> {
|
struct ProviderOptionEntry {
|
||||||
|
id: String,
|
||||||
|
label: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_provider_entries(current_provider: Option<&str>) -> Vec<ProviderOptionEntry> {
|
||||||
let mut providers = goose::providers::providers()
|
let mut providers = goose::providers::providers()
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(metadata, _)| ProviderListEntry {
|
.map(|(metadata, _)| ProviderOptionEntry {
|
||||||
id: metadata.name,
|
id: metadata.name,
|
||||||
label: metadata.display_name,
|
label: metadata.display_name,
|
||||||
})
|
})
|
||||||
@@ -485,7 +511,7 @@ async fn list_provider_entries(current_provider: Option<&str>) -> Vec<ProviderLi
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|provider| provider.id == current_provider)
|
.any(|provider| provider.id == current_provider)
|
||||||
{
|
{
|
||||||
providers.push(ProviderListEntry {
|
providers.push(ProviderOptionEntry {
|
||||||
id: current_provider.to_string(),
|
id: current_provider.to_string(),
|
||||||
label: current_provider.to_string(),
|
label: current_provider.to_string(),
|
||||||
});
|
});
|
||||||
@@ -494,7 +520,7 @@ async fn list_provider_entries(current_provider: Option<&str>) -> Vec<ProviderLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut entries = Vec::with_capacity(providers.len() + 1);
|
let mut entries = Vec::with_capacity(providers.len() + 1);
|
||||||
entries.push(ProviderListEntry {
|
entries.push(ProviderOptionEntry {
|
||||||
id: DEFAULT_PROVIDER_ID.to_string(),
|
id: DEFAULT_PROVIDER_ID.to_string(),
|
||||||
label: DEFAULT_PROVIDER_LABEL.to_string(),
|
label: DEFAULT_PROVIDER_LABEL.to_string(),
|
||||||
});
|
});
|
||||||
@@ -2783,84 +2809,14 @@ impl GooseAcpAgent {
|
|||||||
#[custom_method(ListProvidersRequest)]
|
#[custom_method(ListProvidersRequest)]
|
||||||
async fn on_list_providers(
|
async fn on_list_providers(
|
||||||
&self,
|
&self,
|
||||||
_req: ListProvidersRequest,
|
req: ListProvidersRequest,
|
||||||
) -> Result<ListProvidersResponse, sacp::Error> {
|
) -> Result<ListProvidersResponse, sacp::Error> {
|
||||||
Ok(ListProvidersResponse {
|
|
||||||
providers: list_provider_entries(None).await,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[custom_method(GetProviderDetailsRequest)]
|
|
||||||
async fn on_get_provider_details(
|
|
||||||
&self,
|
|
||||||
_req: GetProviderDetailsRequest,
|
|
||||||
) -> Result<GetProviderDetailsResponse, sacp::Error> {
|
|
||||||
let config = self.load_config().ok();
|
|
||||||
let all = goose::providers::providers().await;
|
|
||||||
let entries = all
|
|
||||||
.into_iter()
|
|
||||||
.map(|(metadata, provider_type)| {
|
|
||||||
let is_configured = config
|
|
||||||
.as_ref()
|
|
||||||
.map(|c| {
|
|
||||||
metadata.config_keys.iter().all(|k| {
|
|
||||||
if !k.required {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if k.secret {
|
|
||||||
c.get_secret::<String>(&k.name).is_ok()
|
|
||||||
} else {
|
|
||||||
c.get_param::<String>(&k.name).is_ok()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(false);
|
|
||||||
ProviderDetailEntry {
|
|
||||||
name: metadata.name.clone(),
|
|
||||||
display_name: metadata.display_name.clone(),
|
|
||||||
description: metadata.description.clone(),
|
|
||||||
default_model: metadata.default_model.clone(),
|
|
||||||
is_configured,
|
|
||||||
provider_type: format!("{:?}", provider_type),
|
|
||||||
config_keys: metadata
|
|
||||||
.config_keys
|
|
||||||
.iter()
|
|
||||||
.map(|k| ProviderConfigKey {
|
|
||||||
name: k.name.clone(),
|
|
||||||
required: k.required,
|
|
||||||
secret: k.secret,
|
|
||||||
default: k.default.clone(),
|
|
||||||
oauth_flow: k.oauth_flow,
|
|
||||||
device_code_flow: k.device_code_flow,
|
|
||||||
primary: k.primary,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
setup_steps: metadata.setup_steps.clone(),
|
|
||||||
known_models: metadata
|
|
||||||
.known_models
|
|
||||||
.iter()
|
|
||||||
.map(|m| ModelEntry {
|
|
||||||
name: m.name.clone(),
|
|
||||||
context_limit: m.context_limit,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(GetProviderDetailsResponse { providers: entries })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[custom_method(GetProviderInventoryRequest)]
|
|
||||||
async fn on_get_provider_inventory(
|
|
||||||
&self,
|
|
||||||
req: GetProviderInventoryRequest,
|
|
||||||
) -> Result<GetProviderInventoryResponse, sacp::Error> {
|
|
||||||
let entries = self
|
let entries = self
|
||||||
.provider_inventory
|
.provider_inventory
|
||||||
.entries(&req.provider_ids)
|
.entries(&req.provider_ids)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| sacp::Error::internal_error().data(e.to_string()))?;
|
.map_err(|e| sacp::Error::internal_error().data(e.to_string()))?;
|
||||||
Ok(GetProviderInventoryResponse {
|
Ok(ListProvidersResponse {
|
||||||
entries: entries.into_iter().map(inventory_entry_to_dto).collect(),
|
entries: entries.into_iter().map(inventory_entry_to_dto).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -4150,7 +4106,12 @@ print(\"hello, world\")
|
|||||||
let inventory = ProviderInventoryEntry {
|
let inventory = ProviderInventoryEntry {
|
||||||
provider_id: "mock".to_string(),
|
provider_id: "mock".to_string(),
|
||||||
provider_name: "Mock".to_string(),
|
provider_name: "Mock".to_string(),
|
||||||
|
description: "Mock".to_string(),
|
||||||
|
default_model: "unused".to_string(),
|
||||||
configured: true,
|
configured: true,
|
||||||
|
provider_type: goose::providers::base::ProviderType::Builtin,
|
||||||
|
config_keys: vec![],
|
||||||
|
setup_steps: vec![],
|
||||||
supports_refresh: true,
|
supports_refresh: true,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
models: models
|
models: models
|
||||||
|
|||||||
@@ -112,32 +112,29 @@ fn test_custom_get_extensions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_custom_list_providers() {
|
fn test_custom_provider_inventory_includes_metadata() {
|
||||||
run_test(async {
|
run_test(async {
|
||||||
let openai = OpenAiFixture::new(vec![], Arc::new(EnforceSessionId::default())).await;
|
let openai = OpenAiFixture::new(vec![], Arc::new(EnforceSessionId::default())).await;
|
||||||
let conn = AcpServerConnection::new(TestConnectionConfig::default(), openai).await;
|
let conn = AcpServerConnection::new(TestConnectionConfig::default(), openai).await;
|
||||||
|
|
||||||
let response = send_custom(conn.cx(), "_goose/providers/list", serde_json::json!({}))
|
let response = send_custom(conn.cx(), "_goose/providers/list", serde_json::json!({}))
|
||||||
.await
|
.await
|
||||||
.expect("provider list should succeed");
|
.expect("provider inventory should succeed");
|
||||||
let providers = response
|
let providers = response
|
||||||
.get("providers")
|
.get("entries")
|
||||||
.and_then(|value| value.as_array())
|
.and_then(|value| value.as_array())
|
||||||
.expect("missing providers array");
|
.expect("missing entries array");
|
||||||
|
let openai = providers
|
||||||
|
.iter()
|
||||||
|
.find(|provider| provider.get("providerId") == Some(&serde_json::json!("openai")))
|
||||||
|
.expect("expected openai inventory entry");
|
||||||
|
|
||||||
assert!(
|
assert!(openai.get("providerName").is_some(), "missing providerName");
|
||||||
providers.iter().any(|provider| {
|
assert!(openai.get("description").is_some(), "missing description");
|
||||||
provider.get("id") == Some(&serde_json::json!("goose"))
|
assert!(openai.get("defaultModel").is_some(), "missing defaultModel");
|
||||||
&& provider.get("label") == Some(&serde_json::json!("Goose (Default)"))
|
assert!(openai.get("providerType").is_some(), "missing providerType");
|
||||||
}),
|
assert!(openai.get("configKeys").is_some(), "missing configKeys");
|
||||||
"expected Goose default provider sentinel"
|
assert!(openai.get("setupSteps").is_some(), "missing setupSteps");
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
providers
|
|
||||||
.iter()
|
|
||||||
.any(|provider| provider.get("id") == Some(&serde_json::json!("openai"))),
|
|
||||||
"expected at least one concrete provider from the goose registry"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,24 +181,6 @@ pub struct RemoveSecretRequest {
|
|||||||
pub key: String,
|
pub key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List providers available through goose, including the config-default sentinel.
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
|
||||||
#[request(method = "_goose/providers/list", response = ListProvidersResponse)]
|
|
||||||
pub struct ListProvidersRequest {}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ProviderListEntry {
|
|
||||||
pub id: String,
|
|
||||||
pub label: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provider list response.
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
|
||||||
pub struct ListProvidersResponse {
|
|
||||||
pub providers: Vec<ProviderListEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Archive a session (soft delete).
|
/// Archive a session (soft delete).
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||||
#[request(method = "_goose/session/archive", response = EmptyResponse)]
|
#[request(method = "_goose/session/archive", response = EmptyResponse)]
|
||||||
@@ -246,40 +228,6 @@ pub struct ImportSessionResponse {
|
|||||||
pub message_count: u64,
|
pub message_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List providers with full metadata (config keys, setup steps, etc.).
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
|
||||||
#[request(method = "_goose/providers/details", response = GetProviderDetailsResponse)]
|
|
||||||
pub struct GetProviderDetailsRequest {}
|
|
||||||
|
|
||||||
/// Provider details response.
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
|
||||||
pub struct GetProviderDetailsResponse {
|
|
||||||
pub providers: Vec<ProviderDetailEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ProviderDetailEntry {
|
|
||||||
pub name: String,
|
|
||||||
pub display_name: String,
|
|
||||||
pub description: String,
|
|
||||||
pub default_model: String,
|
|
||||||
pub is_configured: bool,
|
|
||||||
pub provider_type: String,
|
|
||||||
pub config_keys: Vec<ProviderConfigKey>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub setup_steps: Vec<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub known_models: Vec<ModelEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ModelEntry {
|
|
||||||
pub name: String,
|
|
||||||
pub context_limit: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ProviderConfigKey {
|
pub struct ProviderConfigKey {
|
||||||
@@ -494,22 +442,19 @@ pub struct DictationConfigResponse {
|
|||||||
pub providers: HashMap<String, DictationProviderStatusEntry>,
|
pub providers: HashMap<String, DictationProviderStatusEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read per-provider inventory. Always returns immediately from stored state.
|
/// List providers with setup metadata and the current model inventory snapshot.
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||||
#[request(
|
#[request(method = "_goose/providers/list", response = ListProvidersResponse)]
|
||||||
method = "_goose/providers/inventory",
|
|
||||||
response = GetProviderInventoryResponse
|
|
||||||
)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetProviderInventoryRequest {
|
pub struct ListProvidersRequest {
|
||||||
/// Only return entries for these providers. Empty means all.
|
/// Only return entries for these providers. Empty means all.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub provider_ids: Vec<String>,
|
pub provider_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provider inventory response.
|
/// Provider list response.
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||||
pub struct GetProviderInventoryResponse {
|
pub struct ListProvidersResponse {
|
||||||
pub entries: Vec<ProviderInventoryEntryDto>,
|
pub entries: Vec<ProviderInventoryEntryDto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,8 +529,18 @@ pub struct ProviderInventoryEntryDto {
|
|||||||
pub provider_id: String,
|
pub provider_id: String,
|
||||||
/// Human-readable provider name.
|
/// Human-readable provider name.
|
||||||
pub provider_name: String,
|
pub provider_name: String,
|
||||||
|
/// Description of the provider's capabilities.
|
||||||
|
pub description: String,
|
||||||
|
/// The default/recommended model for this provider.
|
||||||
|
pub default_model: String,
|
||||||
/// Whether Goose has enough configuration to use this provider.
|
/// Whether Goose has enough configuration to use this provider.
|
||||||
pub configured: bool,
|
pub configured: bool,
|
||||||
|
/// Provider classification such as `Preferred`, `Builtin`, `Declarative`, or `Custom`.
|
||||||
|
pub provider_type: String,
|
||||||
|
/// Required configuration keys and setup metadata.
|
||||||
|
pub config_keys: Vec<ProviderConfigKey>,
|
||||||
|
/// Step-by-step setup instructions, when present.
|
||||||
|
pub setup_steps: Vec<String>,
|
||||||
/// Whether this provider supports background inventory refresh.
|
/// Whether this provider supports background inventory refresh.
|
||||||
pub supports_refresh: bool,
|
pub supports_refresh: bool,
|
||||||
/// Whether a refresh is currently in flight.
|
/// Whether a refresh is currently in flight.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::base::{ConfigKey, ModelInfo};
|
use super::base::{ConfigKey, ModelInfo, ProviderType};
|
||||||
use super::canonical::{map_provider_name, map_to_canonical_model, CanonicalModelRegistry};
|
use super::canonical::{map_provider_name, map_to_canonical_model, CanonicalModelRegistry};
|
||||||
use crate::config::declarative_providers::{DeclarativeProviderConfig, ProviderEngine};
|
use crate::config::declarative_providers::{DeclarativeProviderConfig, ProviderEngine};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
@@ -19,7 +19,12 @@ const STALE_AFTER_HOURS: i64 = 24;
|
|||||||
pub struct ProviderInventoryEntry {
|
pub struct ProviderInventoryEntry {
|
||||||
pub provider_id: String,
|
pub provider_id: String,
|
||||||
pub provider_name: String,
|
pub provider_name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub default_model: String,
|
||||||
pub configured: bool,
|
pub configured: bool,
|
||||||
|
pub provider_type: ProviderType,
|
||||||
|
pub config_keys: Vec<ConfigKey>,
|
||||||
|
pub setup_steps: Vec<String>,
|
||||||
pub supports_refresh: bool,
|
pub supports_refresh: bool,
|
||||||
pub refreshing: bool,
|
pub refreshing: bool,
|
||||||
pub models: Vec<InventoryModel>,
|
pub models: Vec<InventoryModel>,
|
||||||
@@ -162,8 +167,13 @@ struct InventorySnapshot {
|
|||||||
struct ProviderDescriptor {
|
struct ProviderDescriptor {
|
||||||
provider_id: String,
|
provider_id: String,
|
||||||
provider_name: String,
|
provider_name: String,
|
||||||
|
description: String,
|
||||||
|
default_model: String,
|
||||||
identity: InventoryIdentity,
|
identity: InventoryIdentity,
|
||||||
configured: bool,
|
configured: bool,
|
||||||
|
provider_type: ProviderType,
|
||||||
|
config_keys: Vec<ConfigKey>,
|
||||||
|
setup_steps: Vec<String>,
|
||||||
supports_refresh: bool,
|
supports_refresh: bool,
|
||||||
static_models: Vec<ModelInfo>,
|
static_models: Vec<ModelInfo>,
|
||||||
model_selection_hint: Option<String>,
|
model_selection_hint: Option<String>,
|
||||||
@@ -199,7 +209,12 @@ impl ProviderInventoryService {
|
|||||||
Ok(Some(ProviderInventoryEntry {
|
Ok(Some(ProviderInventoryEntry {
|
||||||
provider_id: descriptor.provider_id,
|
provider_id: descriptor.provider_id,
|
||||||
provider_name: descriptor.provider_name,
|
provider_name: descriptor.provider_name,
|
||||||
|
description: descriptor.description,
|
||||||
|
default_model: descriptor.default_model,
|
||||||
configured: descriptor.configured,
|
configured: descriptor.configured,
|
||||||
|
provider_type: descriptor.provider_type,
|
||||||
|
config_keys: descriptor.config_keys,
|
||||||
|
setup_steps: descriptor.setup_steps,
|
||||||
supports_refresh: descriptor.supports_refresh,
|
supports_refresh: descriptor.supports_refresh,
|
||||||
refreshing,
|
refreshing,
|
||||||
models,
|
models,
|
||||||
@@ -419,8 +434,13 @@ impl ProviderInventoryService {
|
|||||||
Ok(Some(ProviderDescriptor {
|
Ok(Some(ProviderDescriptor {
|
||||||
provider_id: metadata.name.clone(),
|
provider_id: metadata.name.clone(),
|
||||||
provider_name: metadata.display_name.clone(),
|
provider_name: metadata.display_name.clone(),
|
||||||
|
description: metadata.description.clone(),
|
||||||
|
default_model: metadata.default_model.clone(),
|
||||||
identity,
|
identity,
|
||||||
configured: entry.inventory_configured(),
|
configured: entry.inventory_configured(),
|
||||||
|
provider_type: entry.provider_type(),
|
||||||
|
config_keys: metadata.config_keys.clone(),
|
||||||
|
setup_steps: metadata.setup_steps.clone(),
|
||||||
supports_refresh: entry.supports_inventory_refresh(),
|
supports_refresh: entry.supports_inventory_refresh(),
|
||||||
static_models: metadata.known_models,
|
static_models: metadata.known_models,
|
||||||
model_selection_hint: metadata.model_selection_hint,
|
model_selection_hint: metadata.model_selection_hint,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export async function getProviderInventory(
|
|||||||
): Promise<ProviderInventoryEntryDto[]> {
|
): Promise<ProviderInventoryEntryDto[]> {
|
||||||
const client = await getClient();
|
const client = await getClient();
|
||||||
const t0 = performance.now();
|
const t0 = performance.now();
|
||||||
const response = await client.goose.GooseProvidersInventory({ providerIds });
|
const response = await client.goose.GooseProvidersList({ providerIds });
|
||||||
perfLog(
|
perfLog(
|
||||||
`[perf:inventory] getProviderInventory done in ${(performance.now() - t0).toFixed(1)}ms (n=${response.entries.length})`,
|
`[perf:inventory] getProviderInventory done in ${(performance.now() - t0).toFixed(1)}ms (n=${response.entries.length})`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,16 +20,25 @@ export interface AcpSessionInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DEPRECATED_PROVIDER_IDS = new Set(["claude-code", "codex", "gemini-cli"]);
|
const DEPRECATED_PROVIDER_IDS = new Set(["claude-code", "codex", "gemini-cli"]);
|
||||||
|
const DEFAULT_PROVIDER: AcpProvider = {
|
||||||
|
id: "goose",
|
||||||
|
label: "Goose (Default)",
|
||||||
|
};
|
||||||
|
|
||||||
export async function listProviders(): Promise<AcpProvider[]> {
|
export async function listProviders(): Promise<AcpProvider[]> {
|
||||||
const client = await getClient();
|
const client = await getClient();
|
||||||
const result = await client.goose.GooseProvidersList({});
|
const result = await client.goose.GooseProvidersList({
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: ACP SDK types don't expose providers field
|
providerIds: [],
|
||||||
return (result as any).providers
|
});
|
||||||
.filter(
|
|
||||||
(p: { id: string; label: string }) => !DEPRECATED_PROVIDER_IDS.has(p.id),
|
const providers = result.entries
|
||||||
)
|
.filter((entry) => !DEPRECATED_PROVIDER_IDS.has(entry.providerId))
|
||||||
.map((p: { id: string; label: string }) => ({ id: p.id, label: p.label }));
|
.map((entry) => ({
|
||||||
|
id: entry.providerId,
|
||||||
|
label: entry.providerName,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [DEFAULT_PROVIDER, ...providers];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listSessions(): Promise<AcpSessionInfo[]> {
|
export async function listSessions(): Promise<AcpSessionInfo[]> {
|
||||||
|
|||||||
@@ -126,8 +126,6 @@ export function buildInitScript(options?: {
|
|||||||
return jsonRpcResult(message.id, { stopReason: "end_turn" });
|
return jsonRpcResult(message.id, { stopReason: "end_turn" });
|
||||||
}
|
}
|
||||||
case "_goose/providers/list":
|
case "_goose/providers/list":
|
||||||
return jsonRpcResult(message.id, { providers: [] });
|
|
||||||
case "_goose/providers/inventory":
|
|
||||||
return jsonRpcResult(message.id, { entries: [] });
|
return jsonRpcResult(message.id, { entries: [] });
|
||||||
case "_goose/providers/inventory/refresh":
|
case "_goose/providers/inventory/refresh":
|
||||||
return jsonRpcResult(message.id, { started: [], skipped: [] });
|
return jsonRpcResult(message.id, { started: [], skipped: [] });
|
||||||
|
|||||||
@@ -34,10 +34,6 @@ import type {
|
|||||||
ExportSourceResponse,
|
ExportSourceResponse,
|
||||||
GetExtensionsRequest,
|
GetExtensionsRequest,
|
||||||
GetExtensionsResponse,
|
GetExtensionsResponse,
|
||||||
GetProviderDetailsRequest,
|
|
||||||
GetProviderDetailsResponse,
|
|
||||||
GetProviderInventoryRequest,
|
|
||||||
GetProviderInventoryResponse,
|
|
||||||
GetSessionExtensionsRequest,
|
GetSessionExtensionsRequest,
|
||||||
GetSessionExtensionsResponse,
|
GetSessionExtensionsResponse,
|
||||||
GetToolsRequest,
|
GetToolsRequest,
|
||||||
@@ -76,8 +72,6 @@ import {
|
|||||||
zExportSessionResponse,
|
zExportSessionResponse,
|
||||||
zExportSourceResponse,
|
zExportSourceResponse,
|
||||||
zGetExtensionsResponse,
|
zGetExtensionsResponse,
|
||||||
zGetProviderDetailsResponse,
|
|
||||||
zGetProviderInventoryResponse,
|
|
||||||
zGetSessionExtensionsResponse,
|
zGetSessionExtensionsResponse,
|
||||||
zGetToolsResponse,
|
zGetToolsResponse,
|
||||||
zImportSessionResponse,
|
zImportSessionResponse,
|
||||||
@@ -144,22 +138,6 @@ export class GooseExtClient {
|
|||||||
return zListProvidersResponse.parse(raw) as ListProvidersResponse;
|
return zListProvidersResponse.parse(raw) as ListProvidersResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
async GooseProvidersDetails(
|
|
||||||
params: GetProviderDetailsRequest,
|
|
||||||
): Promise<GetProviderDetailsResponse> {
|
|
||||||
const raw = await this.conn.extMethod("_goose/providers/details", params);
|
|
||||||
return zGetProviderDetailsResponse.parse(raw) as GetProviderDetailsResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async GooseProvidersInventory(
|
|
||||||
params: GetProviderInventoryRequest,
|
|
||||||
): Promise<GetProviderInventoryResponse> {
|
|
||||||
const raw = await this.conn.extMethod("_goose/providers/inventory", params);
|
|
||||||
return zGetProviderInventoryResponse.parse(
|
|
||||||
raw,
|
|
||||||
) as GetProviderInventoryResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async GooseProvidersInventoryRefresh(
|
async GooseProvidersInventoryRefresh(
|
||||||
params: RefreshProviderInventoryRequest,
|
params: RefreshProviderInventoryRequest,
|
||||||
): Promise<RefreshProviderInventoryResponse> {
|
): Promise<RefreshProviderInventoryResponse> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
export type { AddExtensionRequest, ArchiveSessionRequest, CheckSecretRequest, CheckSecretResponse, CreateSourceRequest, CreateSourceResponse, DeleteSessionRequest, DeleteSourceRequest, DictationConfigRequest, DictationConfigResponse, DictationDownloadProgress, DictationLocalModelStatus, DictationModelCancelRequest, DictationModelDeleteRequest, DictationModelDownloadProgressRequest, DictationModelDownloadProgressResponse, DictationModelDownloadRequest, DictationModelOption, DictationModelSelectRequest, DictationModelsListRequest, DictationModelsListResponse, DictationProviderStatusEntry, DictationTranscribeRequest, DictationTranscribeResponse, EmptyResponse, ExportSessionRequest, ExportSessionResponse, ExportSourceRequest, ExportSourceResponse, ExtRequest, ExtResponse, GetExtensionsRequest, GetExtensionsResponse, GetProviderDetailsRequest, GetProviderDetailsResponse, GetProviderInventoryRequest, GetProviderInventoryResponse, GetSessionExtensionsRequest, GetSessionExtensionsResponse, GetToolsRequest, GetToolsResponse, ImportSessionRequest, ImportSessionResponse, ImportSourcesRequest, ImportSourcesResponse, ListProvidersRequest, ListProvidersResponse, ListSourcesRequest, ListSourcesResponse, ModelEntry, ProviderConfigKey, ProviderDetailEntry, ProviderInventoryEntryDto, ProviderInventoryModelDto, ProviderListEntry, ReadConfigRequest, ReadConfigResponse, ReadResourceRequest, ReadResourceResponse, RefreshProviderInventoryRequest, RefreshProviderInventoryResponse, RefreshProviderInventorySkipDto, RefreshProviderInventorySkipReasonDto, RemoveConfigRequest, RemoveExtensionRequest, RemoveSecretRequest, SourceEntry, SourceType, UnarchiveSessionRequest, UpdateSourceRequest, UpdateSourceResponse, UpdateWorkingDirRequest, UpsertConfigRequest, UpsertSecretRequest } from './types.gen.js';
|
export type { AddExtensionRequest, ArchiveSessionRequest, CheckSecretRequest, CheckSecretResponse, CreateSourceRequest, CreateSourceResponse, DeleteSessionRequest, DeleteSourceRequest, DictationConfigRequest, DictationConfigResponse, DictationDownloadProgress, DictationLocalModelStatus, DictationModelCancelRequest, DictationModelDeleteRequest, DictationModelDownloadProgressRequest, DictationModelDownloadProgressResponse, DictationModelDownloadRequest, DictationModelOption, DictationModelSelectRequest, DictationModelsListRequest, DictationModelsListResponse, DictationProviderStatusEntry, DictationTranscribeRequest, DictationTranscribeResponse, EmptyResponse, ExportSessionRequest, ExportSessionResponse, ExportSourceRequest, ExportSourceResponse, ExtRequest, ExtResponse, GetExtensionsRequest, GetExtensionsResponse, GetSessionExtensionsRequest, GetSessionExtensionsResponse, GetToolsRequest, GetToolsResponse, ImportSessionRequest, ImportSessionResponse, ImportSourcesRequest, ImportSourcesResponse, ListProvidersRequest, ListProvidersResponse, ListSourcesRequest, ListSourcesResponse, ProviderConfigKey, ProviderInventoryEntryDto, ProviderInventoryModelDto, ReadConfigRequest, ReadConfigResponse, ReadResourceRequest, ReadResourceResponse, RefreshProviderInventoryRequest, RefreshProviderInventoryResponse, RefreshProviderInventorySkipDto, RefreshProviderInventorySkipReasonDto, RemoveConfigRequest, RemoveExtensionRequest, RemoveSecretRequest, SourceEntry, SourceType, UnarchiveSessionRequest, UpdateSourceRequest, UpdateSourceResponse, UpdateWorkingDirRequest, UpsertConfigRequest, UpsertSecretRequest } from './types.gen.js';
|
||||||
|
|
||||||
export const GOOSE_EXT_METHODS = [
|
export const GOOSE_EXT_METHODS = [
|
||||||
{
|
{
|
||||||
@@ -48,16 +48,6 @@ export const GOOSE_EXT_METHODS = [
|
|||||||
requestType: "ListProvidersRequest",
|
requestType: "ListProvidersRequest",
|
||||||
responseType: "ListProvidersResponse",
|
responseType: "ListProvidersResponse",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
method: "_goose/providers/details",
|
|
||||||
requestType: "GetProviderDetailsRequest",
|
|
||||||
responseType: "GetProviderDetailsResponse",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: "_goose/providers/inventory",
|
|
||||||
requestType: "GetProviderInventoryRequest",
|
|
||||||
responseType: "GetProviderInventoryResponse",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
method: "_goose/providers/inventory/refresh",
|
method: "_goose/providers/inventory/refresh",
|
||||||
requestType: "RefreshProviderInventoryRequest",
|
requestType: "RefreshProviderInventoryRequest",
|
||||||
|
|||||||
@@ -105,69 +105,9 @@ export type GetSessionExtensionsResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List providers available through goose, including the config-default sentinel.
|
* List providers with setup metadata and the current model inventory snapshot.
|
||||||
*/
|
*/
|
||||||
export type ListProvidersRequest = {
|
export type ListProvidersRequest = {
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider list response.
|
|
||||||
*/
|
|
||||||
export type ListProvidersResponse = {
|
|
||||||
providers: Array<ProviderListEntry>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProviderListEntry = {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List providers with full metadata (config keys, setup steps, etc.).
|
|
||||||
*/
|
|
||||||
export type GetProviderDetailsRequest = {
|
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider details response.
|
|
||||||
*/
|
|
||||||
export type GetProviderDetailsResponse = {
|
|
||||||
providers: Array<ProviderDetailEntry>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProviderDetailEntry = {
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
defaultModel: string;
|
|
||||||
isConfigured: boolean;
|
|
||||||
providerType: string;
|
|
||||||
configKeys: Array<ProviderConfigKey>;
|
|
||||||
setupSteps?: Array<string>;
|
|
||||||
knownModels?: Array<ModelEntry>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProviderConfigKey = {
|
|
||||||
name: string;
|
|
||||||
required: boolean;
|
|
||||||
secret: boolean;
|
|
||||||
default?: string | null;
|
|
||||||
oauthFlow?: boolean;
|
|
||||||
deviceCodeFlow?: boolean;
|
|
||||||
primary?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelEntry = {
|
|
||||||
name: string;
|
|
||||||
contextLimit: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read per-provider inventory. Always returns immediately from stored state.
|
|
||||||
*/
|
|
||||||
export type GetProviderInventoryRequest = {
|
|
||||||
/**
|
/**
|
||||||
* Only return entries for these providers. Empty means all.
|
* Only return entries for these providers. Empty means all.
|
||||||
*/
|
*/
|
||||||
@@ -175,9 +115,9 @@ export type GetProviderInventoryRequest = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider inventory response.
|
* Provider list response.
|
||||||
*/
|
*/
|
||||||
export type GetProviderInventoryResponse = {
|
export type ListProvidersResponse = {
|
||||||
entries: Array<ProviderInventoryEntryDto>;
|
entries: Array<ProviderInventoryEntryDto>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -193,10 +133,30 @@ export type ProviderInventoryEntryDto = {
|
|||||||
* Human-readable provider name.
|
* Human-readable provider name.
|
||||||
*/
|
*/
|
||||||
providerName: string;
|
providerName: string;
|
||||||
|
/**
|
||||||
|
* Description of the provider's capabilities.
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
/**
|
||||||
|
* The default/recommended model for this provider.
|
||||||
|
*/
|
||||||
|
defaultModel: string;
|
||||||
/**
|
/**
|
||||||
* Whether Goose has enough configuration to use this provider.
|
* Whether Goose has enough configuration to use this provider.
|
||||||
*/
|
*/
|
||||||
configured: boolean;
|
configured: boolean;
|
||||||
|
/**
|
||||||
|
* Provider classification such as `Preferred`, `Builtin`, `Declarative`, or `Custom`.
|
||||||
|
*/
|
||||||
|
providerType: string;
|
||||||
|
/**
|
||||||
|
* Required configuration keys and setup metadata.
|
||||||
|
*/
|
||||||
|
configKeys: Array<ProviderConfigKey>;
|
||||||
|
/**
|
||||||
|
* Step-by-step setup instructions, when present.
|
||||||
|
*/
|
||||||
|
setupSteps: Array<string>;
|
||||||
/**
|
/**
|
||||||
* Whether this provider supports background inventory refresh.
|
* Whether this provider supports background inventory refresh.
|
||||||
*/
|
*/
|
||||||
@@ -231,6 +191,16 @@ export type ProviderInventoryEntryDto = {
|
|||||||
modelSelectionHint?: string | null;
|
modelSelectionHint?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ProviderConfigKey = {
|
||||||
|
name: string;
|
||||||
|
required: boolean;
|
||||||
|
secret: boolean;
|
||||||
|
default?: string | null;
|
||||||
|
oauthFlow?: boolean;
|
||||||
|
deviceCodeFlow?: boolean;
|
||||||
|
primary?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single model in provider inventory.
|
* A single model in provider inventory.
|
||||||
*/
|
*/
|
||||||
@@ -648,14 +618,14 @@ export type DictationModelSelectRequest = {
|
|||||||
export type ExtRequest = {
|
export type ExtRequest = {
|
||||||
id: string;
|
id: string;
|
||||||
method: string;
|
method: string;
|
||||||
params?: AddExtensionRequest | RemoveExtensionRequest | GetToolsRequest | ReadResourceRequest | UpdateWorkingDirRequest | DeleteSessionRequest | GetExtensionsRequest | GetSessionExtensionsRequest | ListProvidersRequest | GetProviderDetailsRequest | GetProviderInventoryRequest | RefreshProviderInventoryRequest | ReadConfigRequest | UpsertConfigRequest | RemoveConfigRequest | CheckSecretRequest | UpsertSecretRequest | RemoveSecretRequest | ExportSessionRequest | ImportSessionRequest | ArchiveSessionRequest | UnarchiveSessionRequest | CreateSourceRequest | ListSourcesRequest | UpdateSourceRequest | DeleteSourceRequest | ExportSourceRequest | ImportSourcesRequest | DictationTranscribeRequest | DictationConfigRequest | DictationModelsListRequest | DictationModelDownloadRequest | DictationModelDownloadProgressRequest | DictationModelCancelRequest | DictationModelDeleteRequest | DictationModelSelectRequest | {
|
params?: AddExtensionRequest | RemoveExtensionRequest | GetToolsRequest | ReadResourceRequest | UpdateWorkingDirRequest | DeleteSessionRequest | GetExtensionsRequest | GetSessionExtensionsRequest | ListProvidersRequest | RefreshProviderInventoryRequest | ReadConfigRequest | UpsertConfigRequest | RemoveConfigRequest | CheckSecretRequest | UpsertSecretRequest | RemoveSecretRequest | ExportSessionRequest | ImportSessionRequest | ArchiveSessionRequest | UnarchiveSessionRequest | CreateSourceRequest | ListSourcesRequest | UpdateSourceRequest | DeleteSourceRequest | ExportSourceRequest | ImportSourcesRequest | DictationTranscribeRequest | DictationConfigRequest | DictationModelsListRequest | DictationModelDownloadRequest | DictationModelDownloadProgressRequest | DictationModelCancelRequest | DictationModelDeleteRequest | DictationModelSelectRequest | {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExtResponse = {
|
export type ExtResponse = {
|
||||||
id: string;
|
id: string;
|
||||||
result?: EmptyResponse | GetToolsResponse | ReadResourceResponse | GetExtensionsResponse | GetSessionExtensionsResponse | ListProvidersResponse | GetProviderDetailsResponse | GetProviderInventoryResponse | RefreshProviderInventoryResponse | ReadConfigResponse | CheckSecretResponse | ExportSessionResponse | ImportSessionResponse | CreateSourceResponse | ListSourcesResponse | UpdateSourceResponse | ExportSourceResponse | ImportSourcesResponse | DictationTranscribeResponse | DictationConfigResponse | DictationModelsListResponse | DictationModelDownloadProgressResponse | unknown;
|
result?: EmptyResponse | GetToolsResponse | ReadResourceResponse | GetExtensionsResponse | GetSessionExtensionsResponse | ListProvidersResponse | RefreshProviderInventoryResponse | ReadConfigResponse | CheckSecretResponse | ExportSessionResponse | ImportSessionResponse | CreateSourceResponse | ListSourcesResponse | UpdateSourceResponse | ExportSourceResponse | ImportSourcesResponse | DictationTranscribeResponse | DictationConfigResponse | DictationModelsListResponse | DictationModelDownloadProgressResponse | unknown;
|
||||||
} | {
|
} | {
|
||||||
error: {
|
error: {
|
||||||
code: number;
|
code: number;
|
||||||
|
|||||||
@@ -90,27 +90,12 @@ export const zGetSessionExtensionsResponse = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List providers available through goose, including the config-default sentinel.
|
* List providers with setup metadata and the current model inventory snapshot.
|
||||||
*/
|
*/
|
||||||
export const zListProvidersRequest = z.record(z.unknown());
|
export const zListProvidersRequest = z.object({
|
||||||
|
providerIds: z.array(z.string()).optional().default([])
|
||||||
export const zProviderListEntry = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
label: z.string()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider list response.
|
|
||||||
*/
|
|
||||||
export const zListProvidersResponse = z.object({
|
|
||||||
providers: z.array(zProviderListEntry)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List providers with full metadata (config keys, setup steps, etc.).
|
|
||||||
*/
|
|
||||||
export const zGetProviderDetailsRequest = z.record(z.unknown());
|
|
||||||
|
|
||||||
export const zProviderConfigKey = z.object({
|
export const zProviderConfigKey = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
required: z.boolean(),
|
required: z.boolean(),
|
||||||
@@ -124,37 +109,6 @@ export const zProviderConfigKey = z.object({
|
|||||||
primary: z.boolean().optional().default(false)
|
primary: z.boolean().optional().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const zModelEntry = z.object({
|
|
||||||
name: z.string(),
|
|
||||||
contextLimit: z.number().int().gte(0)
|
|
||||||
});
|
|
||||||
|
|
||||||
export const zProviderDetailEntry = z.object({
|
|
||||||
name: z.string(),
|
|
||||||
displayName: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
defaultModel: z.string(),
|
|
||||||
isConfigured: z.boolean(),
|
|
||||||
providerType: z.string(),
|
|
||||||
configKeys: z.array(zProviderConfigKey),
|
|
||||||
setupSteps: z.array(z.string()).optional().default([]),
|
|
||||||
knownModels: z.array(zModelEntry).optional().default([])
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider details response.
|
|
||||||
*/
|
|
||||||
export const zGetProviderDetailsResponse = z.object({
|
|
||||||
providers: z.array(zProviderDetailEntry)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read per-provider inventory. Always returns immediately from stored state.
|
|
||||||
*/
|
|
||||||
export const zGetProviderInventoryRequest = z.object({
|
|
||||||
providerIds: z.array(z.string()).optional().default([])
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single model in provider inventory.
|
* A single model in provider inventory.
|
||||||
*/
|
*/
|
||||||
@@ -182,7 +136,12 @@ export const zProviderInventoryModelDto = z.object({
|
|||||||
export const zProviderInventoryEntryDto = z.object({
|
export const zProviderInventoryEntryDto = z.object({
|
||||||
providerId: z.string(),
|
providerId: z.string(),
|
||||||
providerName: z.string(),
|
providerName: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
defaultModel: z.string(),
|
||||||
configured: z.boolean(),
|
configured: z.boolean(),
|
||||||
|
providerType: z.string(),
|
||||||
|
configKeys: z.array(zProviderConfigKey),
|
||||||
|
setupSteps: z.array(z.string()),
|
||||||
supportsRefresh: z.boolean(),
|
supportsRefresh: z.boolean(),
|
||||||
refreshing: z.boolean(),
|
refreshing: z.boolean(),
|
||||||
models: z.array(zProviderInventoryModelDto),
|
models: z.array(zProviderInventoryModelDto),
|
||||||
@@ -206,9 +165,9 @@ export const zProviderInventoryEntryDto = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider inventory response.
|
* Provider list response.
|
||||||
*/
|
*/
|
||||||
export const zGetProviderInventoryResponse = z.object({
|
export const zListProvidersResponse = z.object({
|
||||||
entries: z.array(zProviderInventoryEntryDto)
|
entries: z.array(zProviderInventoryEntryDto)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -626,8 +585,6 @@ export const zExtRequest = z.object({
|
|||||||
zGetExtensionsRequest,
|
zGetExtensionsRequest,
|
||||||
zGetSessionExtensionsRequest,
|
zGetSessionExtensionsRequest,
|
||||||
zListProvidersRequest,
|
zListProvidersRequest,
|
||||||
zGetProviderDetailsRequest,
|
|
||||||
zGetProviderInventoryRequest,
|
|
||||||
zRefreshProviderInventoryRequest,
|
zRefreshProviderInventoryRequest,
|
||||||
zReadConfigRequest,
|
zReadConfigRequest,
|
||||||
zUpsertConfigRequest,
|
zUpsertConfigRequest,
|
||||||
@@ -672,8 +629,6 @@ export const zExtResponse = z.union([
|
|||||||
zGetExtensionsResponse,
|
zGetExtensionsResponse,
|
||||||
zGetSessionExtensionsResponse,
|
zGetSessionExtensionsResponse,
|
||||||
zListProvidersResponse,
|
zListProvidersResponse,
|
||||||
zGetProviderDetailsResponse,
|
|
||||||
zGetProviderInventoryResponse,
|
|
||||||
zRefreshProviderInventoryResponse,
|
zRefreshProviderInventoryResponse,
|
||||||
zReadConfigResponse,
|
zReadConfigResponse,
|
||||||
zCheckSecretResponse,
|
zCheckSecretResponse,
|
||||||
|
|||||||
+25
-18
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { Box, Text, useInput, useStdout } from "ink";
|
import { Box, Text, useInput, useStdout } from "ink";
|
||||||
import type { GooseClient, ProviderDetailEntry } from "@aaif/goose-sdk";
|
import type { GooseClient, ProviderInventoryEntryDto } from "@aaif/goose-sdk";
|
||||||
import {
|
import {
|
||||||
CRANBERRY,
|
CRANBERRY,
|
||||||
TEAL,
|
TEAL,
|
||||||
@@ -38,7 +38,7 @@ interface ConfigureProps {
|
|||||||
|
|
||||||
interface ModelSelectorProps {
|
interface ModelSelectorProps {
|
||||||
client: GooseClient;
|
client: GooseClient;
|
||||||
provider: ProviderDetailEntry;
|
provider: ProviderInventoryEntryDto;
|
||||||
height: number;
|
height: number;
|
||||||
onSelect: (model: string) => void;
|
onSelect: (model: string) => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
@@ -74,7 +74,7 @@ const ModelSelector = React.memo(function ModelSelector({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
const resp = await client.goose.GooseProvidersModels({
|
const resp = await client.goose.GooseProvidersModels({
|
||||||
providerName: provider.name,
|
providerName: provider.providerId,
|
||||||
});
|
});
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setModels(resp.models);
|
setModels(resp.models);
|
||||||
@@ -96,7 +96,7 @@ const ModelSelector = React.memo(function ModelSelector({
|
|||||||
cancelled = true;
|
cancelled = true;
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
};
|
};
|
||||||
}, [client, provider.name, provider.defaultModel]);
|
}, [client, provider.providerId, provider.defaultModel]);
|
||||||
|
|
||||||
const filtered = (() => {
|
const filtered = (() => {
|
||||||
if (!searchQuery) return models;
|
if (!searchQuery) return models;
|
||||||
@@ -190,7 +190,7 @@ const ModelSelector = React.memo(function ModelSelector({
|
|||||||
<Text color={TEXT_PRIMARY} bold>◆ Select model ◆</Text>
|
<Text color={TEXT_PRIMARY} bold>◆ Select model ◆</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box justifyContent="center" marginBottom={2}>
|
<Box justifyContent="center" marginBottom={2}>
|
||||||
<Text color={TEXT_DIM}>Loading models for {provider.displayName}…</Text>
|
<Text color={TEXT_DIM}>Loading models for {provider.providerName}…</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box justifyContent="center" flexGrow={1} alignItems="center">
|
<Box justifyContent="center" flexGrow={1} alignItems="center">
|
||||||
<Spinner idx={0} />
|
<Spinner idx={0} />
|
||||||
@@ -235,7 +235,7 @@ const ModelSelector = React.memo(function ModelSelector({
|
|||||||
<Text color={TEXT_PRIMARY} bold>◆ Enter model name ◆</Text>
|
<Text color={TEXT_PRIMARY} bold>◆ Enter model name ◆</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box justifyContent="center" marginBottom={2}>
|
<Box justifyContent="center" marginBottom={2}>
|
||||||
<Text color={TEXT_DIM}>Type a model identifier for {provider.displayName}</Text>
|
<Text color={TEXT_DIM}>Type a model identifier for {provider.providerName}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box justifyContent="center">
|
<Box justifyContent="center">
|
||||||
@@ -270,7 +270,7 @@ const ModelSelector = React.memo(function ModelSelector({
|
|||||||
<Text color={TEXT_PRIMARY} bold>◆ Select model ◆</Text>
|
<Text color={TEXT_PRIMARY} bold>◆ Select model ◆</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box justifyContent="center" marginBottom={2}>
|
<Box justifyContent="center" marginBottom={2}>
|
||||||
<Text color={TEXT_DIM}>Choose a model for {provider.displayName}</Text>
|
<Text color={TEXT_DIM}>Choose a model for {provider.providerName}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Search Bar */}
|
{/* Search Bar */}
|
||||||
@@ -359,8 +359,8 @@ export default function ConfigureScreen({
|
|||||||
initialIntent,
|
initialIntent,
|
||||||
}: ConfigureProps) {
|
}: ConfigureProps) {
|
||||||
const [phase, setPhase] = useState<Phase>("loading");
|
const [phase, setPhase] = useState<Phase>("loading");
|
||||||
const [providers, setProviders] = useState<ProviderDetailEntry[]>([]);
|
const [providers, setProviders] = useState<ProviderInventoryEntryDto[]>([]);
|
||||||
const [selectedProvider, setSelectedProvider] = useState<ProviderDetailEntry | null>(null);
|
const [selectedProvider, setSelectedProvider] = useState<ProviderInventoryEntryDto | null>(null);
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
const [errorMsg, setErrorMsg] = useState("");
|
||||||
const [spinIdx, setSpinIdx] = useState(0);
|
const [spinIdx, setSpinIdx] = useState(0);
|
||||||
const [fetchKey, setFetchKey] = useState(0);
|
const [fetchKey, setFetchKey] = useState(0);
|
||||||
@@ -378,13 +378,13 @@ export default function ConfigureScreen({
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const resp = await client.goose.GooseProvidersDetails({});
|
const resp = await client.goose.GooseProvidersList({ providerIds: [] });
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
const sorted = [...resp.providers].sort((a, b) => {
|
const sorted = [...resp.entries].sort((a, b) => {
|
||||||
const aP = a.providerType === "Preferred" ? 0 : 1;
|
const aP = a.providerType === "Preferred" ? 0 : 1;
|
||||||
const bP = b.providerType === "Preferred" ? 0 : 1;
|
const bP = b.providerType === "Preferred" ? 0 : 1;
|
||||||
if (aP !== bP) return aP - bP;
|
if (aP !== bP) return aP - bP;
|
||||||
return a.displayName.localeCompare(b.displayName);
|
return a.providerName.localeCompare(b.providerName);
|
||||||
});
|
});
|
||||||
setProviders(sorted);
|
setProviders(sorted);
|
||||||
|
|
||||||
@@ -392,7 +392,7 @@ export default function ConfigureScreen({
|
|||||||
try {
|
try {
|
||||||
const cfg = await client.goose.GooseConfigRead({ key: "GOOSE_PROVIDER" });
|
const cfg = await client.goose.GooseConfigRead({ key: "GOOSE_PROVIDER" });
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
const current = sorted.find((p) => p.name === cfg.value);
|
const current = sorted.find((p) => p.providerId === cfg.value);
|
||||||
if (current) {
|
if (current) {
|
||||||
setSelectedProvider(current);
|
setSelectedProvider(current);
|
||||||
setPendingConfigValues({});
|
setPendingConfigValues({});
|
||||||
@@ -419,7 +419,11 @@ export default function ConfigureScreen({
|
|||||||
}, [client, fetchKey, initialIntent]);
|
}, [client, fetchKey, initialIntent]);
|
||||||
|
|
||||||
const applyProviderModel = useCallback(
|
const applyProviderModel = useCallback(
|
||||||
async (provider: ProviderDetailEntry, model: string, configValues: Record<string, string>) => {
|
async (
|
||||||
|
provider: ProviderInventoryEntryDto,
|
||||||
|
model: string,
|
||||||
|
configValues: Record<string, string>,
|
||||||
|
) => {
|
||||||
setPhase("saving");
|
setPhase("saving");
|
||||||
try {
|
try {
|
||||||
for (const [key, value] of Object.entries(configValues)) {
|
for (const [key, value] of Object.entries(configValues)) {
|
||||||
@@ -430,11 +434,14 @@ export default function ConfigureScreen({
|
|||||||
await client.goose.GooseConfigUpsert({ key, value });
|
await client.goose.GooseConfigUpsert({ key, value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await client.goose.GooseConfigUpsert({ key: "GOOSE_PROVIDER", value: provider.name });
|
await client.goose.GooseConfigUpsert({
|
||||||
|
key: "GOOSE_PROVIDER",
|
||||||
|
value: provider.providerId,
|
||||||
|
});
|
||||||
await client.goose.GooseConfigUpsert({ key: "GOOSE_MODEL", value: model });
|
await client.goose.GooseConfigUpsert({ key: "GOOSE_MODEL", value: model });
|
||||||
await client.goose.GooseSessionProviderUpdate({
|
await client.goose.GooseSessionProviderUpdate({
|
||||||
sessionId,
|
sessionId,
|
||||||
provider: provider.name,
|
provider: provider.providerId,
|
||||||
model,
|
model,
|
||||||
});
|
});
|
||||||
onComplete();
|
onComplete();
|
||||||
@@ -449,12 +456,12 @@ export default function ConfigureScreen({
|
|||||||
const [pendingConfigValues, setPendingConfigValues] = useState<Record<string, string>>({});
|
const [pendingConfigValues, setPendingConfigValues] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
const handleProviderSelected = useCallback(
|
const handleProviderSelected = useCallback(
|
||||||
(provider: ProviderDetailEntry) => {
|
(provider: ProviderInventoryEntryDto) => {
|
||||||
const keys = provider.configKeys.filter(
|
const keys = provider.configKeys.filter(
|
||||||
(k) => k.required && !k.oauthFlow && !k.deviceCodeFlow,
|
(k) => k.required && !k.oauthFlow && !k.deviceCodeFlow,
|
||||||
);
|
);
|
||||||
setSelectedProvider(provider);
|
setSelectedProvider(provider);
|
||||||
if (keys.length > 0 && !provider.isConfigured) {
|
if (keys.length > 0 && !provider.configured) {
|
||||||
setPhase("configure");
|
setPhase("configure");
|
||||||
} else {
|
} else {
|
||||||
setPendingConfigValues({});
|
setPendingConfigValues({});
|
||||||
|
|||||||
+26
-22
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { Box, Text, useInput, useStdout } from "ink";
|
import { Box, Text, useInput, useStdout } from "ink";
|
||||||
import { TextInput, PasswordInput } from '@inkjs/ui';
|
import { TextInput, PasswordInput } from '@inkjs/ui';
|
||||||
import type { GooseClient, ProviderDetailEntry } from "@aaif/goose-sdk";
|
import type { GooseClient, ProviderInventoryEntryDto } from "@aaif/goose-sdk";
|
||||||
import {
|
import {
|
||||||
CRANBERRY,
|
CRANBERRY,
|
||||||
TEAL,
|
TEAL,
|
||||||
@@ -30,9 +30,9 @@ interface OnboardingProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderSelectorProps {
|
export interface ProviderSelectorProps {
|
||||||
providers: ProviderDetailEntry[];
|
providers: ProviderInventoryEntryDto[];
|
||||||
height: number;
|
height: number;
|
||||||
onSelect: (provider: ProviderDetailEntry) => void;
|
onSelect: (provider: ProviderInventoryEntryDto) => void;
|
||||||
title?: string;
|
title?: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
onBack?: () => void;
|
onBack?: () => void;
|
||||||
@@ -49,8 +49,8 @@ export const ProviderSelector = React.memo(function ProviderSelector({ providers
|
|||||||
const q = searchQuery.toLowerCase();
|
const q = searchQuery.toLowerCase();
|
||||||
return providers.filter(
|
return providers.filter(
|
||||||
(p) =>
|
(p) =>
|
||||||
p.displayName.toLowerCase().includes(q) ||
|
p.providerName.toLowerCase().includes(q) ||
|
||||||
p.name.toLowerCase().includes(q),
|
p.providerId.toLowerCase().includes(q),
|
||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -151,7 +151,11 @@ export const ProviderSelector = React.memo(function ProviderSelector({ providers
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Create grid of provider cards
|
// Create grid of provider cards
|
||||||
const renderProviderCard = (provider: ProviderDetailEntry, _index: number, isSelected: boolean) => {
|
const renderProviderCard = (
|
||||||
|
provider: ProviderInventoryEntryDto,
|
||||||
|
_index: number,
|
||||||
|
isSelected: boolean,
|
||||||
|
) => {
|
||||||
const cardBorder = isSelected ? "double" : "single";
|
const cardBorder = isSelected ? "double" : "single";
|
||||||
const cardBorderColor = isSelected ? GOLD : RULE_COLOR;
|
const cardBorderColor = isSelected ? GOLD : RULE_COLOR;
|
||||||
const textColor = isSelected ? TEXT_PRIMARY : TEXT_SECONDARY;
|
const textColor = isSelected ? TEXT_PRIMARY : TEXT_SECONDARY;
|
||||||
@@ -166,7 +170,7 @@ export const ProviderSelector = React.memo(function ProviderSelector({ providers
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
key={provider.name}
|
key={provider.providerId}
|
||||||
width={cardWidth}
|
width={cardWidth}
|
||||||
height={cardHeight}
|
height={cardHeight}
|
||||||
borderStyle={cardBorder}
|
borderStyle={cardBorder}
|
||||||
@@ -178,14 +182,14 @@ export const ProviderSelector = React.memo(function ProviderSelector({ providers
|
|||||||
<Box justifyContent="space-between" alignItems="center">
|
<Box justifyContent="space-between" alignItems="center">
|
||||||
<Box width={titleWidth} flexShrink={1}>
|
<Box width={titleWidth} flexShrink={1}>
|
||||||
<Text color={textColor} bold={isSelected} wrap="truncate">
|
<Text color={textColor} bold={isSelected} wrap="truncate">
|
||||||
{provider.displayName}
|
{provider.providerName}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexShrink={0}>
|
<Box flexShrink={0}>
|
||||||
{provider.providerType === "Preferred" && (
|
{provider.providerType === "Preferred" && (
|
||||||
<Text color={TEAL}>★</Text>
|
<Text color={TEAL}>★</Text>
|
||||||
)}
|
)}
|
||||||
{provider.isConfigured && (
|
{provider.configured && (
|
||||||
<Text color={TEAL}>✓</Text>
|
<Text color={TEAL}>✓</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -194,7 +198,7 @@ export const ProviderSelector = React.memo(function ProviderSelector({ providers
|
|||||||
<Box marginTop={1} flexDirection="column" flexGrow={1}>
|
<Box marginTop={1} flexDirection="column" flexGrow={1}>
|
||||||
<Box width={contentWidth}>
|
<Box width={contentWidth}>
|
||||||
<Text color={TEXT_DIM} wrap="truncate">
|
<Text color={TEXT_DIM} wrap="truncate">
|
||||||
{provider.name}
|
{provider.providerId}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{provider.description && (
|
{provider.description && (
|
||||||
@@ -306,7 +310,7 @@ export const ProviderSelector = React.memo(function ProviderSelector({ providers
|
|||||||
});
|
});
|
||||||
|
|
||||||
export interface ProviderConfiguratorProps {
|
export interface ProviderConfiguratorProps {
|
||||||
provider: ProviderDetailEntry;
|
provider: ProviderInventoryEntryDto;
|
||||||
height: number;
|
height: number;
|
||||||
onComplete: (values: Record<string, string>) => void;
|
onComplete: (values: Record<string, string>) => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
@@ -383,7 +387,7 @@ export const ProviderConfigurator = React.memo(function ProviderConfigurator({ p
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Box justifyContent="center" marginBottom={1}>
|
<Box justifyContent="center" marginBottom={1}>
|
||||||
<Text color={TEXT_PRIMARY} bold>
|
<Text color={TEXT_PRIMARY} bold>
|
||||||
◆ Configure {provider.displayName} ◆
|
◆ Configure {provider.providerName} ◆
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{provider.description && (
|
{provider.description && (
|
||||||
@@ -473,7 +477,7 @@ export const ProviderConfigurator = React.memo(function ProviderConfigurator({ p
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface SuccessScreenProps {
|
interface SuccessScreenProps {
|
||||||
provider: ProviderDetailEntry | null;
|
provider: ProviderInventoryEntryDto | null;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +505,7 @@ const SuccessScreen = React.memo(function SuccessScreen({ provider, height }: Su
|
|||||||
{provider && (
|
{provider && (
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={TEXT_SECONDARY}>
|
<Text color={TEXT_SECONDARY}>
|
||||||
Connected to {provider.displayName}
|
Connected to {provider.providerName}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -517,9 +521,9 @@ export default function Onboarding({
|
|||||||
onComplete,
|
onComplete,
|
||||||
}: OnboardingProps) {
|
}: OnboardingProps) {
|
||||||
const [phase, setPhase] = useState<Phase>("loading");
|
const [phase, setPhase] = useState<Phase>("loading");
|
||||||
const [providers, setProviders] = useState<ProviderDetailEntry[]>([]);
|
const [providers, setProviders] = useState<ProviderInventoryEntryDto[]>([]);
|
||||||
const [selectedProvider, setSelectedProvider] =
|
const [selectedProvider, setSelectedProvider] =
|
||||||
useState<ProviderDetailEntry | null>(null);
|
useState<ProviderInventoryEntryDto | null>(null);
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
const [errorMsg, setErrorMsg] = useState("");
|
||||||
const [spinIdx, setSpinIdx] = useState(0);
|
const [spinIdx, setSpinIdx] = useState(0);
|
||||||
const [fetchKey, setFetchKey] = useState(0);
|
const [fetchKey, setFetchKey] = useState(0);
|
||||||
@@ -535,12 +539,12 @@ export default function Onboarding({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const resp = await client.goose.GooseProvidersDetails({});
|
const resp = await client.goose.GooseProvidersList({ providerIds: [] });
|
||||||
const sorted = [...resp.providers].sort((a, b) => {
|
const sorted = [...resp.entries].sort((a, b) => {
|
||||||
const aP = a.providerType === "Preferred" ? 0 : 1;
|
const aP = a.providerType === "Preferred" ? 0 : 1;
|
||||||
const bP = b.providerType === "Preferred" ? 0 : 1;
|
const bP = b.providerType === "Preferred" ? 0 : 1;
|
||||||
if (aP !== bP) return aP - bP;
|
if (aP !== bP) return aP - bP;
|
||||||
return a.displayName.localeCompare(b.displayName);
|
return a.providerName.localeCompare(b.providerName);
|
||||||
});
|
});
|
||||||
setProviders(sorted);
|
setProviders(sorted);
|
||||||
setPhase("select_provider");
|
setPhase("select_provider");
|
||||||
@@ -552,7 +556,7 @@ export default function Onboarding({
|
|||||||
}, [client, fetchKey]);
|
}, [client, fetchKey]);
|
||||||
|
|
||||||
const saveProvider = useCallback(
|
const saveProvider = useCallback(
|
||||||
async (provider: ProviderDetailEntry, values: Record<string, string>) => {
|
async (provider: ProviderInventoryEntryDto, values: Record<string, string>) => {
|
||||||
setPhase("saving");
|
setPhase("saving");
|
||||||
try {
|
try {
|
||||||
for (const [key, value] of Object.entries(values)) {
|
for (const [key, value] of Object.entries(values)) {
|
||||||
@@ -565,7 +569,7 @@ export default function Onboarding({
|
|||||||
}
|
}
|
||||||
await client.goose.GooseConfigUpsert({
|
await client.goose.GooseConfigUpsert({
|
||||||
key: "GOOSE_PROVIDER",
|
key: "GOOSE_PROVIDER",
|
||||||
value: provider.name,
|
value: provider.providerId,
|
||||||
});
|
});
|
||||||
await client.goose.GooseConfigUpsert({
|
await client.goose.GooseConfigUpsert({
|
||||||
key: "GOOSE_MODEL",
|
key: "GOOSE_MODEL",
|
||||||
@@ -582,7 +586,7 @@ export default function Onboarding({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const confirmProvider = useCallback(
|
const confirmProvider = useCallback(
|
||||||
(provider: ProviderDetailEntry) => {
|
(provider: ProviderInventoryEntryDto) => {
|
||||||
const keys = provider.configKeys.filter(
|
const keys = provider.configKeys.filter(
|
||||||
(k) => k.required && !k.oauthFlow && !k.deviceCodeFlow,
|
(k) => k.required && !k.oauthFlow && !k.deviceCodeFlow,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user