170 lines
5.3 KiB
Rust
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(())
|
|
} |