From 30d63debd99ad883b2d971826d1f243b00f2436f Mon Sep 17 00:00:00 2001 From: Sithies Date: Mon, 16 Mar 2026 22:06:30 +0100 Subject: [PATCH] refactor: LLM Typen und Traits nach nazarick-core verschoben, BaseAgent extrahiert --- Cargo.lock | 1 + config.toml | 10 ++- crates/api/src/llm.rs | 5 +- crates/api/src/llm/lmstudio.rs | 2 +- crates/nazarick-core/Cargo.toml | 3 +- crates/nazarick-core/src/agent.rs | 65 +++++++++++++++++++ crates/nazarick-core/src/lib.rs | 4 +- crates/nazarick-core/src/llm/mod.rs | 10 +++ crates/nazarick-core/src/llm/traits.rs | 19 ++++++ .../src/llm/types.rs} | 16 ++--- crates/sebas-tian/src/lib.rs | 58 ++++------------- 11 files changed, 127 insertions(+), 66 deletions(-) create mode 100644 crates/nazarick-core/src/agent.rs create mode 100644 crates/nazarick-core/src/llm/mod.rs create mode 100644 crates/nazarick-core/src/llm/traits.rs rename crates/{api/src/llm/provider.rs => nazarick-core/src/llm/types.rs} (79%) diff --git a/Cargo.lock b/Cargo.lock index 72385c8..b11cb10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -760,6 +760,7 @@ dependencies = [ name = "nazarick-core" version = "0.1.0" dependencies = [ + "async-trait", "thiserror", "uuid", ] diff --git a/config.toml b/config.toml index a87abe0..0a26d7e 100644 --- a/config.toml +++ b/config.toml @@ -15,5 +15,13 @@ bot_token = "k1RMRh0NbcROtVlPbUg2GNgtGzb3AKmiHzgIt0E1VcmtWkZFAic7Sv6s incoming_webhook_url = "https://sithies-tb.de6.quickconnect.to/direct/webapi/entry.cgi?api=SYNO.Chat.External&method=chatbot&version=2&token=%22k1RMRh0NbcROtVlPbUg2GNgtGzb3AKmiHzgIt0E1VcmtWkZFAic7Sv6sS3ZPHO1D%22" allowed_user_ids = [5] +agent_id = "lyra" +bot_token = "e8Hg50YgD1YcfmfaKCr1B3lgAE3c2s8QyJOTXyfkPJulKzcqgqq7EBrT4MNw1gUy" +incoming_webhook_url = "https://sithies-tb.de6.quickconnect.to/direct/webapi/entry.cgi?api=SYNO.Chat.External&method=chatbot&version=2&token=%22e8Hg50YgD1YcfmfaKCr1B3lgAE3c2s8QyJOTXyfkPJulKzcqgqq7EBrT4MNw1gUy%22" +allowed_user_ids = [5] + [agents.sebas_tian] -# Sebas-spezifisches \ No newline at end of file +# Sebas-spezifisches + +[agents.lyra] +# Lyra-spezifisches \ No newline at end of file diff --git a/crates/api/src/llm.rs b/crates/api/src/llm.rs index 1dacb80..8c1c45a 100644 --- a/crates/api/src/llm.rs +++ b/crates/api/src/llm.rs @@ -1,7 +1,6 @@ /// Abstraktionsschicht für alle LLM-Provider. /// Neue Provider (Ollama, Mistral) werden hier als weitere Submodule ergänzt. -pub mod provider; pub mod lmstudio; -// Re-export der wichtigsten Typen damit Nutzer nur `api::llm::X` schreiben müssen -pub use provider::{LlmProvider, LlmRequest, LlmResponse, Message}; \ No newline at end of file +// Re-export aus nazarick-core damit bestehende Importe `api::llm::X` weiter funktionieren +pub use nazarick_core::llm::{LlmProvider, LlmRequest, LlmResponse, Message}; \ No newline at end of file diff --git a/crates/api/src/llm/lmstudio.rs b/crates/api/src/llm/lmstudio.rs index f1ae259..f1022a3 100644 --- a/crates/api/src/llm/lmstudio.rs +++ b/crates/api/src/llm/lmstudio.rs @@ -3,7 +3,7 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use nazarick_core::types::Result; use nazarick_core::error::NazarickError; -use crate::llm::provider::{LlmProvider, LlmRequest, LlmResponse, Message}; +use nazarick_core::llm::{LlmProvider, LlmRequest, LlmResponse, Message}; /// LM Studio Provider — für lokale Entwicklung auf dem Entwicklungsrechner. /// LM Studio emuliert die OpenAI Chat Completions API, daher nutzen diff --git a/crates/nazarick-core/Cargo.toml b/crates/nazarick-core/Cargo.toml index d905eb4..db64c82 100644 --- a/crates/nazarick-core/Cargo.toml +++ b/crates/nazarick-core/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] thiserror = "2.0.18" -uuid = { version = "1.22.0", features = ["v4"] } \ No newline at end of file +uuid = { version = "1.22.0", features = ["v4"] } +async-trait = "0.1.89" \ No newline at end of file diff --git a/crates/nazarick-core/src/agent.rs b/crates/nazarick-core/src/agent.rs new file mode 100644 index 0000000..898b53c --- /dev/null +++ b/crates/nazarick-core/src/agent.rs @@ -0,0 +1,65 @@ +// nazarick-core/src/agent.rs +// +// BaseAgent — gemeinsame Logik für alle Agenten. +// Sebas, Lyra und zukünftige Agenten sind nur noch dünne Wrapper darum. + +use crate::prompt::PromptBuilder; +use crate::types::{AgentId, Result}; +use crate::llm::{LlmProvider, LlmRequest, Message}; + +pub struct BaseAgent { + /// Eindeutige ID dieser Agent-Instanz + pub id: AgentId, + /// Baut den System-Prompt aus soul_core + soul_personality + prompt_builder: PromptBuilder, + /// Das LLM-Backend (LmStudio, Ollama, Mistral) + llm: Box, + /// Konversationsverlauf — damit der Agent den Kontext behält + history: Vec, +} + +impl BaseAgent { + /// Erstellt eine neue BaseAgent-Instanz. + /// Wird von jedem Agenten in seinem new() aufgerufen. + pub fn new( + soul_core_path: impl Into, + soul_personality_path: impl Into, + llm: Box, + ) -> Self { + Self { + id: AgentId::new_v4(), + prompt_builder: PromptBuilder::new(soul_core_path, soul_personality_path), + llm, + history: Vec::new(), + } + } + + /// Sendet eine Nachricht und gibt die Antwort zurück. + /// Konversationsverlauf wird automatisch mitgeführt. + pub async fn chat(&mut self, user_message: &str) -> Result { + let system_prompt = self.prompt_builder.build()?; + + self.history.push(Message::user(user_message)); + + let mut messages = vec![Message::system(system_prompt)]; + messages.extend(self.history.clone()); + + let request = LlmRequest { + messages, + max_tokens: 4096, + temperature: 0.7, + }; + + let response = self.llm.complete(request).await?; + + self.history.push(Message::assistant(&response.content)); + + Ok(response.content) + } + + /// Löscht den Konversationsverlauf. + /// Nützlich wenn ein neues Gespräch beginnen soll. + pub fn clear_history(&mut self) { + self.history.clear(); + } +} \ No newline at end of file diff --git a/crates/nazarick-core/src/lib.rs b/crates/nazarick-core/src/lib.rs index c4ffab1..ff17c25 100644 --- a/crates/nazarick-core/src/lib.rs +++ b/crates/nazarick-core/src/lib.rs @@ -9,4 +9,6 @@ pub mod usage; /// Persönlichkeit basiert auf zwei Dateien: /// - soul_core.md → unveränderlicher Kern (Regeln, Sicherheit) /// - soul_personality.md → entwickelbarer Teil (Ton, Präferenzen) -pub mod prompt; \ No newline at end of file +pub mod prompt; +pub mod llm; +pub mod agent; \ No newline at end of file diff --git a/crates/nazarick-core/src/llm/mod.rs b/crates/nazarick-core/src/llm/mod.rs new file mode 100644 index 0000000..c069ec7 --- /dev/null +++ b/crates/nazarick-core/src/llm/mod.rs @@ -0,0 +1,10 @@ +// nazarick-core/src/llm/mod.rs +// +// LLM-Modul — Typen und Traits für alle LLM-Provider. +// Re-exportiert alles damit Nutzer nur `nazarick_core::llm::X` schreiben müssen. + +mod types; +mod traits; + +pub use types::{Message, LlmRequest, LlmResponse}; +pub use traits::LlmProvider; \ No newline at end of file diff --git a/crates/nazarick-core/src/llm/traits.rs b/crates/nazarick-core/src/llm/traits.rs new file mode 100644 index 0000000..733ffc5 --- /dev/null +++ b/crates/nazarick-core/src/llm/traits.rs @@ -0,0 +1,19 @@ +// nazarick-core/src/llm/traits.rs +// +// LlmProvider Trait — gemeinsame Schnittstelle für alle LLM-Backends. +// Neue Provider (Ollama, Mistral) implementieren diesen Trait. + +use crate::types::Result; +use crate::llm::types::{LlmRequest, LlmResponse}; + +/// Zentraler Trait für alle LLM-Provider. +/// Jeder Provider (LmStudio, Ollama, Mistral) implementiert diesen Trait. +#[async_trait::async_trait] +pub trait LlmProvider: Send + Sync { + /// Sendet eine Anfrage an das LLM und gibt die Antwort zurück. + async fn complete(&self, request: LlmRequest) -> Result; + + /// Gibt den Namen des Providers zurück. + /// Wird für Logging und Usage-Tracking verwendet. + fn name(&self) -> &str; +} \ No newline at end of file diff --git a/crates/api/src/llm/provider.rs b/crates/nazarick-core/src/llm/types.rs similarity index 79% rename from crates/api/src/llm/provider.rs rename to crates/nazarick-core/src/llm/types.rs index b381b1a..89265f0 100644 --- a/crates/api/src/llm/provider.rs +++ b/crates/nazarick-core/src/llm/types.rs @@ -1,4 +1,7 @@ -use nazarick_core::types::Result; +// nazarick-core/src/llm/types.rs +// +// Gemeinsame Datentypen für alle LLM-Provider. +// Jeder Provider (LmStudio, Ollama, Mistral) nutzt diese Typen. /// Repräsentiert eine einzelne Nachricht in einem Gespräch. /// Entspricht dem Message-Format das alle gängigen LLM APIs verwenden. @@ -47,15 +50,4 @@ pub struct LlmResponse { pub tokens_input: u64, /// Anzahl der Output-Token (für Usage-Tracking) pub tokens_output: u64, -} - -/// Zentraler Trait für alle LLM-Provider. -#[async_trait::async_trait] -pub trait LlmProvider: Send + Sync { - /// Sendet eine Anfrage an das LLM und gibt die Antwort zurück. - async fn complete(&self, request: LlmRequest) -> Result; - - /// Gibt den Namen des Providers zurück. - /// Wird für Logging und Usage-Tracking verwendet. - fn name(&self) -> &str; } \ No newline at end of file diff --git a/crates/sebas-tian/src/lib.rs b/crates/sebas-tian/src/lib.rs index 1054934..f44afae 100644 --- a/crates/sebas-tian/src/lib.rs +++ b/crates/sebas-tian/src/lib.rs @@ -1,74 +1,38 @@ -/// Sebas Tian — Haupt-Butler-Agent von Nazarick. -/// Implementiert den Agent-Trait und orchestriert -/// LLM-Kommunikation, Prompt-Aufbau und Konversationsverlauf. +// crates/sebas-tian/src/lib.rs +// +// Sebas Tian — Haupt-Butler-Agent. +// Dünner Wrapper um BaseAgent — nur name() ist Sebas-spezifisch. +use nazarick_core::agent::BaseAgent; use nazarick_core::traits::Agent; use nazarick_core::types::AgentId; -use nazarick_core::prompt::PromptBuilder; -use api::llm::{LlmProvider, LlmRequest, Message}; +use nazarick_core::llm::LlmProvider; pub struct Sebas { - /// Eindeutige ID dieser Agent-Instanz - id: AgentId, - /// Baut den System-Prompt aus soul_core + soul_personality - prompt_builder: PromptBuilder, - /// Das LLM-Backend (LmStudio, Ollama, Mistral) - llm: Box, - /// Konversationsverlauf — damit Sebas den Kontext behält - history: Vec, + base: BaseAgent, } impl Sebas { /// Erstellt eine neue Sebas-Instanz. - /// `soul_core_path` → Pfad zu soul_core.md - /// `soul_personality_path` → Pfad zu soul_personality.md - /// `llm` → LLM-Provider (z.B. LmStudioProvider) pub fn new( soul_core_path: impl Into, soul_personality_path: impl Into, llm: Box, ) -> Self { Self { - id: AgentId::new_v4(), - prompt_builder: PromptBuilder::new(soul_core_path, soul_personality_path), - llm, - history: Vec::new(), + base: BaseAgent::new(soul_core_path, soul_personality_path, llm), } } - /// Sendet eine Nachricht an Sebas und gibt seine Antwort zurück. - /// Der Konversationsverlauf wird automatisch mitgeführt. + /// Delegiert chat() an BaseAgent. pub async fn chat(&mut self, user_message: &str) -> nazarick_core::types::Result { - // System-Prompt aus soul-Dateien aufbauen - let system_prompt = self.prompt_builder.build()?; - - // User-Nachricht zum Verlauf hinzufügen - self.history.push(Message::user(user_message)); - - // Vollständige Nachrichtenliste aufbauen: - // System-Prompt + gesamter bisheriger Verlauf - let mut messages = vec![Message::system(system_prompt)]; - messages.extend(self.history.clone()); - - // LLM anfragen - let request = LlmRequest { - messages, - max_tokens: 4096, // ← erhöht damit Thinking + Antwort reinpassen - temperature: 0.7, - }; - - let response = self.llm.complete(request).await?; - - // Antwort zum Verlauf hinzufügen damit Sebas sich erinnert - self.history.push(Message::assistant(&response.content)); - - Ok(response.content) + self.base.chat(user_message).await } } impl Agent for Sebas { fn id(&self) -> AgentId { - self.id + self.base.id } fn name(&self) -> &str {