10 KiB
Provider and Model Configuration Implementation Plan
Goals
- Add
~/.cass/providers.jsonas the source of truth for provider definitions. - Add
~/.cass/models.jsonfor optional model metadata such as context length and max output tokens. - Support API keys as either literal strings or environment-variable references in the form
"$PROVIDER_API_KEY". - Document the config files well enough that a user can manually edit them or ask Cass, in full-access mode, to add/update providers and models by reading the bundled docs.
- Add
cass checkto validate config JSON syntax, schema, references, and basic operational readiness.
Proposed file layout
Cass-managed/user-editable files under ~/.cass:
config.json: user preferences and default provider/model references only (no provider connection details or model metadata).providers.json: provider registry.models.json: model metadata registry.docs/: bundled read-only docs installed on startup.
Keep config.json for user preferences, but move provider connection details out of it. Continue to accept the existing provider, model, base_url, and api_key_env fields as a backward-compatible legacy path, with docs steering users to default_provider/default_model plus the new registry files.
Proposed schemas
config.json
{
"default_model": "accounts/fireworks/models/qwen3p7-plus",
"default_access_mode": "read-only",
"context_message_limit": 80,
"model_tool_result_limit": 24000,
"ui_tool_result_limit": 4000
}
Backward-compatible deprecated fields to continue accepting for now:
{
"base_url": "https://api.fireworks.ai/inference/v1",
"api_key_env": "FIREWORKS_API_KEY"
}
providers.json
Use an array to make manual edits straightforward and preserve room for provider-specific fields.
{
"providers": [
{
"id": "fireworks",
"name": "Fireworks",
"kind": "openai-compatible",
"base_url": "https://api.fireworks.ai/inference/v1",
"api_key": "$FIREWORKS_API_KEY",
"default_model": "accounts/fireworks/models/qwen3p7-plus",
"models": [
"accounts/fireworks/models/qwen3p7-plus"
]
}
]
}
Initial provider fields:
idrequired, unique stable identifier referenced by config defaults and models.nameoptional display name.kindrequired; initially only"openai-compatible"is supported.base_urlrequired foropenai-compatible.api_keyrequired string. If it starts with$, resolve the remaining text as an environment variable name. Otherwise use it as a literal API key.default_modeloptional model to use whenconfig.jsonomitsmodel.modelsoptional list of model ids associated with the provider.
models.json
{
"models": [
{
"id": "accounts/fireworks/models/qwen3p7-plus",
"provider": "fireworks",
"display_name": "Qwen 3p7 Plus",
"context_length": 262144,
"max_output_tokens": 32768,
"supports_tools": true,
"supports_streaming": true
}
]
}
Initial model fields:
idrequired model identifier sent to the provider.providerrequired provider id. Validate that it exists inproviders.json.display_nameoptional human-friendly name.context_lengthoptional positive integer.max_output_tokensoptional positive integer.supports_toolsoptional boolean, defaults totrue.supports_streamingoptional boolean, defaults totrue.
Deduplicate models by (provider, id).
Runtime behavior
- On startup, ensure
~/.cassexists as today. - If
providers.jsonis missing, create a default file containing the current Fireworks provider with"api_key": "$FIREWORKS_API_KEY". - If
models.jsonis missing, create a default file containing metadata for the current default Fireworks model. - Load
config.json,providers.json, andmodels.json. - Resolve the active model:
- CLI
--modeloverrides everything. - Else
config.default_model(or legacyconfig.model). - Else selected provider
default_model. - Else current built-in default.
- CLI
- Resolve the active provider:
- Prefer
config.default_providerwhen present. - Else infer from the selected model when
models.jsonor provider model lists identify exactly one provider. - Else use the Fireworks default provider when available.
- If legacy
base_url/api_key_envare present and no registry provider is selected, synthesize a legacyopenai-compatibleprovider for backward compatibility. - Otherwise fail with a clear config error that suggests running
cass check.
- Prefer
- Resolve API key:
"$NAME"means read env varNAME.- Empty env var names are invalid.
- Literal strings are passed through unchanged.
- Never print literal API key values in errors or check output.
- Construct
OpenAiCompatibleProviderfrom the resolved provider settings instead of rawmodel/base_url/api_key_envfields.
CLI changes
Refactor src/cli.rs to support subcommands while preserving existing invocation forms:
cass [--model MODEL] [--base-url URL] [--api-key-env ENV] [--cwd PATH]
cass --resume <chat-id>
cass --resume
cass check
Implementation sketch:
- Add
Command::Checkas an optional subcommand. - Keep existing top-level flags for chat mode.
- In
app::run, parse config, then if command isCheck, run config checks and exit without entering the TUI. - Exit code
0when checks pass, non-zero when any error exists.
cass check behavior
Initial check scope: config files only.
Checks:
config.json,providers.json, andmodels.jsonparse as valid JSON when present.- Files match expected schema/types and reject unknown required shapes.
- Required provider fields are present.
- Provider ids are unique.
- Provider
kindis supported. openai-compatibleproviders have validbase_urlandapi_keystrings.$ENV_VARAPI key references have non-empty names.- Active provider can be resolved from
default_provider, selected model metadata, provider model lists, or a valid legacy fallback. - Provider
default_modelandmodelsentries can be matched againstmodels.jsonwhen metadata exists. - Model ids are unique within their provider scope and every model has a provider.
- Model numeric metadata is positive.
- Model
providerreferences exist. - Active provider API key resolves. For inactive providers, missing env vars should be warnings, not errors.
Output format example:
Cass config check
✓ ~/.cass/config.json: valid
✓ ~/.cass/providers.json: valid (1 provider)
✓ ~/.cass/models.json: valid (1 model)
✓ active provider: fireworks
✓ active model: accounts/fireworks/models/qwen3p7-plus
✓ api key: FIREWORKS_API_KEY is set
All checks passed.
On errors, print each error with the file path and JSON path when possible.
Code changes
Config loading
- Extend
src/config.rsor split intosrc/config/modules if it becomes large. - Add structs:
ProvidersFileProviderDefinitionModelsFileModelDefinitionResolvedProviderConfigResolvedModelMetadata
- Add helper functions:
load_or_create_default_provider_registry(root)load_or_create_default_model_registry(root)resolve_api_key(spec: &str) -> Result<String>redact_api_key_for_display(spec: &str) -> Stringvalidate_config_files(root, cli_overrides) -> CheckReport
- Update
Configto include resolved provider details, while keeping old fields during migration if needed.
Provider construction
- Change
OpenAiCompatibleProvider::newto accept a resolved settings struct:
pub struct OpenAiCompatibleSettings {
pub model: String,
pub base_url: String,
pub api_key: String,
}
- Update
agent::run_turnto create the provider fromsettings.config.resolved_provider. - Keep current request body behavior initially. Store model metadata for future context-management and output-token use.
Check command
- Add a
src/check.rsmodule for report types and rendering. CheckReportshould hold errors and warnings separately.cass checkshould not start the TUI or create a conversation.- Prefer deterministic output for tests.
Documentation
Add bundled docs:
docs/configuration.md: full schema examples, env-var API key behavior, manual editing steps, and examples prompts for asking Cass to add a provider/model.- Update
docs/README.mdto link toconfiguration.md. - Update top-level
README.mdConfigure and Usage sections.
Include a user-facing example:
Run cass in full-access mode and ask:
"Read ~/.cass/docs/configuration.md, then add an OpenAI-compatible provider named Together using TOGETHER_API_KEY and add model metadata for meta-llama/Llama-3.1-70B-Instruct-Turbo."
Tests
Add tests for:
- Default
providers.jsonandmodels.jsoncreation in a temp Cass root. - Loading current legacy
config.jsonwithbase_urlandapi_key_envstill works. $ENV_VARAPI key resolution succeeds when set and fails clearly when missing.- Literal API key strings are accepted and never appear in check output.
- Duplicate provider ids fail validation.
- Invalid JSON syntax fails validation with file path.
- Invalid provider/model references fail validation.
cass checkreturns success for defaults and failure for invalid files.- Docs install still includes the new configuration document.
Migration/backward compatibility
- Do not break existing users with only
~/.cass/config.json. - Continue honoring
--base-urland--api-key-env; internally convert them to provider overrides for the session. - Mark
base_urlandapi_key_envas deprecated in docs, but do not remove them yet. - If both
providers.jsonand legacy connection fields are present,providers.jsonwins unless CLI overrides are used.
Suggested implementation order
- Add registry structs, defaults, loading, API-key resolution, and validation helpers.
- Add
cass checkCLI plumbing and report output. - Wire resolved provider settings into
OpenAiCompatibleProviderandagent::run_turn. - Bootstrap missing
providers.jsonandmodels.jsonwith defaults. - Add docs and README updates.
- Add/adjust tests.
- Run
cargo fmt,cargo test, andcass check.