Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
9268307
first pass
katzdave Jan 20, 2026
ed68894
lots of improvements
katzdave Jan 21, 2026
a5e807e
another big wave
katzdave Jan 21, 2026
f327622
Test fix
katzdave Jan 21, 2026
56dd2a5
:fix cost v1
katzdave Jan 21, 2026
4c8b08b
Merge branch 'main' of github.com:block/goose into dkatz/canonical-pr…
katzdave Jan 21, 2026
a8d202c
actuall fix cost now
katzdave Jan 21, 2026
2c19b24
some docs fixes
katzdave Jan 22, 2026
078566b
fix cost display
katzdave Jan 22, 2026
87ce2eb
add recommeded to diff
katzdave Jan 22, 2026
248e69c
normalization fix for openrouter + regen
katzdave Jan 22, 2026
cc848de
fix databricks model support
katzdave Jan 22, 2026
4500c26
get rid of deduping logic
katzdave Jan 22, 2026
d93c35d
fmt
katzdave Jan 22, 2026
b2b50bc
Merge branch 'main' of github.com:block/goose into dkatz/canonical-pr…
katzdave Jan 22, 2026
ca1a514
fix build/tests and rm lots of comments
katzdave Jan 22, 2026
edbe3ed
fmt
katzdave Jan 22, 2026
03c3f5b
copilot suggestions
katzdave Jan 22, 2026
88a7ec5
fmt
katzdave Jan 22, 2026
582d9cd
merge
katzdave Jan 23, 2026
6cf36f3
Merge branch 'main' of github.com:block/goose into dkatz/canonical-pr…
katzdave Jan 26, 2026
8097f0f
context hookin
katzdave Jan 26, 2026
4bfe1ea
Merge branch 'dkatz/canonical-provider' of github.com:block/goose int…
katzdave Jan 26, 2026
b3bb63f
big refactor to canonical lookup
katzdave Jan 26, 2026
eeac1f7
update test script
katzdave Jan 27, 2026
ee14662
big client - server change
katzdave Jan 27, 2026
0d22e12
fast model fix
katzdave Jan 27, 2026
115ca7c
fmt
katzdave Jan 27, 2026
c9efc8f
Merge branch 'main' of github.com:block/goose into dkatz/canonical-pr…
katzdave Jan 27, 2026
dd41da5
Fix fetch_supported_models signature to match trait
katzdave Jan 27, 2026
f0d40cd
rebuild canonical
katzdave Jan 27, 2026
52b8ae5
merge
katzdave Jan 27, 2026
ca7ebfc
fmt
katzdave Jan 27, 2026
dc25cb3
fmt
katzdave Jan 27, 2026
c2f6376
review comments
katzdave Jan 29, 2026
ddffc7e
fmt
katzdave Jan 29, 2026
323f8ea
regen openapi
katzdave Jan 29, 2026
2a9837f
fix xai
katzdave Jan 29, 2026
b8b27e6
merge conflitct
katzdave Jan 29, 2026
86d3cbb
merge conflict real
katzdave Jan 29, 2026
9a6f132
merge conflicts?
katzdave Jan 29, 2026
2a62f1c
fix some lingering merge issues
katzdave Jan 29, 2026
11bfc18
another wave of comment cleanup
katzdave Jan 29, 2026
d581d15
rm comment
katzdave Jan 29, 2026
a9488be
rm unnecessary file
katzdave Jan 29, 2026
25746b5
fmt
katzdave Jan 29, 2026
3cfaa73
merge conflicts
katzdave Jan 30, 2026
43e110c
fix cost
katzdave Jan 30, 2026
4b041d1
another small fix
katzdave Jan 30, 2026
9cfaaba
lint
katzdave Jan 30, 2026
f687d54
Merge branch 'main' of github.com:block/goose into dkatz/canonical-co…
katzdave Jan 30, 2026
06e8f81
fix server test
katzdave Jan 30, 2026
d662077
fix diff
katzdave Jan 30, 2026
e1a0964
rm rm readme
katzdave Jan 30, 2026
8cb28be
more test fix
katzdave Jan 31, 2026
f3b410e
for no matching use more general string matching
katzdave Feb 2, 2026
c90bda4
rm checker script
katzdave Feb 2, 2026
ecb6ec4
merge
katzdave Feb 2, 2026
715caea
fix provider name
katzdave Feb 2, 2026
c8fc071
Merge branch 'main' of github.com:block/goose into dkatz/canonical-co…
katzdave Feb 3, 2026
2d82d2a
fix schema
katzdave Feb 3, 2026
bc3eddf
regenerate and merge
katzdave Feb 4, 2026
e4b391c
merge
katzdave Feb 4, 2026
5cd146f
merge
katzdave Feb 10, 2026
5889c6e
Merge branch 'main' of github.com:block/goose into dkatz/canonical-co…
katzdave Feb 10, 2026
b426ff3
conflicts again
katzdave Feb 11, 2026
18760a6
oops
katzdave Feb 11, 2026
31ca8ab
merge
katzdave Feb 11, 2026
896ac38
with_canonical_limits refactor
katzdave Feb 11, 2026
671d504
Fmt
katzdave Feb 11, 2026
e340705
comment cleanup
katzdave Feb 11, 2026
54344c2
merge
katzdave Feb 12, 2026
3a3148d
Merge branch 'main' of github.com:block/goose into dkatz/canonical-co…
katzdave Feb 13, 2026
b69f842
fix html
katzdave Feb 13, 2026
1cd6379
clean up errors slightly
katzdave Feb 13, 2026
eefa79d
openapi
katzdave Feb 13, 2026
40d4450
mcp app error
katzdave Feb 13, 2026
0689d04
Merge branch 'main' of github.com:block/goose into dkatz/canonical-co…
katzdave Feb 13, 2026
2070389
rm unrelated renderer change
katzdave Feb 13, 2026
1e8f5d5
fix openapi schema
katzdave Feb 13, 2026
79a7859
hopefully fix test timeout
katzdave Feb 17, 2026
cbec3d1
fmt
katzdave Feb 17, 2026
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
16 changes: 13 additions & 3 deletions crates/goose-acp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,8 @@ impl GooseAcpAgent {
let config_path = self.config_dir.join(CONFIG_YAML_NAME);
let config = Config::new(&config_path, "goose")?;
let model_id = config.get_goose_model()?;
goose::model::ModelConfig::new(&model_id)?
let provider_name = config.get_goose_provider()?;
goose::model::ModelConfig::new(&model_id)?.with_canonical_limits(&provider_name)
}
};
let provider = (self.provider_factory)(model_config, Vec::new()).await?;
Expand Down Expand Up @@ -955,9 +956,18 @@ impl GooseAcpAgent {
session_id: &str,
model_id: &str,
) -> Result<SetSessionModelResponse, sacp::Error> {
let model_config = goose::model::ModelConfig::new(model_id).map_err(|e| {
sacp::Error::invalid_params().data(format!("Invalid model config: {}", e))
let config_path = self.config_dir.join(CONFIG_YAML_NAME);
let config = Config::new(&config_path, "goose").map_err(|e| {
sacp::Error::internal_error().data(format!("Failed to read config: {}", e))
})?;
let provider_name = config.get_goose_provider().map_err(|_| {
sacp::Error::internal_error().data("No provider configured".to_string())
})?;
let model_config = goose::model::ModelConfig::new(model_id)
.map_err(|e| {
sacp::Error::invalid_params().data(format!("Invalid model config: {}", e))
})?
.with_canonical_limits(&provider_name);
let provider = (self.provider_factory)(model_config, Vec::new())
.await
.map_err(|e| {
Expand Down
4 changes: 1 addition & 3 deletions crates/goose-acp/src/server_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ impl AcpServer {
Box::pin(async move {
let config_path = config_dir.join(goose::config::base::CONFIG_YAML_NAME);
let config = goose::config::Config::new(&config_path, "goose")?;
let provider_name = config
.get_goose_provider()
.map_err(|_| anyhow::anyhow!("No provider configured"))?;
let provider_name = config.get_goose_provider()?;
goose::providers::create(&provider_name, model_config, extensions).await
})
});
Expand Down
2 changes: 1 addition & 1 deletion crates/goose-acp/tests/common_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub async fn run_config_mcp<C: Connection>() {
let mcp = McpFixture::new(Some(expected_session_id.clone())).await;

let config_yaml = format!(
"GOOSE_MODEL: {TEST_MODEL}\nextensions:\n mcp-fixture:\n enabled: true\n type: streamable_http\n name: mcp-fixture\n description: MCP fixture\n uri: \"{}\"\n",
"GOOSE_MODEL: {TEST_MODEL}\nGOOSE_PROVIDER: openai\nextensions:\n mcp-fixture:\n enabled: true\n type: streamable_http\n name: mcp-fixture\n description: MCP fixture\n uri: \"{}\"\n",
mcp.url
);
fs::write(temp_dir.path().join(CONFIG_YAML_NAME), config_yaml).unwrap();
Expand Down
6 changes: 5 additions & 1 deletion crates/goose-acp/tests/fixtures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ pub async fn spawn_acp_server_in_process(
fs::create_dir_all(data_root).unwrap();
let config_path = data_root.join(goose::config::base::CONFIG_YAML_NAME);
if !config_path.exists() {
fs::write(&config_path, format!("GOOSE_MODEL: {TEST_MODEL}\n")).unwrap();
fs::write(
&config_path,
format!("GOOSE_MODEL: {TEST_MODEL}\nGOOSE_PROVIDER: openai\n"),
)
.unwrap();
}
let provider_factory = provider_factory.unwrap_or_else(|| {
let base_url = openai_base_url.to_string();
Expand Down
11 changes: 6 additions & 5 deletions crates/goose-cli/src/commands/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ async fn handle_oauth_configuration(provider_name: &str, key_name: &str) -> anyh
));

// Create a temporary provider instance to handle OAuth
let temp_model = ModelConfig::new("temp")?;
let temp_model = ModelConfig::new("temp")?.with_canonical_limits(provider_name);
match create(provider_name, temp_model, Vec::new()).await {
Ok(provider) => match provider.configure_oauth().await {
Ok(_) => {
Expand Down Expand Up @@ -682,7 +682,8 @@ pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
let spin = spinner();
spin.start("Attempting to fetch supported models...");
let models_res = {
let temp_model_config = ModelConfig::new(&provider_meta.default_model)?;
let temp_model_config =
ModelConfig::new(&provider_meta.default_model)?.with_canonical_limits(provider_name);
let temp_provider = create(provider_name, temp_model_config, Vec::new()).await?;
retry_operation(&RetryConfig::default(), || async {
temp_provider.fetch_recommended_models().await
Expand Down Expand Up @@ -1442,7 +1443,7 @@ pub async fn configure_tool_permissions_dialog() -> anyhow::Result<()> {
let model: String = config
.get_goose_model()
.expect("No model configured. Please set model first");
let model_config = ModelConfig::new(&model)?;
let model_config = ModelConfig::new(&model)?.with_canonical_limits(&provider_name);

let agent = Agent::new();

Expand Down Expand Up @@ -1662,7 +1663,7 @@ pub async fn handle_openrouter_auth() -> anyhow::Result<()> {
println!("\nTesting configuration...");
let configured_model: String = config.get_goose_model()?;
let model_config = match goose::model::ModelConfig::new(&configured_model) {
Ok(config) => config,
Ok(config) => config.with_canonical_limits("openrouter"),
Err(e) => {
eprintln!("⚠️ Invalid model configuration: {}", e);
eprintln!("Your settings have been saved. Please check your model configuration.");
Expand Down Expand Up @@ -1742,7 +1743,7 @@ pub async fn handle_tetrate_auth() -> anyhow::Result<()> {
println!("\nTesting configuration...");
let configured_model: String = config.get_goose_model()?;
let model_config = match goose::model::ModelConfig::new(&configured_model) {
Ok(config) => config,
Ok(config) => config.with_canonical_limits("tetrate"),
Err(e) => {
eprintln!("⚠️ Invalid model configuration: {}", e);
eprintln!("Your settings have been saved. Please check your model configuration.");
Expand Down
8 changes: 7 additions & 1 deletion crates/goose-cli/src/commands/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,13 @@ pub async fn handle_term_info() -> Result<()> {
let context_limit = config
.get_goose_model()
.ok()
.and_then(|model_name| goose::model::ModelConfig::new(&model_name).ok())
.and_then(|model_name| {
config.get_goose_provider().ok().and_then(|provider_name| {
goose::model::ModelConfig::new(&model_name)
.ok()
.map(|c| c.with_canonical_limits(&provider_name))
})
})
.map(|mc| mc.context_limit())
.unwrap_or(128_000);

Expand Down
2 changes: 1 addition & 1 deletion crates/goose-cli/src/commands/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ fn get_provider_and_model() -> (String, String) {
}

async fn create_agent(provider_name: &str, model: &str) -> Result<Agent> {
let model_config = goose::model::ModelConfig::new(model)?;
let model_config = goose::model::ModelConfig::new(model)?.with_canonical_limits(provider_name);

let agent = Agent::new();

Expand Down
2 changes: 1 addition & 1 deletion crates/goose-cli/src/scenario_tests/scenario_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ where

let inner_provider = create(
&factory_name,
ModelConfig::new(config.model_name)?,
ModelConfig::new(config.model_name)?.with_canonical_limits(&factory_name),
Vec::new(),
)
.await?;
Expand Down
1 change: 1 addition & 0 deletions crates/goose-cli/src/session/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ fn resolve_provider_and_model(
output::render_error(&format!("Failed to create model configuration: {}", e));
process::exit(1);
})
.with_canonical_limits(&provider_name)
.with_temperature(temperature)
};

Expand Down
2 changes: 1 addition & 1 deletion crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1841,7 +1841,7 @@ async fn get_reasoner() -> Result<Arc<dyn Provider>, anyhow::Error> {
};

let model_config =
ModelConfig::new_with_context_env(model, Some("GOOSE_PLANNER_CONTEXT_LIMIT"))?;
ModelConfig::new_with_context_env(model, &provider, Some("GOOSE_PLANNER_CONTEXT_LIMIT"))?;
let extensions = goose::config::extensions::get_enabled_extensions_with_config(config);
let reasoner = create(&provider, model_config, extensions).await?;

Expand Down
8 changes: 4 additions & 4 deletions crates/goose-server/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ derive_utoipa!(Icon as IconSchema);
super::routes::config_management::check_provider,
super::routes::config_management::set_config_provider,
super::routes::config_management::configure_provider_oauth,
super::routes::config_management::get_pricing,
super::routes::config_management::get_canonical_model_info,
super::routes::prompts::get_prompts,
super::routes::prompts::get_prompt,
super::routes::prompts::save_prompt,
Expand Down Expand Up @@ -443,9 +443,9 @@ derive_utoipa!(Icon as IconSchema);
super::routes::config_management::UpdateCustomProviderRequest,
super::routes::config_management::CheckProviderRequest,
super::routes::config_management::SetProviderRequest,
super::routes::config_management::PricingQuery,
super::routes::config_management::PricingResponse,
super::routes::config_management::PricingData,
super::routes::config_management::ModelInfoQuery,
super::routes::config_management::ModelInfoResponse,
super::routes::config_management::ModelInfoData,
super::routes::prompts::PromptsListResponse,
super::routes::prompts::PromptContentResponse,
super::routes::prompts::SavePromptRequest,
Expand Down
1 change: 1 addition & 0 deletions crates/goose-server/src/routes/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ async fn update_agent_provider(
format!("Invalid model config: {}", e),
)
})?
.with_canonical_limits(&payload.provider)
.with_context_limit(payload.context_limit)
.with_request_params(payload.request_params);

Expand Down
125 changes: 44 additions & 81 deletions crates/goose-server/src/routes/config_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,6 @@ fn is_valid_provider_name(provider_name: &str) -> bool {
pub async fn read_config(
Json(query): Json<ConfigKeyQuery>,
) -> Result<Json<ConfigValueResponse>, ErrorResponse> {
if query.key == "model-limits" {
let limits = ModelConfig::get_all_model_limits();
return Ok(Json(ConfigValueResponse::Value(serde_json::to_value(
limits,
)?)));
}

let config = Config::global();

let response_value = match config.get(&query.key, query.is_secret) {
Expand Down Expand Up @@ -386,7 +379,7 @@ pub async fn get_provider_models(
)));
}

let model_config = ModelConfig::new(&metadata.default_model)?;
let model_config = ModelConfig::new(&metadata.default_model)?.with_canonical_limits(&name);
let provider = goose::providers::create(&name, model_config, Vec::new()).await?;

let models_result = provider.fetch_recommended_models().await;
Expand Down Expand Up @@ -426,66 +419,60 @@ pub async fn get_slash_commands() -> Result<Json<SlashCommandsResponse>, ErrorRe
}

#[derive(Serialize, ToSchema)]
pub struct PricingData {
pub struct ModelInfoData {
pub provider: String,
pub model: String,
pub input_token_cost: f64,
pub output_token_cost: f64,
pub context_limit: usize,
pub max_output_tokens: Option<usize>,
pub input_token_cost: Option<f64>,
pub output_token_cost: Option<f64>,
pub cache_read_token_cost: Option<f64>,
pub cache_write_token_cost: Option<f64>,
pub currency: String,
pub context_length: Option<u32>,
}

#[derive(Serialize, ToSchema)]
pub struct PricingResponse {
pub pricing: Vec<PricingData>,
pub struct ModelInfoResponse {
pub model_info: Option<ModelInfoData>,
pub source: String,
}

#[derive(Deserialize, ToSchema)]
pub struct PricingQuery {
pub struct ModelInfoQuery {
pub provider: String,
pub model: String,
}

#[utoipa::path(
post,
path = "/config/pricing",
request_body = PricingQuery,
path = "/config/canonical-model-info",
request_body = ModelInfoQuery,
responses(
(status = 200, description = "Model pricing data retrieved successfully", body = PricingResponse)
(status = 200, description = "Model information retrieved successfully", body = ModelInfoResponse)
)
)]
Comment on lines 446 to 453
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API documentation declares a 404 response status, but the implementation always returns 200 (Ok) with a None value when the model is not found. This creates a discrepancy between the documented API behavior and actual implementation. Either update the implementation to return StatusCode::NOT_FOUND when model_info is None, or remove the 404 status from the documentation.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, let's remove the 404 handler

pub async fn get_pricing(
Json(query): Json<PricingQuery>,
) -> Result<Json<PricingResponse>, ErrorResponse> {
let canonical_model =
maybe_get_canonical_model(&query.provider, &query.model).ok_or_else(|| {
ErrorResponse::not_found(format!(
"Model '{}/{}' not found",
query.provider, query.model
))
})?;

let mut pricing_data = Vec::new();

if let (Some(input_cost), Some(output_cost)) =
(canonical_model.cost.input, canonical_model.cost.output)
{
pricing_data.push(PricingData {
provider: query.provider.clone(),
model: query.model.clone(),
// Canonical model costs are per million tokens, convert to per-token
input_token_cost: input_cost / 1_000_000.0,
output_token_cost: output_cost / 1_000_000.0,
currency: "$".to_string(),
context_length: Some(canonical_model.limit.context as u32),
});
}
pub async fn get_canonical_model_info(
Json(query): Json<ModelInfoQuery>,
) -> Json<ModelInfoResponse> {
let canonical_model = maybe_get_canonical_model(&query.provider, &query.model);

Comment on lines +454 to 458
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handler returns Result<_, StatusCode> and drops the standard ErrorResponse body used by other routes, which makes 404s harder to debug for API consumers; consider returning ErrorResponse::not_found(...) (or a small JSON error payload) while still mapping to HTTP 404.

Copilot uses AI. Check for mistakes.
let model_info = canonical_model.map(|canonical_model| ModelInfoData {
provider: query.provider.clone(),
model: query.model.clone(),
context_limit: canonical_model.limit.context,
max_output_tokens: canonical_model.limit.output,
// Costs are per million tokens - client handles division for display
input_token_cost: canonical_model.cost.input,
output_token_cost: canonical_model.cost.output,
cache_read_token_cost: canonical_model.cost.cache_read,
cache_write_token_cost: canonical_model.cost.cache_write,
currency: "$".to_string(),
});

Ok(Json(PricingResponse {
pricing: pricing_data,
Json(ModelInfoResponse {
model_info,
source: "canonical".to_string(),
}))
})
}

#[utoipa::path(
Expand Down Expand Up @@ -807,9 +794,11 @@ pub async fn configure_provider_oauth(
)));
}

let temp_model = ModelConfig::new("temp").map_err(|e| {
ErrorResponse::bad_request(format!("Failed to create temporary model config: {}", e))
})?;
let temp_model = ModelConfig::new("temp")
.map_err(|e| {
ErrorResponse::bad_request(format!("Failed to create temporary model config: {}", e))
})?
.with_canonical_limits(&provider_name);

// OAuth configuration does not use extensions.
let provider = create(&provider_name, temp_model, Vec::new())
Expand Down Expand Up @@ -849,7 +838,10 @@ pub fn routes(state: Arc<AppState>) -> Router {
.route("/config/providers/{name}/models", get(get_provider_models))
.route("/config/detect-provider", post(detect_provider))
.route("/config/slash_commands", get(get_slash_commands))
.route("/config/pricing", post(get_pricing))
.route(
"/config/canonical-model-info",
post(get_canonical_model_info),
)
.route("/config/init", post(init_config))
.route("/config/backup", post(backup_config))
.route("/config/recover", post(recover_config))
Expand All @@ -872,33 +864,4 @@ pub fn routes(state: Arc<AppState>) -> Router {
}

#[cfg(test)]
mod tests {
use http::HeaderMap;

use super::*;

#[tokio::test]
async fn test_read_model_limits() {
let mut headers = HeaderMap::new();
headers.insert("X-Secret-Key", "test".parse().unwrap());

let result = read_config(Json(ConfigKeyQuery {
key: "model-limits".to_string(),
is_secret: false,
}))
.await;

assert!(result.is_ok());
let response = match result.unwrap().0 {
ConfigValueResponse::Value(value) => value,
ConfigValueResponse::MaskedValue(_) => panic!("unexpected secret"),
};

let limits: Vec<goose::model::ModelLimitConfig> = serde_json::from_value(response).unwrap();
assert!(!limits.is_empty());

let gpt4_limit = limits.iter().find(|l| l.pattern == "gpt-4o");
assert!(gpt4_limit.is_some());
assert_eq!(gpt4_limit.unwrap().context_limit, 128_000);
}
}
mod tests {}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous config route tests were removed and replaced with an empty mod tests {} even though this PR adds a new /config/canonical-model-info endpoint; add coverage for at least the 200 path (known model) and 404 path (unknown model) to prevent regressions.

Suggested change
mod tests {}
mod tests {
use super::*;
use std::sync::Arc;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use serde_json::json;
use tower::ServiceExt; // for `oneshot`
fn test_app() -> Router {
// Construct a test instance of the app router with a default AppState.
let state = Arc::new(AppState::default());
routes(state)
}
#[tokio::test]
async fn canonical_model_info_known_model_returns_200() {
let app = test_app();
// Use a model name that is expected to be treated as a known/valid model.
let body = serde_json::to_vec(&json!({ "model": "known-model" })).unwrap();
let request = Request::builder()
.method("POST")
.uri("/config/canonical-model-info")
.header("content-type", "application/json")
.body(Body::from(body))
.unwrap();
let response = app.oneshot(request).await.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[tokio::test]
async fn canonical_model_info_unknown_model_returns_404() {
let app = test_app();
// Use a clearly invalid/unknown model name to trigger the 404 path.
let body = serde_json::to_vec(&json!({ "model": "this-model-should-not-exist" })).unwrap();
let request = Request::builder()
.method("POST")
.uri("/config/canonical-model-info")
.header("content-type", "application/json")
.body(Body::from(body))
.unwrap();
let response = app.oneshot(request).await.unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
}

Copilot uses AI. Check for mistakes.
1 change: 1 addition & 0 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,7 @@ impl Agent {
.ok_or_else(|| anyhow!("Could not configure agent: missing model"))?;
crate::model::ModelConfig::new(&model_name)
.map_err(|e| anyhow!("Could not configure agent: invalid model {}", e))?
.with_canonical_limits(&provider_name)
}
};

Expand Down
9 changes: 4 additions & 5 deletions crates/goose/src/agents/platform_extensions/summon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1331,11 +1331,10 @@ impl SummonClient {
.or_else(|| session.provider_name.clone())
.ok_or_else(|| anyhow::anyhow!("No provider configured"))?;

let mut model_config = session
.model_config
.clone()
.map(Ok)
.unwrap_or_else(|| crate::model::ModelConfig::new("default"))?;
let mut model_config = session.model_config.clone().map(Ok).unwrap_or_else(|| {
crate::model::ModelConfig::new("default")
.map(|c| c.with_canonical_limits(&provider_name))
})?;

if let Some(model) = &params.model {
model_config.model_name = model.clone();
Expand Down
Loading
Loading