Skip to content

Commit 9aed300

Browse files
committed
Add base_path field to custom provider config
Allow custom providers using the OpenAI engine to explicitly set the API path (e.g. "v1/responses" vs "v1/chat/completions") instead of relying on URL path parsing or the default. This enables pointing a custom provider at an OpenAI-compatible endpoint that serves the Responses API without needing to embed the path in the base_url. The new optional base_path field is threaded through the declarative provider config, CLI, and server routes. When set, it takes priority over both the URL-derived path and the model-name heuristic in should_use_responses_api(). Signed-off-by: Dan Prince <dprince@redhat.com>
1 parent 85c7f97 commit 9aed300

File tree

4 files changed

+22
-4
lines changed

4 files changed

+22
-4
lines changed

crates/goose-cli/src/commands/configure.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,7 @@ fn add_provider() -> anyhow::Result<()> {
20272027
headers,
20282028
requires_auth,
20292029
catalog_provider_id: None,
2030+
base_path: None,
20302031
})?;
20312032

20322033
cliclack::outro(format!("Custom provider added: {}", display_name))?;

crates/goose-server/src/routes/config_management.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub struct UpdateCustomProviderRequest {
100100
pub requires_auth: bool,
101101
#[serde(default)]
102102
pub catalog_provider_id: Option<String>,
103+
#[serde(default)]
104+
pub base_path: Option<String>,
103105
}
104106

105107
fn default_requires_auth() -> bool {
@@ -655,6 +657,7 @@ pub async fn create_custom_provider(
655657
headers: request.headers,
656658
requires_auth: request.requires_auth,
657659
catalog_provider_id: request.catalog_provider_id,
660+
base_path: request.base_path,
658661
},
659662
)?;
660663

@@ -726,6 +729,7 @@ pub async fn update_custom_provider(
726729
headers: request.headers,
727730
requires_auth: request.requires_auth,
728731
catalog_provider_id: request.catalog_provider_id,
732+
base_path: request.base_path,
729733
},
730734
)?;
731735

crates/goose/src/config/declarative_providers.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub struct DeclarativeProviderConfig {
4444
pub requires_auth: bool,
4545
#[serde(default)]
4646
pub catalog_provider_id: Option<String>,
47+
#[serde(default)]
48+
pub base_path: Option<String>,
4749
}
4850

4951
fn default_requires_auth() -> bool {
@@ -105,6 +107,7 @@ pub struct CreateCustomProviderParams {
105107
pub headers: Option<HashMap<String, String>>,
106108
pub requires_auth: bool,
107109
pub catalog_provider_id: Option<String>,
110+
pub base_path: Option<String>,
108111
}
109112

110113
#[derive(Debug, Clone)]
@@ -119,6 +122,7 @@ pub struct UpdateCustomProviderParams {
119122
pub headers: Option<HashMap<String, String>>,
120123
pub requires_auth: bool,
121124
pub catalog_provider_id: Option<String>,
125+
pub base_path: Option<String>,
122126
}
123127

124128
pub fn create_custom_provider(
@@ -159,6 +163,7 @@ pub fn create_custom_provider(
159163
supports_streaming: params.supports_streaming,
160164
requires_auth: params.requires_auth,
161165
catalog_provider_id: params.catalog_provider_id,
166+
base_path: params.base_path,
162167
};
163168

164169
let custom_providers_dir = custom_providers_dir();
@@ -221,6 +226,7 @@ pub fn update_custom_provider(params: UpdateCustomProviderParams) -> Result<()>
221226
supports_streaming: params.supports_streaming,
222227
requires_auth: params.requires_auth,
223228
catalog_provider_id: params.catalog_provider_id,
229+
base_path: params.base_path.or(existing_config.base_path),
224230
};
225231

226232
let file_path = custom_providers_dir().join(format!("{}.json", updated_config.name));

crates/goose/src/providers/openai.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,18 @@ impl OpenAiProvider {
168168
} else {
169169
format!("{}://{}", url.scheme(), url.host_str().unwrap_or(""))
170170
};
171-
let base_path = url.path().trim_start_matches('/').to_string();
172-
let base_path = if base_path.is_empty() || base_path == "v1" || base_path == "v1/" {
173-
"v1/chat/completions".to_string()
171+
let normalized_base_path = config
172+
.base_path
173+
.filter(|p| !p.trim().is_empty());
174+
let base_path = if let Some(ref explicit_path) = normalized_base_path {
175+
explicit_path.trim_start_matches('/').to_string()
174176
} else {
175-
base_path
177+
let url_path = url.path().trim_start_matches('/').to_string();
178+
if url_path.is_empty() || url_path == "v1" || url_path == "v1/" {
179+
"v1/chat/completions".to_string()
180+
} else {
181+
url_path
182+
}
176183
};
177184

178185
let timeout_secs = config.timeout_seconds.unwrap_or(600);

0 commit comments

Comments
 (0)