upgrade Skill system auf regitry

This commit is contained in:
Sithies
2026-03-17 21:51:00 +01:00
parent 389c759166
commit 4e6b2c6759
28 changed files with 534 additions and 478 deletions
+3
View File
@@ -8,6 +8,9 @@ edition = "2024"
sebas-tian = { path = "../sebas-tian" }
lyra = { path = "../lyra" }
# Kern
nazarick-core = { path = "../nazarick-core" }
# Skills
skills = { path = "../skills" }
+14 -6
View File
@@ -234,15 +234,23 @@ fn split_message(text: &str) -> Vec<String> {
let mut remaining = text;
while remaining.len() > MAX_CHUNK_SIZE {
// Schnittpunkt suchen — bevorzugt Zeilenumbruch, dann Leerzeichen
let cut = remaining[..MAX_CHUNK_SIZE]
// Sicherstellen dass wir auf einer char-Grenze starten
let safe_max = {
let mut idx = MAX_CHUNK_SIZE;
while !remaining.is_char_boundary(idx) {
idx -= 1;
}
idx
};
let cut = remaining[..safe_max]
.rfind('\n')
.or_else(|| remaining[..MAX_CHUNK_SIZE].rfind(". "))
.or_else(|| remaining[..MAX_CHUNK_SIZE].rfind(' '))
.unwrap_or(MAX_CHUNK_SIZE);
.or_else(|| remaining[..safe_max].rfind(". "))
.or_else(|| remaining[..safe_max].rfind(' '))
.unwrap_or(safe_max);
chunks.push(remaining[..cut].trim().to_string());
remaining = remaining[cut..].trim_start();
remaining = &remaining[cut..].trim_start();
}
if !remaining.is_empty() {
+2 -9
View File
@@ -1,25 +1,18 @@
// crates/nazarick/src/chat/types.rs
//
// Gemeinsame Typen für alle Chat-Kanäle.
use serde::Deserialize;
// ─── Auth ────────────────────────────────────────────────────────────────────
#[derive(Debug)]
pub enum AuthResult {
Allowed,
Denied { user_id: u64, username: String },
}
// ─── Agent-Config ─────────────────────────────────────────────────────────────
//
// Wird aus config.toml geladen.
// Jeder Agent hat seinen eigenen Bot-Token und Webhook-URL.
#[derive(Debug, Deserialize, Clone)]
pub struct AgentChatConfig {
pub agent_id: String,
pub max_tokens: u32,
pub max_loops: u32,
pub bot_token: String,
pub incoming_webhook_url: String,
pub allowed_user_ids: Vec<u64>,
-14
View File
@@ -3,33 +3,19 @@
use serde::Deserialize;
use crate::chat::types::AgentChatConfig;
/// Wurzel der gesamten Nazarick-Konfiguration.
/// Entspricht dem obersten Level in config.toml.
#[derive(Debug, Deserialize)]
pub struct NazarickConfig {
/// Alles unter [chat] in config.toml
pub chat: ChatConfig,
}
/// Konfiguration für den Chat-Connector.
/// Entspricht dem [chat]-Block in config.toml.
#[derive(Debug, Deserialize)]
pub struct ChatConfig {
/// Port auf dem Nazarick auf eingehende Webhooks lauscht
pub listen_port: u16,
/// Synology User-ID des Admins — bekommt System-Benachrichtigungen
pub admin_user_id: u64,
/// Basis Webhook URL für Admin-Benachrichtigungen — ohne user_ids Parameter
pub admin_webhook_url: String,
/// Liste aller konfigurierten Bot-Agenten
pub agents: Vec<AgentChatConfig>,
}
/// Lädt die Konfiguration aus config.toml im Arbeitsverzeichnis.
/// Gibt einen Fehler zurück wenn die Datei fehlt oder ungültig ist.
pub fn load() -> anyhow::Result<NazarickConfig> {
let content = std::fs::read_to_string("config/config.toml")?;
let config = toml::from_str(&content)?;
+24 -26
View File
@@ -1,8 +1,3 @@
// crates/nazarick/src/main.rs
//
// Nazarick — Einstiegspunkt.
// Initialisiert alle Komponenten und startet den HTTP-Server.
mod chat;
mod config;
@@ -14,68 +9,73 @@ use tower_http::trace::TraceLayer;
use tracing::info;
use api::llm::lmstudio::LmStudioProvider;
use nazarick_core::agent::skill_registry::SkillRegistry;
use sebas_tian::Sebas;
use lyra::Lyra;
use skills::personality::PersonalitySkill;
use chat::synology::{handle_incoming, AppState};
use skills as _;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Logging initialisieren
tracing_subscriber::fmt()
.with_env_filter("nazarick=info,tower_http=debug,api=debug")
.init();
info!("Nazarick erwacht...");
// Arbeitsverzeichnis auf Workspace-Root setzen
// Damit relative Pfade wie "config/shared_core.md" immer funktionieren
let exe_path = std::env::current_exe()?;
let workspace_root = exe_path
.parent() // debug/
.and_then(|p| p.parent()) // target/
.and_then(|p| p.parent()) // workspace root
.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());
// Config laden
let cfg = config::load().map_err(|e| {
eprintln!("Config Fehler: {}", e);
e
})?;
let port = cfg.chat.listen_port;
// Sebas Tian — Butler Agent
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 = Sebas::new(
"sebas_tian",
"config/shared_core.md",
"crates/sebas-tian/config/soul_core.md",
"crates/sebas-tian/config/soul_personality.md",
Box::new(LmStudioProvider::new(
"http://localhost:1234",
"qwen/qwen3.5-9b",
)),
Arc::new(PersonalitySkill::new(
"crates/sebas-tian/config/soul_personality.md",
)),
registry.clone(),
sebas_cfg.max_tokens,
sebas_cfg.max_loops,
);
// Lyra — Companion Agent
let lyra = Lyra::new(
"lyra",
"config/shared_core.md",
"crates/lyra/config/soul_core.md",
"crates/lyra/config/soul_personality.md",
Box::new(LmStudioProvider::new(
"http://localhost:1234",
"qwen/qwen3.5-9b",
)),
Arc::new(PersonalitySkill::new(
"crates/lyra/config/soul_personality.md",
)),
registry.clone(),
lyra_cfg.max_tokens,
lyra_cfg.max_loops,
);
// Shared State aufbauen
let state = Arc::new(AppState {
agents: cfg.chat.agents,
admin_user_id: cfg.chat.admin_user_id,
@@ -85,13 +85,11 @@ async fn main() -> anyhow::Result<()> {
lyra: Mutex::new(lyra),
});
// Routes registrieren
let app = Router::new()
.route("/chat/synology", post(handle_incoming))
.with_state(state)
.layer(TraceLayer::new_for_http());
// Server starten
let addr = format!("0.0.0.0:{}", port);
let listener = tokio::net::TcpListener::bind(&addr).await?;