add HumanInterface

add cli
This commit is contained in:
milan
2026-04-11 15:33:27 +02:00
parent 5bf98a1efc
commit 5aaedbd73f
9 changed files with 156 additions and 29 deletions
+25 -2
View File
@@ -6,8 +6,9 @@ use own_mcp::audio::{AudioClient, AudioClientTrait};
use rmcp::model::Implementation;
use serde::Deserialize;
use url::Url;
use own_mcp::mcp;
use own_mcp::{mcp, AgentChat};
use own_mcp::mcp::{guaranteed_mcp_server_name, MCPClient};
use own_mcp::mcp::chat::{ChatError, ToolPermission};
use own_assist_common::config_from_file;
#[derive(Debug, Deserialize)]
@@ -16,7 +17,8 @@ pub struct Config {
#[serde(rename = "audio-server")]
audio_server: AudioServerConfig,
#[serde(rename = "mcp-servers")]
mcp_servers: Vec<MCPServerConfig>
mcp_servers: Vec<MCPServerConfig>,
permissions: Option<Vec<PermissionConfig>>,
}
#[derive(Debug, Deserialize)]
@@ -75,6 +77,20 @@ impl Config {
mcp_clients
}
pub async fn set_tool_permissions(&self, agent_chat: &mut AgentChat) -> Result<(), ChatError> {
if let Some(permissions) = &self.permissions && permissions.len() > 0 {
for permission_config in permissions {
log::info!("set permission for `{}` to {:?}", permission_config.tool_name, permission_config.permission);
agent_chat.set_permission(permission_config.tool_name.clone(), permission_config.permission.clone())?;
}
}
else {
log::debug!("no permissions were provided.")
}
Ok(())
}
}
#[derive(Debug, Deserialize)]
@@ -97,4 +113,11 @@ pub struct MCPServerConfig {
pub name: Option<String>,
pub url: Url,
pub authorization: Option<String>, // should probably implement oauth some time // actually, fuck oauth
}
#[derive(Debug, Deserialize)]
pub struct PermissionConfig {
#[serde(rename = "tool-name")]
pub tool_name: String,
pub permission: ToolPermission
}
+38
View File
@@ -0,0 +1,38 @@
use crate::human_interface::{HumanInterface, HumanInterfaceError};
use dialoguer::theme::ColorfulTheme;
use dialoguer::{Confirm, Input};
use own_mcp::mcp::chat::PermissionAnswer;
pub struct CommandLine;
impl HumanInterface for CommandLine {
fn agent_message(&self, message: String) {
println!("ai: {}", message);
}
async fn expect_user_message(&self) -> Result<String, HumanInterfaceError> {
let user_message: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("you")
.interact_text()
.map_err(|e| HumanInterfaceError::IoError(e.into()))?;
Ok(user_message)
}
async fn ask_for_permission(&self,
mcp_server_name: String,
tool_name: String,
) -> Result<PermissionAnswer, HumanInterfaceError> {
let confirmation = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt(format!("Allow usage of {mcp_server_name}:{tool_name}"))
.interact()
.map_err(|e| HumanInterfaceError::IoError(e.into()))?;
match confirmation {
true => Ok(PermissionAnswer::Granted),
false => Ok(PermissionAnswer::Denied),
}
}
async fn run(&self) -> Result<(), HumanInterfaceError> {
Ok(())
}
}
+22
View File
@@ -0,0 +1,22 @@
pub mod cli;
use thiserror::Error;
use own_mcp::mcp::chat::PermissionAnswer;
#[derive(Debug, Error)]
pub enum HumanInterfaceError {
#[error(transparent)]
IoError(std::io::Error),
#[error("other error in human interface: {0}")]
Other(String)
}
pub trait HumanInterface {
fn agent_message(&self, message: String);
fn expect_user_message(&self) -> impl Future<Output = Result<String, HumanInterfaceError>>;
fn ask_for_permission(&self, mcp_server_name: String, tool_name:String) -> impl Future<Output = Result<PermissionAnswer, HumanInterfaceError>>;
fn run(&self) -> impl Future<Output = Result<(), HumanInterfaceError>>;
}
+18 -20
View File
@@ -1,14 +1,12 @@
mod config;
mod model;
mod human_interface;
use own_mcp::audio::{AudioClientTrait};
use own_mcp::mcp::chat::{PermissionAnswer, ToolPermission};
use crate::config::Config;
use crate::model::create_model_from_config;
async fn permission_callback(mcp_server_name: String, tool_name:String) -> PermissionAnswer {
PermissionAnswer::Denied // TODO: implement
}
use crate::human_interface::HumanInterface;
#[tokio::main]
async fn main() {
@@ -19,45 +17,45 @@ async fn main() {
std::process::exit(1);
}).unwrap();
let audio_client = config.audio_client();
/*let audio_client = config.audio_client();
log::debug!("Audio server status {:?}", audio_client.status().await.inspect_err(|e|{
log::error!("audio server error {}", e);
std::process::exit(1);
}).unwrap());
}).unwrap());*/
let ollama = config.ollama_instance();
let mcp_clients = config.mcp_clients().await;
let model_name = &config.ollama_config().model.name;
create_model_from_config(&ollama, &config.ollama_config().model).await.inspect_err(|e|{
log::error!("failed creating ollama model {model_name}: {e}");
std::process::exit(1);
}).unwrap();
let system_prompt = "Du bist ein Assistent, der per Sprache bedient wird. Du erhälst die Transkription \
und dein Output wird per Sprache ausgegeben und sollte dementsprechend auch kurz sein. \
und dein Output wird per Sprache ausgegeben und sollte dementsprechend auch kurz sein und kein Markdown enthalten. \
Wichtiger als deine Antworten sind deine Aktionen.\
Nutze bitte die tools, falls du sie brauchst um Informationen zu bekommen (z.B: über das aktuelle Datum oder den aktuellen Wochentag). \
Nutze bitte die tools, falls du sie brauchst um Informationen zu bekommen (z.B. über das aktuelle Datum oder den aktuellen Wochentag). \
Du bist in einem Agent Loop und kannst mehrere Tools hintereinander nutzen. \
Falls der Nutzer das Nutzen eines Tools ablehnt, sag ihm bescheid, dass du es brauchst.".to_string();
let mcp_clients = config.mcp_clients().await;
let mut agent_chat = own_mcp::AgentChat::new(ollama, model_name.clone(), mcp_clients, system_prompt).await.inspect_err(
|e| {
log::error!("error creating agent: {}", e);
std::process::exit(1);
}
).unwrap();
config.set_tool_permissions(&mut agent_chat).await.unwrap();
log::info!("all tools: {:#?}", agent_chat.get_all_tools().collect::<Vec<_>>());
agent_chat.set_permission("datetime:get_weekday".to_string(), ToolPermission::Allowed).unwrap();
agent_chat.set_permission("datetime:get_utc_datetime".to_string(), ToolPermission::Allowed).unwrap();
agent_chat.set_permission("datetime:get_local_datetime".to_string(), ToolPermission::Allowed).unwrap();
agent_chat.set_permission("datetime:get_week".to_string(), ToolPermission::Allowed).unwrap();
let human_interface = human_interface::cli::CommandLine{};
human_interface.run().await.unwrap();
let answer = agent_chat.message("In welcher Kalenderwoche befinden wir uns gerade?".to_string(), permission_callback).await;
dbg!(answer);
loop {
let user_message = human_interface.expect_user_message().await.unwrap();
let agent_message = agent_chat.message(user_message, async |mcp_server_name, tool_name|{
human_interface.ask_for_permission(mcp_server_name, tool_name).await.expect("io should not fail")
}).await.expect("failed communicating with agent");
human_interface.agent_message(agent_message.content);
}
}