Compare commits
2 commits
1a7d6ae0e2
...
45ab675b21
| Author | SHA1 | Date | |
|---|---|---|---|
| 45ab675b21 | |||
| ac716981ec |
108 changed files with 160 additions and 608 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::models::automation_model::{Automation, TriggerKind};
|
use crate::models::automation_model::{Automation, TriggerKind};
|
||||||
use crate::services::script::ScriptService;
|
use crate::basic::ScriptService;
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use chrono::Timelike;
|
use chrono::Timelike;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::services::email::save_email_draft;
|
use crate::email::save_email_draft;
|
||||||
use crate::services::email::{fetch_latest_sent_to, SaveDraftRequest};
|
use crate::email::{fetch_latest_sent_to, SaveDraftRequest};
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use rhai::Dynamic;
|
use rhai::Dynamic;
|
||||||
use rhai::Engine;
|
use rhai::Engine;
|
||||||
|
|
||||||
|
|
@ -7,8 +7,8 @@ use std::fs;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use crate::services::utils;
|
use crate::utils;
|
||||||
|
|
||||||
pub fn create_site_keyword(state: &AppState, engine: &mut Engine) {
|
pub fn create_site_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
let state_clone = state.clone();
|
let state_clone = state.clone();
|
||||||
|
|
@ -43,7 +43,7 @@ pub fn create_site_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_site(
|
async fn create_site(
|
||||||
config: &crate::services::config::AppConfig,
|
config: &crate::config::AppConfig,
|
||||||
alias: Dynamic,
|
alias: Dynamic,
|
||||||
template_dir: Dynamic,
|
template_dir: Dynamic,
|
||||||
prompt: Dynamic,
|
prompt: Dynamic,
|
||||||
|
|
@ -4,10 +4,10 @@ use rhai::Engine;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use crate::services::utils;
|
use crate::utils;
|
||||||
use crate::services::utils::row_to_json;
|
use crate::utils::row_to_json;
|
||||||
use crate::services::utils::to_array;
|
use crate::utils::to_array;
|
||||||
|
|
||||||
pub fn find_keyword(state: &AppState, engine: &mut Engine) {
|
pub fn find_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
let db = state.db_custom.clone();
|
let db = state.db_custom.clone();
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rhai::Dynamic;
|
use rhai::Dynamic;
|
||||||
use rhai::Engine;
|
use rhai::Engine;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use reqwest::{self, Client};
|
use reqwest::{self, Client};
|
||||||
use rhai::{Dynamic, Engine};
|
use rhai::{Dynamic, Engine};
|
||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::services::{state::AppState, web_automation::BrowserPool};
|
use crate::{state::AppState, web_automation::BrowserPool};
|
||||||
use log::info;
|
use log::info;
|
||||||
use rhai::{Dynamic, Engine};
|
use rhai::{Dynamic, Engine};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::services::{state::AppState, utils::call_llm};
|
use crate::{state::AppState, utils::call_llm};
|
||||||
use rhai::{Dynamic, Engine};
|
use rhai::{Dynamic, Engine};
|
||||||
|
|
||||||
pub fn llm_keyword(state: &AppState, engine: &mut Engine) {
|
pub fn llm_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
|
|
@ -5,7 +5,7 @@ use serde_json::{json, Value};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::models::automation_model::TriggerKind;
|
use crate::models::automation_model::TriggerKind;
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
pub fn on_keyword(state: &AppState, engine: &mut Engine) {
|
pub fn on_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
let db = state.db_custom.clone();
|
let db = state.db_custom.clone();
|
||||||
|
|
@ -2,7 +2,7 @@ use log::info;
|
||||||
use rhai::Dynamic;
|
use rhai::Dynamic;
|
||||||
use rhai::Engine;
|
use rhai::Engine;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
pub fn print_keyword(_state: &AppState, engine: &mut Engine) {
|
pub fn print_keyword(_state: &AppState, engine: &mut Engine) {
|
||||||
// PRINT command
|
// PRINT command
|
||||||
|
|
@ -5,8 +5,8 @@ use serde_json::{json, Value};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use crate::services::utils;
|
use crate::utils;
|
||||||
|
|
||||||
pub fn set_keyword(state: &AppState, engine: &mut Engine) {
|
pub fn set_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
let db = state.db_custom.clone();
|
let db = state.db_custom.clone();
|
||||||
|
|
@ -5,7 +5,7 @@ use serde_json::{json, Value};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::models::automation_model::TriggerKind;
|
use crate::models::automation_model::TriggerKind;
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
pub fn set_schedule_keyword(state: &AppState, engine: &mut Engine) {
|
pub fn set_schedule_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
let db = state.db_custom.clone();
|
let db = state.db_custom.clone();
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rhai::{Dynamic, Engine};
|
use rhai::{Dynamic, Engine};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
use crate::services::keywords::create_draft::create_draft_keyword;
|
mod keywords;
|
||||||
use crate::services::keywords::create_site::create_site_keyword;
|
|
||||||
use crate::services::keywords::find::find_keyword;
|
use self::keywords::create_draft::create_draft_keyword;
|
||||||
use crate::services::keywords::first::first_keyword;
|
use self::keywords::create_site::create_site_keyword;
|
||||||
use crate::services::keywords::last::last_keyword;
|
use self::keywords::find::find_keyword;
|
||||||
use crate::services::keywords::format::format_keyword;
|
use self::keywords::first::first_keyword;
|
||||||
use crate::services::keywords::for_next::for_keyword;
|
use self::keywords::for_next::for_keyword;
|
||||||
use crate::services::keywords::get::get_keyword;
|
use self::keywords::format::format_keyword;
|
||||||
use crate::services::keywords::get_website::get_website_keyword;
|
use self::keywords::get::get_keyword;
|
||||||
use crate::services::keywords::llm_keyword::llm_keyword;
|
use self::keywords::get_website::get_website_keyword;
|
||||||
use crate::services::keywords::on::on_keyword;
|
use self::keywords::last::last_keyword;
|
||||||
use crate::services::keywords::print::print_keyword;
|
use self::keywords::llm_keyword::llm_keyword;
|
||||||
use crate::services::keywords::set::set_keyword;
|
use self::keywords::on::on_keyword;
|
||||||
use crate::services::keywords::set_schedule::set_schedule_keyword;
|
use self::keywords::print::print_keyword;
|
||||||
use crate::services::keywords::wait::wait_keyword;
|
use self::keywords::set::set_keyword;
|
||||||
use crate::services::state::AppState;
|
use self::keywords::set_schedule::set_schedule_keyword;
|
||||||
|
use self::keywords::wait::wait_keyword;
|
||||||
|
use crate::shared::AppState;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rhai::{Dynamic, Engine, EvalAltResult};
|
use rhai::{Dynamic, Engine, EvalAltResult};
|
||||||
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod channels;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use livekit::{DataPacketKind, Room, RoomOptions};
|
use livekit::{DataPacketKind, Room, RoomOptions};
|
||||||
|
|
@ -6,7 +8,7 @@ use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
|
||||||
use crate::services::shared::{BotResponse, UserMessage};
|
use crate::shared::{BotResponse, UserMessage};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ChannelAdapter: Send + Sync {
|
pub trait ChannelAdapter: Send + Sync {
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::services::{config::EmailConfig, state::AppState};
|
use crate::{config::EmailConfig, state::AppState};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use actix_web::error::ErrorInternalServerError;
|
use actix_web::error::ErrorInternalServerError;
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
|
use actix_web::web;
|
||||||
|
|
||||||
use actix_web::{ web};
|
|
||||||
|
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use actix_web::{post, HttpResponse};
|
use actix_web::{post, HttpResponse};
|
||||||
use minio::s3::builders::ObjectContent;
|
use minio::s3::builders::ObjectContent;
|
||||||
|
use minio::s3::types::ToStream;
|
||||||
use minio::s3::Client;
|
use minio::s3::Client;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use minio::s3::types::ToStream;
|
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
|
|
||||||
use minio::s3::client::{Client as MinioClient, ClientBuilder as MinioClientBuilder};
|
use minio::s3::client::{Client as MinioClient, ClientBuilder as MinioClientBuilder};
|
||||||
|
|
@ -16,18 +14,18 @@ use minio::s3::creds::StaticProvider;
|
||||||
use minio::s3::http::BaseUrl;
|
use minio::s3::http::BaseUrl;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::services::config::{AppConfig};
|
use crate::config::AppConfig;
|
||||||
use crate::services::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
pub async fn init_minio(config: &AppConfig) -> Result<MinioClient, minio::s3::error::Error> {
|
pub async fn init_minio(config: &AppConfig) -> Result<MinioClient, minio::s3::error::Error> {
|
||||||
let scheme = if config.minio.use_ssl { "https" } else { "http" };
|
let scheme = if config.minio.use_ssl {
|
||||||
|
"https"
|
||||||
|
} else {
|
||||||
|
"http"
|
||||||
|
};
|
||||||
let base_url = format!("{}://{}", scheme, config.minio.server);
|
let base_url = format!("{}://{}", scheme, config.minio.server);
|
||||||
let base_url = BaseUrl::from_str(&base_url)?;
|
let base_url = BaseUrl::from_str(&base_url)?;
|
||||||
let credentials = StaticProvider::new(
|
let credentials = StaticProvider::new(&config.minio.access_key, &config.minio.secret_key, None);
|
||||||
&config.minio.access_key,
|
|
||||||
&config.minio.secret_key,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let minio_client = MinioClientBuilder::new(base_url)
|
let minio_client = MinioClientBuilder::new(base_url)
|
||||||
.provider(Some(credentials))
|
.provider(Some(credentials))
|
||||||
|
|
@ -104,8 +102,6 @@ pub async fn upload_file(
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[post("/files/list/{folder_path}")]
|
#[post("/files/list/{folder_path}")]
|
||||||
pub async fn list_file(
|
pub async fn list_file(
|
||||||
folder_path: web::Path<String>,
|
folder_path: web::Path<String>,
|
||||||
|
|
@ -132,11 +128,12 @@ pub async fn list_file(
|
||||||
for item in result.contents {
|
for item in result.contents {
|
||||||
file_list.push(item.name);
|
file_list.push(item.name);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(actix_web::error::ErrorInternalServerError(
|
return Err(actix_web::error::ErrorInternalServerError(format!(
|
||||||
format!("Failed to list files in MinIO: {}", e)
|
"Failed to list files in MinIO: {}",
|
||||||
));
|
e
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ use langchain_rust::{
|
||||||
template_fstring,
|
template_fstring,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::services::{state::AppState, utils::azure_from_config};
|
use crate::{state::AppState, utils::azure_from_config};
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct ChatRequest {
|
struct ChatRequest {
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
pub mod llm_generic;
|
||||||
|
pub mod llm_local;
|
||||||
|
pub mod llm_provider;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use langchain_rust::{
|
use langchain_rust::{
|
||||||
|
|
@ -9,7 +13,7 @@ use serde_json::Value;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::services::tools::ToolManager;
|
use crate::tools::ToolManager;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait LLMProvider: Send + Sync {
|
pub trait LLMProvider: Send + Sync {
|
||||||
78
src/main.rs
78
src/main.rs
|
|
@ -1,4 +1,21 @@
|
||||||
use actix_web::middleware::Logger;
|
mod auth;
|
||||||
|
mod automation;
|
||||||
|
mod basic;
|
||||||
|
mod bot;
|
||||||
|
mod channels;
|
||||||
|
mod chart;
|
||||||
|
mod config;
|
||||||
|
mod context;
|
||||||
|
mod email;
|
||||||
|
mod file;
|
||||||
|
mod llm;
|
||||||
|
mod org;
|
||||||
|
mod session;
|
||||||
|
mod shared;
|
||||||
|
mod tools;
|
||||||
|
mod web_automation;
|
||||||
|
mod whatsapp;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use qdrant_client::Qdrant;
|
use qdrant_client::Qdrant;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -7,27 +24,21 @@ use actix_web::{web, App, HttpServer};
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::services::auth::AuthService;
|
use crate::auth::AuthService;
|
||||||
use crate::services::automation::AutomationService;
|
use crate::bot::BotOrchestrator;
|
||||||
use crate::services::channels::ChannelAdapter;
|
use crate::config::AppConfig;
|
||||||
use crate::services::config::AppConfig;
|
use crate::email::{
|
||||||
use crate::services::email::{
|
|
||||||
get_emails, get_latest_email_from, list_emails, save_click, save_draft, send_email,
|
get_emails, get_latest_email_from, list_emails, save_click, save_draft, send_email,
|
||||||
};
|
};
|
||||||
use crate::services::file::{list_file, upload_file};
|
use crate::file::{list_file, upload_file};
|
||||||
use crate::services::llm_generic::generic_chat_completions;
|
use crate::llm::llm_generic::generic_chat_completions;
|
||||||
use crate::services::llm_local::{
|
use crate::llm::llm_local::{
|
||||||
chat_completions_local, embeddings_local, ensure_llama_servers_running,
|
chat_completions_local, embeddings_local, ensure_llama_servers_running,
|
||||||
};
|
};
|
||||||
use crate::services::orchestrator::BotOrchestrator;
|
use crate::session::SessionManager;
|
||||||
use crate::services::session::SessionManager;
|
use crate::shared::state::AppState;
|
||||||
use crate::services::state::AppState;
|
use crate::tools::{RedisToolExecutor, ToolManager};
|
||||||
use crate::services::tools::{RedisToolExecutor, ToolManager};
|
use crate::web_automation::{initialize_browser_pool, BrowserPool};
|
||||||
use crate::services::web_automation::{initialize_browser_pool, BrowserPool};
|
|
||||||
use crate::services::whatsapp::WhatsAppAdapter;
|
|
||||||
|
|
||||||
mod models;
|
|
||||||
mod services;
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
|
|
@ -41,7 +52,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
let db = PgPool::connect(&db_url).await.unwrap();
|
let db = PgPool::connect(&db_url).await.unwrap();
|
||||||
let db_custom = PgPool::connect(&db_custom_url).await.unwrap();
|
let db_custom = PgPool::connect(&db_custom_url).await.unwrap();
|
||||||
|
|
||||||
let minio_client = services::file::init_minio(&config)
|
let minio_client = init_minio(&config)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to initialize Minio");
|
.expect("Failed to initialize Minio");
|
||||||
|
|
||||||
|
|
@ -65,7 +76,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
Ok(redis_url_value) => {
|
Ok(redis_url_value) => {
|
||||||
let client = redis::Client::open(redis_url_value.clone())
|
let client = redis::Client::open(redis_url_value.clone())
|
||||||
.expect("Failed to create Redis client");
|
.expect("Failed to create Redis client");
|
||||||
let conn = redis::aio::Connection::new(client)
|
let conn = client
|
||||||
|
.get_connection()
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create Redis connection");
|
.expect("Failed to create Redis connection");
|
||||||
Some(Arc::new(conn))
|
Some(Arc::new(conn))
|
||||||
|
|
@ -81,28 +93,27 @@ async fn main() -> std::io::Result<()> {
|
||||||
let session_manager = SessionManager::new(db.clone(), redis_conn.clone());
|
let session_manager = SessionManager::new(db.clone(), redis_conn.clone());
|
||||||
let auth_service = AuthService::new(db.clone(), redis_conn.clone());
|
let auth_service = AuthService::new(db.clone(), redis_conn.clone());
|
||||||
|
|
||||||
let llm_provider: Arc<dyn crate::services::llm_local::LLMProvider> =
|
let llm_provider: Arc<dyn crate::llm_local::LLMProvider> = match std::env::var("LLM_PROVIDER")
|
||||||
match std::env::var("LLM_PROVIDER")
|
|
||||||
.unwrap_or("mock".to_string())
|
.unwrap_or("mock".to_string())
|
||||||
.as_str()
|
.as_str()
|
||||||
{
|
{
|
||||||
"openai" => Arc::new(crate::services::llm_local::OpenAIClient::new(
|
"openai" => Arc::new(crate::llm_local::OpenAIClient::new(
|
||||||
std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY required"),
|
std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY required"),
|
||||||
)),
|
)),
|
||||||
"anthropic" => Arc::new(crate::services::llm_local::AnthropicClient::new(
|
"anthropic" => Arc::new(crate::llm_local::AnthropicClient::new(
|
||||||
std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY required"),
|
std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY required"),
|
||||||
)),
|
)),
|
||||||
_ => Arc::new(crate::services::llm_local::MockLLMProvider::new()),
|
_ => Arc::new(crate::llm_local::MockLLMProvider::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let web_adapter = Arc::new(crate::services::channels::WebChannelAdapter::new());
|
let web_adapter = Arc::new(crate::channels::WebChannelAdapter::new());
|
||||||
let voice_adapter = Arc::new(crate::services::channels::VoiceAdapter::new(
|
let voice_adapter = Arc::new(crate::channels::VoiceAdapter::new(
|
||||||
std::env::var("LIVEKIT_URL").unwrap_or("ws://localhost:7880".to_string()),
|
std::env::var("LIVEKIT_URL").unwrap_or("ws://localhost:7880".to_string()),
|
||||||
std::env::var("LIVEKIT_API_KEY").unwrap_or("dev".to_string()),
|
std::env::var("LIVEKIT_API_KEY").unwrap_or("dev".to_string()),
|
||||||
std::env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string()),
|
std::env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string()),
|
||||||
));
|
));
|
||||||
|
|
||||||
let whatsapp_adapter = Arc::new(crate::services::whatsapp::WhatsAppAdapter::new(
|
let whatsapp_adapter = Arc::new(crate::whatsapp::WhatsAppAdapter::new(
|
||||||
std::env::var("META_ACCESS_TOKEN").unwrap_or("".to_string()),
|
std::env::var("META_ACCESS_TOKEN").unwrap_or("".to_string()),
|
||||||
std::env::var("META_PHONE_NUMBER_ID").unwrap_or("".to_string()),
|
std::env::var("META_PHONE_NUMBER_ID").unwrap_or("".to_string()),
|
||||||
std::env::var("META_WEBHOOK_VERIFY_TOKEN").unwrap_or("".to_string()),
|
std::env::var("META_WEBHOOK_VERIFY_TOKEN").unwrap_or("".to_string()),
|
||||||
|
|
@ -111,7 +122,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
let tool_executor = Arc::new(
|
let tool_executor = Arc::new(
|
||||||
RedisToolExecutor::new(
|
RedisToolExecutor::new(
|
||||||
redis_url.as_str(),
|
redis_url.as_str(),
|
||||||
web_adapter.clone() as Arc<dyn crate::services::channels::ChannelAdapter>,
|
web_adapter.clone() as Arc<dyn crate::channels::ChannelAdapter>,
|
||||||
db.clone(),
|
db.clone(),
|
||||||
redis_conn.clone(),
|
redis_conn.clone(),
|
||||||
)
|
)
|
||||||
|
|
@ -216,8 +227,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
.service(get_emails)
|
.service(get_emails)
|
||||||
.service(list_emails)
|
.service(list_emails)
|
||||||
.service(send_email)
|
.service(send_email)
|
||||||
.service(crate::services::orchestrator::chat_stream)
|
.service(crate::orchestrator::chat_stream)
|
||||||
.service(crate::services::orchestrator::chat)
|
.service(crate::orchestrator::chat)
|
||||||
.service(chat_completions_local)
|
.service(chat_completions_local)
|
||||||
.service(save_draft)
|
.service(save_draft)
|
||||||
.service(generic_chat_completions)
|
.service(generic_chat_completions)
|
||||||
|
|
@ -232,6 +243,11 @@ async fn main() -> std::io::Result<()> {
|
||||||
.service(services::orchestrator::get_sessions)
|
.service(services::orchestrator::get_sessions)
|
||||||
.service(services::orchestrator::get_session_history)
|
.service(services::orchestrator::get_session_history)
|
||||||
.service(services::orchestrator::index)
|
.service(services::orchestrator::index)
|
||||||
|
.service(create_organization)
|
||||||
|
.service(get_organization)
|
||||||
|
.service(list_organizations)
|
||||||
|
.service(update_organization)
|
||||||
|
.service(delete_organization)
|
||||||
})
|
})
|
||||||
.bind((config.server.host.clone(), config.server.port))?
|
.bind((config.server.host.clone(), config.server.port))?
|
||||||
.run()
|
.run()
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
pub mod automation_model;
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
|
||||||
pub struct Bot {
|
|
||||||
pub bot_id: Uuid,
|
|
||||||
pub name: String,
|
|
||||||
pub status: BotStatus,
|
|
||||||
pub config: serde_json::Value,
|
|
||||||
pub created_at: chrono::DateTime<Utc>,
|
|
||||||
pub updated_at: chrono::DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
#[sqlx(type_name = "bot_status", rename_all = "snake_case")]
|
|
||||||
pub enum BotStatus {
|
|
||||||
Active,
|
|
||||||
Inactive,
|
|
||||||
Maintenance,
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::FromRow;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
|
||||||
pub struct organization {
|
|
||||||
pub org_id: Uuid,
|
|
||||||
pub name: String,
|
|
||||||
pub slug: String,
|
|
||||||
pub created_at: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,7 @@
|
||||||
.service(create_organization)
|
|
||||||
.service(get_organization)
|
|
||||||
.service(list_organizations)
|
|
||||||
.service(update_organization)
|
|
||||||
.service(delete_organization)
|
|
||||||
|
|
||||||
|
|
||||||
use actix_web::{web, HttpResponse, Result};
|
use actix_web::{web, HttpResponse, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::Utc;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{FromRow, PgPool};
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
pub mod auth;
|
|
||||||
pub mod automation;
|
|
||||||
pub mod bot;
|
|
||||||
pub mod channels;
|
|
||||||
pub mod chart;
|
|
||||||
pub mod config;
|
|
||||||
pub mod context;
|
|
||||||
pub mod email;
|
|
||||||
pub mod file;
|
|
||||||
pub mod keywords;
|
|
||||||
pub mod llm;
|
|
||||||
pub mod session;
|
|
||||||
pub mod shared;
|
|
||||||
pub mod tools;
|
|
||||||
pub mod web_automation;
|
|
||||||
pub mod whatsapp;
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
// .service(create_bot)
|
|
||||||
// .service(get_bot)
|
|
||||||
// .service(list_bots)
|
|
||||||
// .service(update_bot)
|
|
||||||
// .service(delete_bot)
|
|
||||||
// .service(update_bot_status)
|
|
||||||
// .service(execute_bot_command)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::services::{config::BotConfig, state::AppState};
|
|
||||||
use actix_web::{
|
|
||||||
delete, get, post, put,
|
|
||||||
web::{self, Data, Json, Path},
|
|
||||||
HttpResponse, Responder, Result,
|
|
||||||
};
|
|
||||||
use chrono::Utc;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::{postgres::PgQueryResult, FromRow, PgPool};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
// 1. Core Data Structures
|
|
||||||
|
|
||||||
// 2. Request/Response DTOs
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct CreateBotRequest {
|
|
||||||
pub name: String,
|
|
||||||
pub initial_config: Option<serde_json::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct UpdateBotRequest {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub status: Option<BotStatus>,
|
|
||||||
pub config: Option<serde_json::Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct BotResponse {
|
|
||||||
pub bot_id: Uuid,
|
|
||||||
pub name: String,
|
|
||||||
pub status: BotStatus,
|
|
||||||
pub created_at: chrono::DateTime<Utc>,
|
|
||||||
pub updated_at: chrono::DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Helper Functions
|
|
||||||
impl From<Bot> for BotResponse {
|
|
||||||
fn from(bot: Bot) -> Self {
|
|
||||||
BotResponse {
|
|
||||||
bot_id: bot.bot_id,
|
|
||||||
name: bot.name,
|
|
||||||
status: bot.status,
|
|
||||||
created_at: bot.created_at,
|
|
||||||
updated_at: bot.updated_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn find_bot(bot_id: Uuid, pool: &PgPool) -> Result<Bot, sqlx::Error> {
|
|
||||||
sqlx::query_as::<_, Bot>("SELECT * FROM bots WHERE bot_id = $1")
|
|
||||||
.bind(bot_id)
|
|
||||||
.fetch_one(pool)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. API Endpoints
|
|
||||||
#[post("/bots/create")]
|
|
||||||
pub async fn create_bot(
|
|
||||||
payload: Json<CreateBotRequest>,
|
|
||||||
state: Data<AppState>,
|
|
||||||
) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let new_bot = sqlx::query_as::<_, Bot>(
|
|
||||||
r#"
|
|
||||||
INSERT INTO bots (name, status, config)
|
|
||||||
VALUES ($1, 'active', $2)
|
|
||||||
RETURNING *
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(&payload.name)
|
|
||||||
.bind(&payload.initial_config)
|
|
||||||
.fetch_one(&state.db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
log::error!("Failed to create bot: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to create bot")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Created().json(BotResponse::from(new_bot)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/bots/{bot_id}")]
|
|
||||||
pub async fn get_bot(
|
|
||||||
path: Path<Uuid>,
|
|
||||||
state: Data<AppState>,
|
|
||||||
) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let bot_id = path.into_inner();
|
|
||||||
let bot = find_bot(bot_id, &state.db).await.map_err(|e| match e {
|
|
||||||
sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
|
|
||||||
_ => {
|
|
||||||
log::error!("Failed to fetch bot: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to fetch bot")
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(BotResponse::from(bot)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/bots")]
|
|
||||||
pub async fn list_bots(state: Data<AppState>) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let bots = sqlx::query_as::<_, Bot>("SELECT * FROM bots ORDER BY created_at DESC")
|
|
||||||
.fetch_all(&state.db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
log::error!("Failed to list bots: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to list bots")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let responses: Vec<BotResponse> = bots.into_iter().map(BotResponse::from).collect();
|
|
||||||
Ok(HttpResponse::Ok().json(responses))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[put("/bots/{bot_id}")]
|
|
||||||
pub async fn update_bot(
|
|
||||||
path: Path<Uuid>,
|
|
||||||
payload: Json<UpdateBotRequest>,
|
|
||||||
state: Data<AppState>,
|
|
||||||
) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let bot_id = path.into_inner();
|
|
||||||
|
|
||||||
let updated_bot = sqlx::query_as::<_, Bot>(
|
|
||||||
r#"
|
|
||||||
UPDATE bots
|
|
||||||
SET
|
|
||||||
name = COALESCE($1, name),
|
|
||||||
status = COALESCE($2, status),
|
|
||||||
config = COALESCE($3, config),
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE bot_id = $4
|
|
||||||
RETURNING *
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(&payload.name)
|
|
||||||
.bind(&payload.status)
|
|
||||||
.bind(&payload.config)
|
|
||||||
.bind(bot_id)
|
|
||||||
.fetch_one(&state.db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| match e {
|
|
||||||
sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
|
|
||||||
_ => {
|
|
||||||
log::error!("Failed to update bot: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to update bot")
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(BotResponse::from(updated_bot)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("/bots/{bot_id}")]
|
|
||||||
pub async fn delete_bot(
|
|
||||||
path: Path<Uuid>,
|
|
||||||
state: Data<AppState>,
|
|
||||||
) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let bot_id = path.into_inner();
|
|
||||||
|
|
||||||
let result = sqlx::query("DELETE FROM bots WHERE bot_id = $1")
|
|
||||||
.bind(bot_id)
|
|
||||||
.execute(&state.db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
log::error!("Failed to delete bot: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to delete bot")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if result.rows_affected() == 0 {
|
|
||||||
return Err(actix_web::error::ErrorNotFound("Bot not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(HttpResponse::NoContent().finish())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[put("/bots/{bot_id}/status")]
|
|
||||||
pub async fn update_bot_status(
|
|
||||||
path: Path<Uuid>,
|
|
||||||
new_status: Json<BotStatus>,
|
|
||||||
state: Data<AppState>,
|
|
||||||
) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let bot_id = path.into_inner();
|
|
||||||
|
|
||||||
let updated_bot = sqlx::query_as::<_, Bot>(
|
|
||||||
"UPDATE bots SET status = $1, updated_at = NOW() WHERE bot_id = $2 RETURNING *",
|
|
||||||
)
|
|
||||||
.bind(new_status.into_inner())
|
|
||||||
.bind(bot_id)
|
|
||||||
.fetch_one(&state.db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| match e {
|
|
||||||
sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
|
|
||||||
_ => {
|
|
||||||
log::error!("Failed to update bot status: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to update bot status")
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(BotResponse::from(updated_bot)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/bots/{bot_id}/execute")]
|
|
||||||
pub async fn execute_bot_command(
|
|
||||||
path: Path<Uuid>,
|
|
||||||
command: Json<serde_json::Value>,
|
|
||||||
state: Data<AppState>,
|
|
||||||
) -> Result<impl Responder, actix_web::Error> {
|
|
||||||
let bot_id = path.into_inner();
|
|
||||||
|
|
||||||
// Verify bot exists
|
|
||||||
let _ = find_bot(bot_id, &state.db).await.map_err(|e| match e {
|
|
||||||
sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"),
|
|
||||||
_ => {
|
|
||||||
log::error!("Failed to fetch bot: {}", e);
|
|
||||||
actix_web::error::ErrorInternalServerError("Failed to fetch bot")
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Here you would implement your bot execution logic
|
|
||||||
// For now, we'll just echo back the command
|
|
||||||
Ok(HttpResponse::Ok().json(json!({
|
|
||||||
"bot_id": bot_id,
|
|
||||||
"command": command,
|
|
||||||
"result": "Command executed successfully (simulated)"
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
use actix_web::{web, HttpResponse, Result};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
struct ChatRequest {
|
|
||||||
input: String,
|
|
||||||
context: Option<AppContext>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
struct AppContext {
|
|
||||||
view_type: Option<String>,
|
|
||||||
email_context: Option<EmailContext>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
struct EmailContext {
|
|
||||||
id: String,
|
|
||||||
subject: String,
|
|
||||||
labels: Vec<String>,
|
|
||||||
from: Option<String>,
|
|
||||||
to: Option<Vec<String>>,
|
|
||||||
body: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
|
||||||
struct ChatResponse {
|
|
||||||
response: String,
|
|
||||||
tool_calls: Option<Vec<ToolCall>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
|
||||||
struct ToolCall {
|
|
||||||
tool_name: String,
|
|
||||||
parameters: serde_json::Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
|
||||||
struct ToolDefinition {
|
|
||||||
name: String,
|
|
||||||
description: String,
|
|
||||||
parameters: serde_json::Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::post("/chat")]
|
|
||||||
pub async fn chat(
|
|
||||||
web::Json(request): web::Json<ChatRequest>,
|
|
||||||
state: web::Data<AppState>,
|
|
||||||
) -> Result<HttpResponse> {
|
|
||||||
let azure_config = from_config(&state.config.clone().unwrap().ai);
|
|
||||||
let open_ai = OpenAI::new(azure_config);
|
|
||||||
|
|
||||||
// Define available tools based on context
|
|
||||||
let tools = get_available_tools(&request.context);
|
|
||||||
|
|
||||||
// Build the prompt with context and available tools
|
|
||||||
let system_prompt = build_system_prompt(&request.context, &tools);
|
|
||||||
let user_message = format!("{}\n\nUser input: {}", system_prompt, request.input);
|
|
||||||
|
|
||||||
let response = match open_ai.invoke(&user_message).await {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => {
|
|
||||||
error!("Error invoking API: {}", err);
|
|
||||||
return Err(actix_web::error::ErrorInternalServerError(
|
|
||||||
"Failed to invoke OpenAI API",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the response for tool calls
|
|
||||||
let tool_calls = parse_tool_calls(&response);
|
|
||||||
|
|
||||||
let chat_response = ChatResponse {
|
|
||||||
response,
|
|
||||||
tool_calls,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(chat_response))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_available_tools(context: &Option<AppContext>) -> Vec<ToolDefinition> {
|
|
||||||
let mut tools = Vec::new();
|
|
||||||
|
|
||||||
if let Some(ctx) = context {
|
|
||||||
if let Some(view_type) = &ctx.view_type {
|
|
||||||
match view_type.as_str() {
|
|
||||||
"email" => {
|
|
||||||
tools.push(ToolDefinition {
|
|
||||||
name: "replyEmail".to_string(),
|
|
||||||
description: "Reply to the current email with generated content".to_string(),
|
|
||||||
parameters: serde_json::json!({
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The reply content to send"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["content"]
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
tools.push(ToolDefinition {
|
|
||||||
name: "forwardEmail".to_string(),
|
|
||||||
description: "Forward the current email to specified recipients".to_string(),
|
|
||||||
parameters: serde_json::json!({
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"recipients": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {"type": "string"},
|
|
||||||
"description": "Email addresses to forward to"
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Additional message to include"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["recipients"]
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tools
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_system_prompt(context: &Option<AppContext>, tools: &[ToolDefinition]) -> String {
|
|
||||||
let mut prompt = String::new();
|
|
||||||
|
|
||||||
if let Some(ctx) = context {
|
|
||||||
if let Some(view_type) = &ctx.view_type {
|
|
||||||
match view_type.as_str() {
|
|
||||||
"email" => {
|
|
||||||
if let Some(email_ctx) = &ctx.email_context {
|
|
||||||
prompt.push_str(&format!(
|
|
||||||
"You are an email assistant. Current email context:\n\
|
|
||||||
Subject: {}\n\
|
|
||||||
ID: {}\n\
|
|
||||||
Labels: {:?}\n\n",
|
|
||||||
email_ctx.subject, email_ctx.id, email_ctx.labels
|
|
||||||
));
|
|
||||||
|
|
||||||
if let Some(from) = &email_ctx.from {
|
|
||||||
prompt.push_str(&format!("From: {}\n", from));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(body) = &email_ctx.body {
|
|
||||||
prompt.push_str(&format!("Body: {}\n", body));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tools.is_empty() {
|
|
||||||
prompt.push_str("\nAvailable tools:\n");
|
|
||||||
for tool in tools {
|
|
||||||
prompt.push_str(&format!(
|
|
||||||
"- {}: {}\n Parameters: {}\n\n",
|
|
||||||
tool.name, tool.description, tool.parameters
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt.push_str(
|
|
||||||
"If you need to use a tool, respond with:\n\
|
|
||||||
TOOL_CALL: tool_name\n\
|
|
||||||
PARAMETERS: {json_parameters}\n\
|
|
||||||
RESPONSE: your_response_text\n\n\
|
|
||||||
Otherwise, just provide a normal response.\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_tool_calls(response: &str) -> Option<Vec<ToolCall>> {
|
|
||||||
if !response.contains("TOOL_CALL:") {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tool_calls = Vec::new();
|
|
||||||
let lines: Vec<&str> = response.lines().collect();
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
while i < lines.len() {
|
|
||||||
if lines[i].starts_with("TOOL_CALL:") {
|
|
||||||
let tool_name = lines[i].replace("TOOL_CALL:", "").trim().to_string();
|
|
||||||
|
|
||||||
// Look for parameters in the next line
|
|
||||||
if i + 1 < lines.len() && lines[i + 1].starts_with("PARAMETERS:") {
|
|
||||||
let params_str = lines[i + 1].replace("PARAMETERS:", "").trim();
|
|
||||||
|
|
||||||
if let Ok(parameters) = serde_json::from_str::<serde_json::Value>(params_str) {
|
|
||||||
tool_calls.push(ToolCall {
|
|
||||||
tool_name,
|
|
||||||
parameters,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if tool_calls.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(tool_calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::services::shared::shared::UserSession;
|
use crate::shared::shared::UserSession;
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use redis::{aio::Connection as ConnectionManager, AsyncCommands};
|
use redis::{aio::Connection as ConnectionManager, AsyncCommands};
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue