default resilience

This commit is contained in:
jaberjaber23
2026-03-05 22:57:08 +03:00
parent 45e06b9bad
commit ebcdc17c13
10 changed files with 240 additions and 29 deletions
+62
View File
@@ -0,0 +1,62 @@
name: Bug Report
description: Report a bug or unexpected behavior
labels: ["bug"]
body:
- type: textarea
id: description
attributes:
label: Description
description: What happened?
placeholder: Describe the bug clearly and concisely.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What did you expect to happen?
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: How can we reproduce this?
placeholder: |
1. Run `openfang start`
2. Open dashboard at http://localhost:4200
3. Click ...
validations:
required: true
- type: input
id: version
attributes:
label: OpenFang Version
description: Output of `openfang -V`
placeholder: "0.3.23"
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating System
options:
- Linux (x86_64)
- Linux (aarch64/ARM64)
- macOS (Apple Silicon)
- macOS (Intel)
- Windows
- Android (Termux)
- Other
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs / Screenshots
description: Paste relevant logs or attach screenshots.
@@ -0,0 +1,24 @@
name: Feature Request
description: Suggest a new feature or improvement
labels: ["enhancement"]
body:
- type: textarea
id: description
attributes:
label: Description
description: What feature would you like?
placeholder: Describe the feature and why it would be useful.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Have you tried any workarounds?
- type: textarea
id: context
attributes:
label: Additional Context
description: Any other context, screenshots, or references.
+17
View File
@@ -0,0 +1,17 @@
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
labels:
- "dependencies"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 3
labels:
- "ci"
+19
View File
@@ -0,0 +1,19 @@
## Summary
<!-- What does this PR do? Link related issues with "Fixes #123". -->
## Changes
<!-- Brief list of what changed. -->
## Testing
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` passes
- [ ] `cargo test --workspace` passes
- [ ] Live integration tested (if applicable)
## Security
- [ ] No new unsafe code
- [ ] No secrets or API keys in diff
- [ ] User input validated at boundaries
Generated
+14 -14
View File
@@ -3866,7 +3866,7 @@ dependencies = [
[[package]]
name = "openfang-api"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"async-trait",
"axum",
@@ -3903,7 +3903,7 @@ dependencies = [
[[package]]
name = "openfang-channels"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"async-trait",
"axum",
@@ -3934,7 +3934,7 @@ dependencies = [
[[package]]
name = "openfang-cli"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"clap",
"clap_complete",
@@ -3961,7 +3961,7 @@ dependencies = [
[[package]]
name = "openfang-desktop"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"axum",
"open",
@@ -3987,7 +3987,7 @@ dependencies = [
[[package]]
name = "openfang-extensions"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"aes-gcm",
"argon2",
@@ -4015,7 +4015,7 @@ dependencies = [
[[package]]
name = "openfang-hands"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"chrono",
"dashmap",
@@ -4032,7 +4032,7 @@ dependencies = [
[[package]]
name = "openfang-kernel"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"async-trait",
"chrono",
@@ -4068,7 +4068,7 @@ dependencies = [
[[package]]
name = "openfang-memory"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"async-trait",
"chrono",
@@ -4087,7 +4087,7 @@ dependencies = [
[[package]]
name = "openfang-migrate"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"chrono",
"dirs 6.0.0",
@@ -4106,7 +4106,7 @@ dependencies = [
[[package]]
name = "openfang-runtime"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"anyhow",
"async-trait",
@@ -4138,7 +4138,7 @@ dependencies = [
[[package]]
name = "openfang-skills"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"chrono",
"hex",
@@ -4161,7 +4161,7 @@ dependencies = [
[[package]]
name = "openfang-types"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"async-trait",
"chrono",
@@ -4180,7 +4180,7 @@ dependencies = [
[[package]]
name = "openfang-wire"
version = "0.3.22"
version = "0.3.23"
dependencies = [
"async-trait",
"chrono",
@@ -8802,7 +8802,7 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "xtask"
version = "0.3.22"
version = "0.3.23"
[[package]]
name = "yoke"
+1 -1
View File
@@ -18,7 +18,7 @@ members = [
]
[workspace.package]
version = "0.3.23"
version = "0.3.24"
edition = "2021"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/RightNow-AI/openfang"
+8 -5
View File
@@ -2075,20 +2075,23 @@ fn cmd_doctor(json: bool, repair: bool) {
}
let answer = prompt_input(" Create default config? [Y/n] ");
if answer.is_empty() || answer.starts_with('y') || answer.starts_with('Y') {
let default_config = r#"# OpenFang Agent OS configuration
let (provider, api_key_env, model) = detect_best_provider();
let default_config = format!(
r#"# OpenFang Agent OS configuration
# See https://github.com/RightNow-AI/openfang for documentation
# For Docker, change to "0.0.0.0:4200" or set OPENFANG_LISTEN env var.
api_listen = "127.0.0.1:4200"
[default_model]
provider = "groq"
model = "llama-3.3-70b-versatile"
api_key_env = "GROQ_API_KEY"
provider = "{provider}"
model = "{model}"
api_key_env = "{api_key_env}"
[memory]
decay_rate = 0.05
"#;
"#
);
let _ = std::fs::create_dir_all(&openfang_dir);
if std::fs::write(&config_path, default_config).is_ok() {
restrict_file_permissions(&config_path);
+12 -5
View File
@@ -1027,24 +1027,31 @@ impl OpenFangKernel {
&mut restored_entry.manifest.resources,
);
// Apply default_model to restored agents (same logic as spawn)
// Apply default_model to restored agents.
//
// Two cases:
// 1. Agent has empty/default provider → always apply default_model
// 2. Agent named "assistant" (auto-spawned) → update to match
// default_model so config.toml changes take effect on restart
{
let dm = &kernel.config.default_model;
let is_default_provider = restored_entry.manifest.model.provider.is_empty()
|| restored_entry.manifest.model.provider == "default";
let is_default_model = restored_entry.manifest.model.model.is_empty()
|| restored_entry.manifest.model.model == "default";
if is_default_provider && is_default_model {
let dm = &kernel.config.default_model;
let is_auto_spawned = restored_entry.name == "assistant"
&& restored_entry.manifest.description == "General-purpose assistant";
if is_default_provider && is_default_model || is_auto_spawned {
if !dm.provider.is_empty() {
restored_entry.manifest.model.provider = dm.provider.clone();
}
if !dm.model.is_empty() {
restored_entry.manifest.model.model = dm.model.clone();
}
if !dm.api_key_env.is_empty() && restored_entry.manifest.model.api_key_env.is_none() {
if !dm.api_key_env.is_empty() {
restored_entry.manifest.model.api_key_env = Some(dm.api_key_env.clone());
}
if dm.base_url.is_some() && restored_entry.manifest.model.base_url.is_none() {
if dm.base_url.is_some() {
restored_entry.manifest.model.base_url.clone_from(&dm.base_url);
}
}
+4 -3
View File
@@ -54,10 +54,11 @@ impl SetupWizard {
/// model configuration, capabilities, and schedule.
pub fn build_plan(intent: AgentIntent) -> SetupPlan {
// Map model tier to provider/model
// Use "default" so the kernel applies config.toml's [default_model].
// Only "complex" tier gets an explicit Anthropic override.
let (provider, model) = match intent.model_tier.as_str() {
"simple" => ("groq", "llama-3.3-70b-versatile"),
"complex" => ("anthropic", "claude-sonnet-4-20250514"),
_ => ("groq", "llama-3.3-70b-versatile"), // medium default
_ => ("default", "default"),
};
// Build capabilities from intent
@@ -285,7 +286,7 @@ mod tests {
let plan = SetupWizard::build_plan(intent);
assert_eq!(plan.manifest.name, "research-bot");
assert_eq!(plan.manifest.model.provider, "groq");
assert_eq!(plan.manifest.model.provider, "default");
assert!(plan
.manifest
.capabilities
+79 -1
View File
@@ -26,9 +26,17 @@ impl ModelCatalog {
/// Create a new catalog populated with builtin models and providers.
pub fn new() -> Self {
let models = builtin_models();
let aliases = builtin_aliases();
let mut aliases = builtin_aliases();
let mut providers = builtin_providers();
// Auto-register aliases defined on model entries
for model in &models {
for alias in &model.aliases {
let lower = alias.to_lowercase();
aliases.entry(lower).or_insert_with(|| model.id.clone());
}
}
// Set model counts on providers
for provider in &mut providers {
provider.model_count = models.iter().filter(|m| m.provider == provider.id).count();
@@ -2589,6 +2597,76 @@ fn builtin_models() -> Vec<ModelCatalogEntry> {
supports_streaming: true,
aliases: vec![],
},
ModelCatalogEntry {
id: "qwen3-235b-a22b".into(),
display_name: "Qwen3 235B".into(),
provider: "qwen".into(),
tier: ModelTier::Frontier,
context_window: 131_072,
max_output_tokens: 8_192,
input_cost_per_m: 4.00,
output_cost_per_m: 12.00,
supports_tools: true,
supports_vision: false,
supports_streaming: true,
aliases: vec!["qwen3".into()],
},
ModelCatalogEntry {
id: "qwen3-30b-a3b".into(),
display_name: "Qwen3 30B".into(),
provider: "qwen".into(),
tier: ModelTier::Fast,
context_window: 131_072,
max_output_tokens: 8_192,
input_cost_per_m: 0.30,
output_cost_per_m: 0.60,
supports_tools: true,
supports_vision: false,
supports_streaming: true,
aliases: vec![],
},
ModelCatalogEntry {
id: "qwen-coder-plus-latest".into(),
display_name: "Qwen Coder Plus (Latest)".into(),
provider: "qwen".into(),
tier: ModelTier::Smart,
context_window: 131_072,
max_output_tokens: 8_192,
input_cost_per_m: 0.80,
output_cost_per_m: 2.00,
supports_tools: true,
supports_vision: false,
supports_streaming: true,
aliases: vec!["qwen-coder".into()],
},
ModelCatalogEntry {
id: "qwen2.5-coder-32b-instruct".into(),
display_name: "Qwen 2.5 Coder 32B".into(),
provider: "qwen".into(),
tier: ModelTier::Balanced,
context_window: 131_072,
max_output_tokens: 8_192,
input_cost_per_m: 0.80,
output_cost_per_m: 2.00,
supports_tools: true,
supports_vision: false,
supports_streaming: true,
aliases: vec![],
},
ModelCatalogEntry {
id: "qwen-vl-max".into(),
display_name: "Qwen VL Max".into(),
provider: "qwen".into(),
tier: ModelTier::Frontier,
context_window: 32_768,
max_output_tokens: 8_192,
input_cost_per_m: 3.00,
output_cost_per_m: 9.00,
supports_tools: false,
supports_vision: true,
supports_streaming: true,
aliases: vec![],
},
// ══════════════════════════════════════════════════════════════
// MiniMax (4)
// ══════════════════════════════════════════════════════════════