upgrade Skill system auf regitry
This commit is contained in:
@@ -7,3 +7,5 @@ edition = "2024"
|
||||
nazarick-core = { path = "../nazarick-core" }
|
||||
tracing = "0.1.44"
|
||||
anyhow = "1.0.102"
|
||||
async-trait = "0.1.89"
|
||||
inventory = "0.3.22"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// crates/skills/src/lib.rs
|
||||
//
|
||||
// Skills — explizite Fähigkeiten für Nazarick-Agenten.
|
||||
// Kein generischer Shell-Zugriff — jeder Skill ist bewusst implementiert.
|
||||
pub mod skills;
|
||||
|
||||
pub mod personality;
|
||||
// Stellt sicher dass alle inventory::submit! ausgeführt werden.
|
||||
// Ohne diesen Import würden Skills nie eingesammelt.
|
||||
pub use skills::personality;
|
||||
@@ -1,93 +0,0 @@
|
||||
// crates/skills/src/personality.rs
|
||||
//
|
||||
// Personality Skill — implementiert PersonalityWriter Trait.
|
||||
// Schreibt Persönlichkeits-Updates in soul_personality.md.
|
||||
|
||||
use nazarick_core::agent::PersonalityWriter;
|
||||
use tracing::info;
|
||||
|
||||
/// Konkrete Implementierung des PersonalityWriter Traits.
|
||||
/// Wird in main.rs erstellt und via Dependency Injection an SkillExecutor übergeben.
|
||||
pub struct PersonalitySkill {
|
||||
/// Pfad zur soul_personality.md des Agenten
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl PersonalitySkill {
|
||||
/// Erstellt einen neuen PersonalitySkill für einen Agenten.
|
||||
/// `path` → Pfad zur soul_personality.md
|
||||
pub fn new(path: impl Into<String>) -> Self {
|
||||
Self { path: path.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonalityWriter for PersonalitySkill {
|
||||
/// Aktualisiert ein Feld in soul_personality.md.
|
||||
/// Abschnitt wird ersetzt wenn vorhanden, sonst angehängt.
|
||||
fn update(&self, field: &str, value: &str) -> anyhow::Result<()> {
|
||||
let content = std::fs::read_to_string(&self.path)?;
|
||||
let section_header = format!("## {}", field);
|
||||
let new_section = format!("## {}\n{}", field, value);
|
||||
|
||||
let updated = if content.contains(§ion_header) {
|
||||
let mut result = String::new();
|
||||
let mut in_section = false;
|
||||
|
||||
for line in content.lines() {
|
||||
if line.trim_start().starts_with("## ") && line.contains(field) {
|
||||
result.push_str(&new_section);
|
||||
result.push('\n');
|
||||
in_section = true;
|
||||
} else if line.trim_start().starts_with("## ") && in_section {
|
||||
in_section = false;
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
} else if !in_section {
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
format!("{}\n{}\n", content.trim_end(), new_section)
|
||||
};
|
||||
|
||||
std::fs::write(&self.path, updated)?;
|
||||
info!(path = %self.path, field = %field, "Persönlichkeit aktualisiert");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Entfernt einen Abschnitt aus soul_personality.md.
|
||||
/// Macht nichts wenn der Abschnitt nicht existiert.
|
||||
fn remove(&self, field: &str) -> anyhow::Result<()> {
|
||||
let content = std::fs::read_to_string(&self.path)?;
|
||||
let section_header = format!("## {}", field);
|
||||
|
||||
// Abschnitt nicht vorhanden — nichts zu tun
|
||||
if !content.contains(§ion_header) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut result = String::new();
|
||||
let mut in_section = false;
|
||||
|
||||
for line in content.lines() {
|
||||
if line.trim_start().starts_with("## ") && line.contains(field) {
|
||||
// Abschnitt gefunden — überspringen
|
||||
in_section = true;
|
||||
} else if line.trim_start().starts_with("## ") && in_section {
|
||||
// Nächster Abschnitt — Section-Modus beenden
|
||||
in_section = false;
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
} else if !in_section {
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
std::fs::write(&self.path, result.trim_end())?;
|
||||
info!(path = %self.path, field = %field, "Persönlichkeits-Abschnitt entfernt");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod personality;
|
||||
@@ -0,0 +1,131 @@
|
||||
// crates/skills/src/skills/personality.rs
|
||||
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use anyhow::Result;
|
||||
use tracing::info;
|
||||
use nazarick_core::agent::traits::{Skill, SkillInput, SkillOutput};
|
||||
use nazarick_core::agent::context::AgentContext;
|
||||
use nazarick_core::agent::skill_registry::SkillMeta;
|
||||
|
||||
pub struct PersonalitySkill;
|
||||
|
||||
impl PersonalitySkill {
|
||||
fn path(agent_id: &str) -> String {
|
||||
format!("crates/{}/config/soul_personality.md", agent_id)
|
||||
}
|
||||
|
||||
fn do_update(path: &str, field: &str, value: &str) -> Result<()> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
let section_header = format!("## {}", field);
|
||||
let new_section = format!("## {}\n{}", field, value);
|
||||
|
||||
let updated = if content.contains(§ion_header) {
|
||||
let mut result = String::new();
|
||||
let mut in_section = false;
|
||||
for line in content.lines() {
|
||||
if line.trim_start().starts_with("## ") && line.contains(field) {
|
||||
result.push_str(&new_section);
|
||||
result.push('\n');
|
||||
in_section = true;
|
||||
} else if line.trim_start().starts_with("## ") && in_section {
|
||||
in_section = false;
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
} else if !in_section {
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
format!("{}\n{}\n", content.trim_end(), new_section)
|
||||
};
|
||||
|
||||
std::fs::write(path, updated)?;
|
||||
info!(path = %path, field = %field, "Persönlichkeit aktualisiert");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_remove(path: &str, field: &str) -> Result<()> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
if !content.contains(&format!("## {}", field)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut result = String::new();
|
||||
let mut in_section = false;
|
||||
for line in content.lines() {
|
||||
if line.trim_start().starts_with("## ") && line.contains(field) {
|
||||
in_section = true;
|
||||
} else if line.trim_start().starts_with("## ") && in_section {
|
||||
in_section = false;
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
} else if !in_section {
|
||||
result.push_str(line);
|
||||
result.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
std::fs::write(path, result.trim_end())?;
|
||||
info!(path = %path, field = %field, "Persönlichkeits-Abschnitt entfernt");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Skill for PersonalitySkill {
|
||||
fn summary(&self) -> &str {
|
||||
"Liest und schreibt den PERSONALITY [MUTABLE] Block — speichert dauerhaft Eigenschaften wie Ton, Stil oder Präferenzen des Herrn die das Verhalten des Agenten beeinflussen"
|
||||
}
|
||||
|
||||
fn details(&self) -> &str {
|
||||
"Verwaltet Persönlichkeitswerte in soul_personality.md.
|
||||
|
||||
## update
|
||||
Setzt oder überschreibt einen Wert:
|
||||
<skill name=\"personality\">
|
||||
<action>update</action>
|
||||
<field>Ton</field>
|
||||
<value>kurz und direkt</value>
|
||||
</skill>
|
||||
|
||||
## remove
|
||||
Entfernt einen Wert:
|
||||
<skill name=\"personality\">
|
||||
<action>remove</action>
|
||||
<field>Ton</field>
|
||||
</skill>"
|
||||
}
|
||||
|
||||
async fn execute(&self, input: SkillInput, ctx: AgentContext) -> Result<SkillOutput> {
|
||||
let path = Self::path(&ctx.agent_id);
|
||||
let field = input.require("field")?;
|
||||
|
||||
// action ist optional — fehlt es, wird aus value abgeleitet
|
||||
let action = input.get("action").unwrap_or_else(|| {
|
||||
if input.get("value").is_some() { "update" } else { "remove" }
|
||||
});
|
||||
|
||||
match action {
|
||||
"update" => {
|
||||
let value = input.require("value")?;
|
||||
Self::do_update(&path, field, value)?;
|
||||
Ok(SkillOutput::ok(format!("'{}' gesetzt auf '{}'", field, value)))
|
||||
}
|
||||
"remove" => {
|
||||
Self::do_remove(&path, field)?;
|
||||
Ok(SkillOutput::ok(format!("'{}' entfernt", field)))
|
||||
}
|
||||
unknown => Ok(SkillOutput::err(format!("Unbekannte Action '{}'", unknown)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inventory::submit!(SkillMeta {
|
||||
name: "personality",
|
||||
allowed: &["all"],
|
||||
awaits_result: false,
|
||||
skill: || Arc::new(PersonalitySkill),
|
||||
});
|
||||
Reference in New Issue
Block a user