apply formatting
This commit is contained in:
+32
-18
@@ -1,15 +1,15 @@
|
||||
use own_assist_common::config_loader::ConfigLoadingError;
|
||||
use std::collections::HashMap;
|
||||
use ollama_rs::headers::{HeaderMap, HeaderValue};
|
||||
use ollama_rs::Ollama;
|
||||
use ollama_rs::headers::{HeaderMap, HeaderValue};
|
||||
use own_assist_common::config_from_file;
|
||||
use own_assist_common::config_loader::ConfigLoadingError;
|
||||
use own_mcp::audio::{AudioClient, AudioClientTrait};
|
||||
use own_mcp::mcp::chat::{ChatError, ToolPermission};
|
||||
use own_mcp::mcp::{MCPClient, guaranteed_mcp_server_name};
|
||||
use own_mcp::{AgentChat, mcp};
|
||||
use rmcp::model::Implementation;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
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)]
|
||||
pub struct Config {
|
||||
@@ -26,7 +26,7 @@ impl Config {
|
||||
pub fn from_file() -> Result<Self, ConfigLoadingError> {
|
||||
config_from_file("assist.toml")
|
||||
}
|
||||
|
||||
|
||||
pub fn ollama_config(&self) -> &OllamaConfig {
|
||||
&self.ollama
|
||||
}
|
||||
@@ -35,7 +35,10 @@ impl Config {
|
||||
let mut ollama_headers = HeaderMap::new();
|
||||
|
||||
if let Some(authorization_header) = &self.ollama.authorization {
|
||||
ollama_headers.append("Authorization", HeaderValue::from_str(authorization_header).unwrap());
|
||||
ollama_headers.append(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(authorization_header).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
let ollama_default_url = Url::parse("http://127.0.0.1:11434").unwrap();
|
||||
@@ -58,7 +61,9 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mcp_clients(&self) -> Result<HashMap<String, MCPClient>, rmcp::service::ClientInitializeError> {
|
||||
pub async fn mcp_clients(
|
||||
&self,
|
||||
) -> Result<HashMap<String, MCPClient>, rmcp::service::ClientInitializeError> {
|
||||
let mut mcp_clients: HashMap<String, MCPClient> = HashMap::new();
|
||||
|
||||
for mcp_server in &self.mcp_servers {
|
||||
@@ -66,7 +71,8 @@ impl Config {
|
||||
mcp_server.url.as_str(),
|
||||
mcp_server.authorization.clone(),
|
||||
Implementation::new("own_assist", env!("CARGO_PKG_VERSION")),
|
||||
).await?;
|
||||
)
|
||||
.await?;
|
||||
|
||||
let server_name = guaranteed_mcp_server_name(mcp_server.name.clone(), &client);
|
||||
let server_data = client;
|
||||
@@ -78,13 +84,21 @@ impl Config {
|
||||
}
|
||||
|
||||
pub async fn set_tool_permissions(&self, agent_chat: &mut AgentChat) -> Result<(), ChatError> {
|
||||
if let Some(permissions) = &self.permissions && !permissions.is_empty() {
|
||||
if let Some(permissions) = &self.permissions
|
||||
&& !permissions.is_empty()
|
||||
{
|
||||
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)?;
|
||||
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,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
log::debug!("no permissions were provided.")
|
||||
}
|
||||
|
||||
@@ -138,5 +152,5 @@ pub struct MCPServerConfig {
|
||||
pub struct PermissionConfig {
|
||||
#[serde(rename = "tool-name")]
|
||||
pub tool_name: String,
|
||||
pub permission: ToolPermission
|
||||
}
|
||||
pub permission: ToolPermission,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::translate;
|
||||
use crate::human_interface::{HumanInterface, HumanInterfaceError};
|
||||
use crate::tlt;
|
||||
use crate::translate;
|
||||
use cpal::traits::DeviceTrait;
|
||||
use own_mcp::audio::models::VoiceRequest;
|
||||
use own_mcp::audio::{AudioClient, AudioClientTrait};
|
||||
@@ -8,7 +9,6 @@ use std::io;
|
||||
use std::path::Path;
|
||||
use tokio::fs::create_dir_all;
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
use crate::tlt;
|
||||
|
||||
const TEMPORARY_AUDIO_PATH: &str = "temporary_audio";
|
||||
|
||||
@@ -31,7 +31,7 @@ impl Audio {
|
||||
async fn wait_for_input_line(expected_line: &str) -> Result<(), io::Error> {
|
||||
loop {
|
||||
let stdin_reader = tokio::io::BufReader::new(tokio::io::stdin());
|
||||
log::trace!("wating for '{expected_line}' to continue");
|
||||
log::trace!("waiting for '{expected_line}' to continue");
|
||||
if let Some(line) = stdin_reader.lines().next_line().await? {
|
||||
log::debug!("Received line: {line:?}");
|
||||
if line == expected_line {
|
||||
@@ -53,8 +53,8 @@ impl Audio {
|
||||
log::debug!("Received line: {line:?}");
|
||||
|
||||
match line.as_str() {
|
||||
GRANTED_LINE => { return Ok(PermissionAnswer::Granted) },
|
||||
DENIED_LINE => { return Ok(PermissionAnswer::Denied) },
|
||||
GRANTED_LINE => return Ok(PermissionAnswer::Granted),
|
||||
DENIED_LINE => return Ok(PermissionAnswer::Denied),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
@@ -64,10 +64,7 @@ impl Audio {
|
||||
async fn play_text(&self, text: String) -> Result<(), HumanInterfaceError> {
|
||||
let bytes = self
|
||||
.client
|
||||
.tts(VoiceRequest {
|
||||
text,
|
||||
config: None,
|
||||
})
|
||||
.tts(VoiceRequest { text, config: None })
|
||||
.await
|
||||
.map_err(|e| HumanInterfaceError::Other(format!("Failed getting tts: {e}")))?;
|
||||
|
||||
@@ -75,9 +72,11 @@ impl Audio {
|
||||
|
||||
tokio::fs::write(&wav_file_path, &bytes).await?;
|
||||
|
||||
let handle = rodio::DeviceSinkBuilder::open_default_sink().expect("open default audio stream");
|
||||
let handle =
|
||||
rodio::DeviceSinkBuilder::open_default_sink().expect("open default audio stream");
|
||||
let file = std::fs::File::open(wav_file_path)?;
|
||||
let player = rodio::play(handle.mixer(), file).map_err(|e| HumanInterfaceError::Other(format!("Failed playing audio: {e}")))?;
|
||||
let player = rodio::play(handle.mixer(), file)
|
||||
.map_err(|e| HumanInterfaceError::Other(format!("Failed playing audio: {e}")))?;
|
||||
|
||||
player.sleep_until_end();
|
||||
|
||||
@@ -111,14 +110,14 @@ impl HumanInterface for Audio {
|
||||
|
||||
let wav_file_path = Path::new(TEMPORARY_AUDIO_PATH).join("recording.wav");
|
||||
|
||||
samples_to_wav(
|
||||
samples,
|
||||
&wav_spec,
|
||||
&wav_file_path,
|
||||
)
|
||||
.map_err(|e| HumanInterfaceError::Other(format!("hound error: {e}")))?;
|
||||
samples_to_wav(samples, &wav_spec, &wav_file_path)
|
||||
.map_err(|e| HumanInterfaceError::Other(format!("hound error: {e}")))?;
|
||||
|
||||
let transcription = self.client.transcribe(wav_file_path).await.map_err(|e|HumanInterfaceError::Other(format!("failed to transcribe: {e}")))?;
|
||||
let transcription = self
|
||||
.client
|
||||
.transcribe(wav_file_path)
|
||||
.await
|
||||
.map_err(|e| HumanInterfaceError::Other(format!("failed to transcribe: {e}")))?;
|
||||
|
||||
log::info!("transcription: {transcription:?}");
|
||||
|
||||
@@ -130,7 +129,12 @@ impl HumanInterface for Audio {
|
||||
mcp_server_name: String,
|
||||
tool_name: String,
|
||||
) -> Result<PermissionAnswer, HumanInterfaceError> {
|
||||
self.play_text(tlt!("tool_allow_question_audio", mcp_server_name, tool_name)).await?;
|
||||
self.play_text(tlt!(
|
||||
"tool_allow_question_audio",
|
||||
mcp_server_name,
|
||||
tool_name
|
||||
))
|
||||
.await?;
|
||||
|
||||
let decision = Self::wait_for_decision().await?;
|
||||
log::debug!("decision: {decision:?}");
|
||||
@@ -146,11 +150,13 @@ mod tests {
|
||||
const AUTH_HEADER: &str = include_str!(".AUTH_HEADER");
|
||||
|
||||
async fn get_test_human_interface() -> Audio {
|
||||
Audio::new(AudioClient::new(
|
||||
url::Url::parse("https://audio.mboemer.de"
|
||||
).unwrap()).
|
||||
with_authorization(
|
||||
AUTH_HEADER.to_string()), cpal::Host::default().default_input_device().unwrap()).await.unwrap()
|
||||
Audio::new(
|
||||
AudioClient::new(url::Url::parse("https://audio.mboemer.de").unwrap())
|
||||
.with_authorization(AUTH_HEADER.to_string()),
|
||||
cpal::Host::default().default_input_device().unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -166,6 +172,9 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_tts_playing() {
|
||||
let human_interface = get_test_human_interface().await;
|
||||
human_interface.play_text(String::from("Peter ist jetzt in deinem PC.")).await.unwrap();
|
||||
human_interface
|
||||
.play_text(String::from("Peter ist jetzt in deinem PC."))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
+13
-10
@@ -1,11 +1,11 @@
|
||||
use std::time::Duration;
|
||||
use console::style;
|
||||
use crate::translate;
|
||||
use crate::human_interface::{HumanInterface, HumanInterfaceError};
|
||||
use crate::tlt;
|
||||
use crate::translate;
|
||||
use console::style;
|
||||
use dialoguer::theme::ColorfulTheme;
|
||||
use dialoguer::{Confirm, Input};
|
||||
use own_mcp::mcp::chat::PermissionAnswer;
|
||||
use crate::tlt;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct CommandLine {
|
||||
progress_bar: Option<indicatif::ProgressBar>,
|
||||
@@ -29,7 +29,7 @@ impl HumanInterface for CommandLine {
|
||||
self.progress_bar = None;
|
||||
}
|
||||
|
||||
println!("{}: {}",style( tlt!("assistant")).bold().italic(),message);
|
||||
println!("{}: {}", style(tlt!("assistant")).bold().italic(), message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -41,14 +41,18 @@ impl HumanInterface for CommandLine {
|
||||
|
||||
// waiting for agent response
|
||||
self.progress_bar = Some(Self::spinner());
|
||||
self.progress_bar.clone().unwrap().enable_steady_tick(Duration::from_millis(100));
|
||||
self.progress_bar
|
||||
.clone()
|
||||
.unwrap()
|
||||
.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
Ok(user_message)
|
||||
}
|
||||
|
||||
async fn ask_for_permission(&self,
|
||||
mcp_server_name: String,
|
||||
tool_name: String,
|
||||
async fn ask_for_permission(
|
||||
&self,
|
||||
mcp_server_name: String,
|
||||
tool_name: String,
|
||||
) -> Result<PermissionAnswer, HumanInterfaceError> {
|
||||
if let Some(progress_bar) = &self.progress_bar {
|
||||
progress_bar.finish_and_clear();
|
||||
@@ -65,4 +69,3 @@ impl HumanInterface for CommandLine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
pub mod cli;
|
||||
pub mod audio;
|
||||
pub mod cli;
|
||||
|
||||
use thiserror::Error;
|
||||
use own_mcp::mcp::chat::PermissionAnswer;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum HumanInterfaceError {
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("other error in human interface: {0}")]
|
||||
Other(String)
|
||||
Other(String),
|
||||
}
|
||||
|
||||
pub trait HumanInterface {
|
||||
fn agent_message(&mut self, message: String) -> impl Future<Output = Result<(), HumanInterfaceError>>;
|
||||
fn agent_message(
|
||||
&mut self,
|
||||
message: String,
|
||||
) -> impl Future<Output = Result<(), HumanInterfaceError>>;
|
||||
|
||||
fn expect_user_message(&mut 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 ask_for_permission(
|
||||
&self,
|
||||
mcp_server_name: String,
|
||||
tool_name: String,
|
||||
) -> impl Future<Output = Result<PermissionAnswer, HumanInterfaceError>>;
|
||||
}
|
||||
|
||||
+4
-5
@@ -77,11 +77,10 @@ mod tests {
|
||||
let formatted = format_dynamically("{...} is cool!".to_string(), vec!["Rust".to_string()]);
|
||||
assert_eq!(formatted, "Rust is cool!");
|
||||
|
||||
let formatted = format_dynamically("{...}, {...} and {...} are three consecutive numbers.".to_string(), vec![
|
||||
"1".to_string(),
|
||||
"2".to_string(),
|
||||
"3".to_string(),
|
||||
]);
|
||||
let formatted = format_dynamically(
|
||||
"{...}, {...} and {...} are three consecutive numbers.".to_string(),
|
||||
vec!["1".to_string(), "2".to_string(), "3".to_string()],
|
||||
);
|
||||
assert_eq!(formatted, "1, 2 and 3 are three consecutive numbers.");
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -10,9 +10,8 @@ use crate::model::create_model_from_config;
|
||||
use base64::Engine;
|
||||
use clap::Parser;
|
||||
use cpal::traits::HostTrait;
|
||||
use own_mcp::AgentChat;
|
||||
use std::fmt::Display;
|
||||
use own_assist_common::exit_msg;
|
||||
use own_mcp::AgentChat;
|
||||
|
||||
/// partial mcp client with cli and voice interaction
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -76,10 +75,7 @@ async fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let config = Config::from_file()
|
||||
.inspect_err(|e| {
|
||||
log::error!("error loading assist.toml: {}", e);
|
||||
std::process::exit(1);
|
||||
})
|
||||
.inspect_err(exit_msg!("error loading assist.toml: {}"))
|
||||
.unwrap();
|
||||
|
||||
let ollama = config.ollama_instance();
|
||||
@@ -98,7 +94,7 @@ async fn main() {
|
||||
.inspect_err(exit_msg!("failed creating MCP clients"))
|
||||
.unwrap();
|
||||
|
||||
let mut agent_chat = own_mcp::AgentChat::new(ollama, model_name.clone(), mcp_clients)
|
||||
let mut agent_chat = AgentChat::new(ollama, model_name.clone(), mcp_clients)
|
||||
.await
|
||||
.inspect_err(exit_msg!("failed to create agent"))
|
||||
.unwrap();
|
||||
@@ -135,4 +131,4 @@ async fn main() {
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+5
-8
@@ -9,19 +9,16 @@ use ollama_rs::models::create::{CreateModelRequest, CreateModelStatus};
|
||||
pub async fn create_model_from_config(
|
||||
ollama: &Ollama,
|
||||
config: &OllamaModelConfig,
|
||||
interface: &HumanInterface
|
||||
interface: &HumanInterface,
|
||||
) -> Result<CreateModelStatus, OllamaError> {
|
||||
let model_options = ModelOptions::default()
|
||||
.num_ctx(config.context_size.unwrap_or(2048)) // 2048 is the ollama default.
|
||||
.temperature(config.temperature.unwrap_or(0.8)); // 0.8 is the ollama default
|
||||
|
||||
let system_prompt = config
|
||||
.system_prompt
|
||||
.clone()
|
||||
.unwrap_or(match interface {
|
||||
HumanInterface::Cli => tlt!("system_prompt_cli"),
|
||||
HumanInterface::Audio => tlt!("system_prompt_audio"),
|
||||
});
|
||||
let system_prompt = config.system_prompt.clone().unwrap_or(match interface {
|
||||
HumanInterface::Cli => tlt!("system_prompt_cli"),
|
||||
HumanInterface::Audio => tlt!("system_prompt_audio"),
|
||||
});
|
||||
|
||||
// print message to warn user of long waiting times
|
||||
if let Ok(models) = ollama.list_local_models().await
|
||||
|
||||
Reference in New Issue
Block a user