custom provider model field `max_tokens` is silently ignored
- Dominant language
- Rust
- Stars
- 54.2k
- Forks
- 6.2k
- Avg merge
- 3d 4h
- Merged PRs (30d)
- 240
Description
## Summary
Declarative custom providers (`~/.config/goose/custom_providers/*.json`) define per-model entries that are parsed into `ModelInfo`. `ModelInfo` has no `max_tokens` field, and neither it nor `DeclarativeProviderConfig` uses `deny_unknown_fields` — so a user who writes `"max_tokens": 8192` in a model entry gets zero feedback and zero effect: serde's default "ignore unknown fields" behavior silently drops it.
## To Reproduce
1. Create `~/.config/goose/custom_providers/test.json`:
```json
{
"name": "test",
"engine": "openai",
"display_name": "Test",
"description": "test provider",
"api_key_env": "TEST_API_KEY",
"base_url": "http://localhost:8080/v1/chat/completions",
"models": [
{ "name": "my-model", "context_limit": 32768, "max_tokens": 8192 }
],
"requires_auth": false
}
```
2. Run a session against a self-hosted llama.cpp server via this custom OpenAI-compatible provider and inspect the outgoing request body.
3. The request contains no `max_tokens`/`max_completion_tokens` honoring the declared 8192, and goose never reports that the field was ignored.
## Expected vs actual
- **Expected:** the declared value is honored, or goose rejects/warns about the unsupported field at load time.
- **Actual:** the field is silently ignored. `ModelConfig.max_tokens` is only ever populated from `GOOSE_MAX_TOKENS` or the canonical model registry, so the per-model declaration has no effect.
## Code pointers (origin/main)
- `ModelInfo` — no `max_tokens` field, no `deny_unknown_fields`: `crates/goose-provider-types/src/base.rs:239`
- Declarative config parses `models: Vec`: `crates/goose-providers/src/declarative.rs:120`
- Request builder only emits the cap from `model_config.max_tokens`: `crates/goose-provider-types/src/formats/openai.rs:1710`
- The only sources for `ModelConfig.max_tokens`: `GOOSE_MAX_TOKENS` (`crates/goose/src/config/base.rs:1155`) and the canonical fill (`crates/goose-provider-types/src/model.rs:118`)
## Possible fixes (no preference)
- Support `max_tokens` in `ModelInfo` and map it into `ModelConfig`, or
- add `#[serde(deny_unknown_fields)]` to the declarative provider structs so typos/unsupported fields fail fast, or
- log a warning listing ignored unknown fields when loading custom provider files.
Observed on goose 1.45.0; verified still present on `main` (e20cb8787).
Related: #11049 — its repro also declares a per-model `"max_tokens": 4096` that is not applied, but that issue tracks `context_limit` precedence in the inventory path; the schema/unknown-field gap reported here is distinct and would remain after that fix.
Contributor guide
Research direction
The issue is in the declarative provider parsing. Start by examining the ModelInfo struct in crates/goose-provider-types/src/base.rs and the parsing in crates/goose-providers/src/declarative.rs. Understand how the max_tokens field should be added to ModelInfo and then mapped into ModelConfig. Check the request builder in crates/goose-provider-types/src/formats/openai.rs to see where the cap is applied. Done looks like the field being respected in the outgoing request or a clear error/warning if it's unsupported.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- ai-infra-agents, devtools
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100