Files
nazarick-private/crates/nazarick/src/main.rs
T
Sithies b6a5618f78
CI / check (push) Successful in 3m13s
CI / test (push) Successful in 3m53s
CI / clippy (push) Failing after 3m15s
CI / deploy (push) Has been skipped
Fix
2026-04-25 18:59:24 +02:00

170 lines
5.3 KiB
Rust

// crates/nazarick/src/main.rs
mod chat;
mod config;
use std::sync::Arc;
use axum::{routing::post, Router};
use reqwest::Client;
use tokio::sync::Mutex;
use tower_http::trace::TraceLayer;
use tracing::info;
use nazarick_core::agent::base::AgentConfig;
use nazarick_core::agent::skill_registry::SkillRegistry;
use nazarick_core::llm::{LlmProvider, SkillFormat};
use api::llm::openai_compat::OpenAiCompatProvider;
use nazarick_core::memory::Memory;
use nazarick_core::summarizer::Summarizer;
use memory::store::MemoryStore;
use memory::summarizer::Summarizer as MemorySummarizer;
use sebas_tian::Sebas;
use lyra::Lyra;
use chat::synology::{handle_incoming, AppState};
use config::ModelConfig;
use skills as _;
fn build_provider(model_cfg: &ModelConfig) -> Box<dyn LlmProvider> {
let skill_format = model_cfg.skill_format
.as_deref()
.map(|s| s.parse::<SkillFormat>().unwrap_or(SkillFormat::Xml))
.unwrap_or(SkillFormat::Xml);
match model_cfg.provider.as_str() {
"openai_compat" => Box::new(OpenAiCompatProvider::new(
&model_cfg.url,
&model_cfg.model,
model_cfg.api_key.clone(),
skill_format,
)),
unknown => panic!("Unbekannter Provider: '{}'", unknown),
}
}
async fn build_memory(agent_id: &str) -> anyhow::Result<Arc<dyn Memory>> {
let store = MemoryStore::open(agent_id).await?;
Ok(Arc::new(store))
}
fn build_summarizer(model_cfg: &ModelConfig) -> Arc<dyn Summarizer> {
Arc::new(MemorySummarizer::new(
&model_cfg.url,
&model_cfg.model,
model_cfg.max_summary_tokens.unwrap_or(4000),
))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter("nazarick=info,tower_http=debug,api=debug")
.init();
info!("Nazarick erwacht...");
let exe_path = std::env::current_exe()?;
let workspace_root = exe_path
.parent()
.and_then(|p| p.parent())
.and_then(|p| p.parent())
.ok_or_else(|| anyhow::anyhow!("Workspace-Root nicht gefunden"))?;
std::env::set_current_dir(workspace_root)?;
info!("Arbeitsverzeichnis: {}", workspace_root.display());
let cfg = config::load().map_err(|e| {
eprintln!("Config Fehler: {}", e);
e
})?;
let port = cfg.chat.listen_port;
let registry = Arc::new(SkillRegistry::collect());
info!("Skills geladen: {:?}", registry.all_names());
let sebas_cfg = cfg.chat.agents.iter()
.find(|a| a.agent_id == "sebas_tian")
.ok_or_else(|| anyhow::anyhow!("sebas_tian nicht in config"))?;
let lyra_cfg = cfg.chat.agents.iter()
.find(|a| a.agent_id == "lyra")
.ok_or_else(|| anyhow::anyhow!("lyra nicht in config"))?;
let sebas_model = cfg.models
.get(&sebas_cfg.model)
.ok_or_else(|| anyhow::anyhow!("Modell '{}' nicht in [models] config", sebas_cfg.model))?;
let lyra_model = cfg.models
.get(&lyra_cfg.model)
.ok_or_else(|| anyhow::anyhow!("Modell '{}' nicht in [models] config", lyra_cfg.model))?;
let summary_model = cfg.models
.get("summary")
.ok_or_else(|| anyhow::anyhow!("'summary' nicht in [models] config"))?;
let sebas_memory = build_memory("sebas_tian").await?;
let lyra_memory = build_memory("lyra").await?;
let summarizer = build_summarizer(summary_model);
info!("Memory geladen");
let mut sebas = Sebas::new(
AgentConfig {
agent_id: "sebas_tian".to_string(),
shared_core_path: "config/shared_core.md".to_string(),
soul_core_path: "crates/sebas-tian/config/soul_core.md".to_string(),
max_tokens: sebas_cfg.max_tokens,
max_loops: sebas_cfg.max_loops,
history_window: sebas_cfg.history_window,
summary_every: sebas_cfg.summary_every,
conversation_timeout_mins: sebas_cfg.conversation_timeout_mins,
},
build_provider(sebas_model),
registry.clone(),
sebas_memory,
summarizer.clone(),
);
sebas.init().await?;
let mut lyra = Lyra::new(
AgentConfig {
agent_id: "lyra".to_string(),
shared_core_path: "config/shared_core.md".to_string(),
soul_core_path: "crates/lyra/config/soul_core.md".to_string(),
max_tokens: lyra_cfg.max_tokens,
max_loops: lyra_cfg.max_loops,
history_window: lyra_cfg.history_window,
summary_every: lyra_cfg.summary_every,
conversation_timeout_mins: lyra_cfg.conversation_timeout_mins,
},
build_provider(lyra_model),
registry.clone(),
lyra_memory,
summarizer.clone(),
);
lyra.init().await?;
info!("Agenten initialisiert");
let state = Arc::new(AppState {
agents: cfg.chat.agents,
admin_user_id: cfg.chat.admin_user_id,
admin_webhook_url: cfg.chat.admin_webhook_url,
http: Client::new(),
sebas: Mutex::new(sebas),
lyra: Mutex::new(lyra),
});
let app = Router::new()
.route("/chat/synology", post(handle_incoming))
.with_state(state)
.layer(TraceLayer::new_for_http());
let addr = format!("0.0.0.0:{}", port);
let listener = tokio::net::TcpListener::bind(&addr).await?;
info!("Lausche auf {}", addr);
axum::serve(listener, app).await?;
Ok(())
}