Allow zero-config models - use provider default

Users can now omit the models section entirely from agent config.
The agent will automatically use the default model from an available provider.

Changes:
- Agent.get_model(): Falls back to provider name ("ollama" or "openai")
- ProviderRegistry.resolve_provider(): Handles bare provider names
- OpenAiProvider.parse_model(): Uses default_model when spec is "openai"
- OllamaProvider.parse_model(): Uses default_model when spec is "ollama"

Example minimal config (no models needed):
[[providers.ollama]]
base_url = "http://localhost:11434"
default_model = "llama3"

[agents.default]
description = "Default agent"
system_prompt = "You are helpful."
tools = ["file", "shell", "git"]
# No [agents.default.models] needed!

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-03-19 15:08:07 +01:00
parent aa778579ee
commit b52d82dec5
4 changed files with 31 additions and 13 deletions

View File

@@ -80,19 +80,20 @@ impl Agent {
if let Some(model) = self.config.get_model_for_task(task_type) {
return Ok(model);
}
// Fallback: use a reasonable default based on available providers
// Prefer ollama for local-first, then openai
match task_type {
TaskType::Code | TaskType::Planning | TaskType::ToolUse => {
// For complex tasks, try openai first
Ok("openai/gpt-4o".to_string())
}
TaskType::Chat | TaskType::Default => {
// For chat and default, use ollama
Ok("ollama/llama3".to_string())
}
// Fallback: use the default model from any available provider
// Prefer ollama for local-first usage
if self.provider_registry.get("ollama").is_some() {
return Ok("ollama".to_string());
}
if self.provider_registry.get("openai").is_some() {
return Ok("openai".to_string());
}
// No providers available
Err(AgentError::ConfigError(
"No model configured and no providers available".to_string()
))
}
/// Get available tools as provider Tool definitions

View File

@@ -46,7 +46,11 @@ impl OllamaProvider {
// Handle "ollama/model-name" format
if let Some(stripped) = model_spec.strip_prefix("ollama/") {
stripped.to_string()
} else if model_spec == "ollama" {
// Just provider name - use default model
self.default_model.clone()
} else {
// Use as-is (could be a model name)
model_spec.to_string()
}
}

View File

@@ -27,7 +27,11 @@ impl OpenAiProvider {
// Handle "openai/model-name" format
if let Some(stripped) = model_spec.strip_prefix("openai/") {
stripped.to_string()
} else if model_spec == "openai" {
// Just provider name - use default model
self.default_model.clone()
} else {
// Use as-is (could be a model name)
model_spec.to_string()
}
}

View File

@@ -103,12 +103,21 @@ impl ProviderRegistry {
}
/// Resolve a model specification to a provider
/// Format: "provider/model-name" or just "model-name" (uses default)
/// Format:
/// - "provider/model-name" - use specific model
/// - "provider" - use provider's default model
/// - "model-name" - try to find provider with this model as default
pub fn resolve_provider(&self, model_spec: &str) -> Result<(Arc<dyn Provider>, String)> {
if let Some((provider_name, model_name)) = model_spec.split_once('/') {
let provider = self.get_or_error(provider_name)?;
Ok((provider, model_name.to_string()))
} else {
// Check if it's just a provider name (no model specified)
// In this case, use the provider's default model
if let Some(provider) = self.get(model_spec) {
return Ok((provider, model_spec.to_string()));
}
// Try to find a provider that has this model as default
// Prefer Ollama for local-first
if let Some(provider) = self.get("ollama") {