2025-11-27 23:10:43 -03:00
|
|
|
pub mod kb_context;
|
2025-11-22 22:55:35 -03:00
|
|
|
use crate::core::config::ConfigManager;
|
2025-11-27 23:10:43 -03:00
|
|
|
|
|
|
|
|
#[cfg(feature = "drive")]
|
2025-11-22 22:55:35 -03:00
|
|
|
use crate::drive::drive_monitor::DriveMonitor;
|
2026-01-22 19:45:18 -03:00
|
|
|
#[cfg(feature = "llm")]
|
2025-11-27 23:10:43 -03:00
|
|
|
use crate::llm::llm_models;
|
2026-01-22 19:45:18 -03:00
|
|
|
#[cfg(feature = "llm")]
|
2025-11-22 22:55:35 -03:00
|
|
|
use crate::llm::OpenAIClient;
|
2025-11-27 23:10:43 -03:00
|
|
|
#[cfg(feature = "nvidia")]
|
|
|
|
|
use crate::nvidia::get_system_metrics;
|
2025-11-28 18:15:09 -03:00
|
|
|
use crate::shared::message_types::MessageType;
|
2025-11-22 22:55:35 -03:00
|
|
|
use crate::shared::models::{BotResponse, UserMessage, UserSession};
|
|
|
|
|
use crate::shared::state::AppState;
|
|
|
|
|
use axum::extract::ws::{Message, WebSocket};
|
|
|
|
|
use axum::{
|
|
|
|
|
extract::{ws::WebSocketUpgrade, Extension, Query, State},
|
|
|
|
|
http::StatusCode,
|
|
|
|
|
response::{IntoResponse, Json},
|
|
|
|
|
};
|
|
|
|
|
use diesel::PgConnection;
|
|
|
|
|
use futures::{sink::SinkExt, stream::StreamExt};
|
|
|
|
|
use log::{error, info, trace, warn};
|
|
|
|
|
use serde_json;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use tokio::sync::mpsc;
|
|
|
|
|
use tokio::sync::Mutex as AsyncMutex;
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
pub mod channels;
|
|
|
|
|
pub mod multimedia;
|
|
|
|
|
|
|
|
|
|
pub fn get_default_bot(conn: &mut PgConnection) -> (Uuid, String) {
|
|
|
|
|
use crate::shared::models::schema::bots::dsl::*;
|
|
|
|
|
use diesel::prelude::*;
|
2025-11-27 23:10:43 -03:00
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
match bots
|
|
|
|
|
.filter(is_active.eq(true))
|
|
|
|
|
.select((id, name))
|
|
|
|
|
.first::<(Uuid, String)>(conn)
|
|
|
|
|
.optional()
|
|
|
|
|
{
|
|
|
|
|
Ok(Some((bot_id, bot_name))) => (bot_id, bot_name),
|
|
|
|
|
Ok(None) => {
|
|
|
|
|
warn!("No active bots found, using nil UUID");
|
|
|
|
|
(Uuid::nil(), "default".to_string())
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to query default bot: {}", e);
|
|
|
|
|
(Uuid::nil(), "default".to_string())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct BotOrchestrator {
|
|
|
|
|
pub state: Arc<AppState>,
|
|
|
|
|
pub mounted_bots: Arc<AsyncMutex<HashMap<String, Arc<DriveMonitor>>>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BotOrchestrator {
|
|
|
|
|
pub fn new(state: Arc<AppState>) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
state,
|
|
|
|
|
mounted_bots: Arc::new(AsyncMutex::new(HashMap::new())),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
pub fn mount_all_bots(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
2025-11-27 23:10:43 -03:00
|
|
|
info!("mount_all_bots called");
|
2025-11-22 22:55:35 -03:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-22 19:45:18 -03:00
|
|
|
#[cfg(feature = "llm")]
|
2025-11-22 22:55:35 -03:00
|
|
|
pub async fn stream_response(
|
|
|
|
|
&self,
|
|
|
|
|
message: UserMessage,
|
|
|
|
|
response_tx: mpsc::Sender<BotResponse>,
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
trace!(
|
|
|
|
|
"Streaming response for user: {}, session: {}",
|
|
|
|
|
message.user_id,
|
|
|
|
|
message.session_id
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let user_id = Uuid::parse_str(&message.user_id)?;
|
|
|
|
|
let session_id = Uuid::parse_str(&message.session_id)?;
|
|
|
|
|
let bot_id = Uuid::parse_str(&message.bot_id).unwrap_or_default();
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
let (session, context_data, history, model, key) = {
|
2025-11-22 22:55:35 -03:00
|
|
|
let state_clone = self.state.clone();
|
|
|
|
|
tokio::task::spawn_blocking(
|
|
|
|
|
move || -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
let session = {
|
|
|
|
|
let mut sm = state_clone.session_manager.blocking_lock();
|
|
|
|
|
sm.get_session_by_id(session_id)?
|
|
|
|
|
}
|
2025-12-26 08:59:25 -03:00
|
|
|
.ok_or("Session not found")?;
|
2025-11-22 22:55:35 -03:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut sm = state_clone.session_manager.blocking_lock();
|
|
|
|
|
sm.save_message(session.id, user_id, 1, &message.content, 1)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let context_data = {
|
|
|
|
|
let sm = state_clone.session_manager.blocking_lock();
|
2025-12-26 08:59:25 -03:00
|
|
|
sm.get_session_context_data(&session.id, &session.user_id)?
|
2025-11-22 22:55:35 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let history = {
|
|
|
|
|
let mut sm = state_clone.session_manager.blocking_lock();
|
|
|
|
|
sm.get_conversation_history(session.id, user_id)?
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let config_manager = ConfigManager::new(state_clone.conn.clone());
|
|
|
|
|
let model = config_manager
|
|
|
|
|
.get_config(&bot_id, "llm-model", Some("gpt-3.5-turbo"))
|
|
|
|
|
.unwrap_or_else(|_| "gpt-3.5-turbo".to_string());
|
|
|
|
|
let key = config_manager
|
|
|
|
|
.get_config(&bot_id, "llm-key", Some(""))
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
Ok((session, context_data, history, model, key))
|
2025-11-22 22:55:35 -03:00
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.await??
|
|
|
|
|
};
|
|
|
|
|
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
let system_prompt = "You are a helpful assistant.".to_string();
|
2025-12-10 18:29:34 -03:00
|
|
|
let messages = OpenAIClient::build_messages(&system_prompt, &context_data, &history);
|
2025-11-22 22:55:35 -03:00
|
|
|
|
|
|
|
|
let (stream_tx, mut stream_rx) = mpsc::channel::<String>(100);
|
|
|
|
|
let llm = self.state.llm_provider.clone();
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
let model_clone = model.clone();
|
|
|
|
|
let key_clone = key.clone();
|
2025-11-28 09:27:29 -03:00
|
|
|
let messages_clone = messages.clone();
|
2025-11-22 22:55:35 -03:00
|
|
|
tokio::spawn(async move {
|
|
|
|
|
if let Err(e) = llm
|
2025-11-28 09:27:29 -03:00
|
|
|
.generate_stream("", &messages_clone, stream_tx, &model_clone, &key_clone)
|
2025-11-22 22:55:35 -03:00
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
error!("LLM streaming error: {}", e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let mut full_response = String::new();
|
2025-11-27 23:10:43 -03:00
|
|
|
let mut analysis_buffer = String::new();
|
|
|
|
|
let mut in_analysis = false;
|
|
|
|
|
let handler = llm_models::get_handler(&model);
|
2025-11-22 22:55:35 -03:00
|
|
|
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
trace!("Using model handler for {}", model);
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
#[cfg(feature = "nvidia")]
|
|
|
|
|
{
|
|
|
|
|
let initial_tokens = crate::shared::utils::estimate_token_count(&context_data);
|
|
|
|
|
let config_manager = ConfigManager::new(self.state.conn.clone());
|
|
|
|
|
let max_context_size = config_manager
|
|
|
|
|
.get_config(&bot_id, "llm-server-ctx-size", None)
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
.parse::<usize>()
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
if let Ok(metrics) = get_system_metrics() {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"\nNVIDIA: {:.1}% | CPU: {:.1}% | Tokens: {}/{}",
|
|
|
|
|
metrics.gpu_usage.unwrap_or(0.0),
|
|
|
|
|
metrics.cpu_usage,
|
|
|
|
|
initial_tokens,
|
|
|
|
|
max_context_size
|
|
|
|
|
);
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
while let Some(chunk) = stream_rx.recv().await {
|
|
|
|
|
trace!("Received LLM chunk: {:?}", chunk);
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
analysis_buffer.push_str(&chunk);
|
2025-11-22 22:55:35 -03:00
|
|
|
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
if !in_analysis && handler.has_analysis_markers(&analysis_buffer) {
|
2025-11-27 23:10:43 -03:00
|
|
|
in_analysis = true;
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
log::debug!(
|
|
|
|
|
"Detected start of thinking/analysis content for model {}",
|
|
|
|
|
model
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let processed = handler.process_content(&analysis_buffer);
|
|
|
|
|
if !processed.is_empty() && processed != analysis_buffer {
|
|
|
|
|
full_response.push_str(&processed);
|
|
|
|
|
|
|
|
|
|
let response = BotResponse {
|
|
|
|
|
bot_id: message.bot_id.clone(),
|
|
|
|
|
user_id: message.user_id.clone(),
|
|
|
|
|
session_id: message.session_id.clone(),
|
|
|
|
|
channel: message.channel.clone(),
|
|
|
|
|
content: processed,
|
2025-11-28 18:15:09 -03:00
|
|
|
message_type: MessageType::BOT_RESPONSE,
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
stream_token: None,
|
|
|
|
|
is_complete: false,
|
|
|
|
|
suggestions: Vec::new(),
|
|
|
|
|
context_name: None,
|
|
|
|
|
context_length: 0,
|
|
|
|
|
context_max_length: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if response_tx.send(response).await.is_err() {
|
|
|
|
|
warn!("Response channel closed");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-23 18:40:58 -03:00
|
|
|
continue;
|
2025-11-27 23:10:43 -03:00
|
|
|
}
|
2025-11-22 22:55:35 -03:00
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
if in_analysis && handler.is_analysis_complete(&analysis_buffer) {
|
|
|
|
|
in_analysis = false;
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
log::debug!(
|
|
|
|
|
"Detected end of thinking/analysis content for model {}",
|
|
|
|
|
model
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let processed = handler.process_content(&analysis_buffer);
|
|
|
|
|
if !processed.is_empty() {
|
|
|
|
|
full_response.push_str(&processed);
|
|
|
|
|
|
|
|
|
|
let response = BotResponse {
|
|
|
|
|
bot_id: message.bot_id.clone(),
|
|
|
|
|
user_id: message.user_id.clone(),
|
|
|
|
|
session_id: message.session_id.clone(),
|
|
|
|
|
channel: message.channel.clone(),
|
|
|
|
|
content: processed,
|
2025-11-28 18:15:09 -03:00
|
|
|
message_type: MessageType::BOT_RESPONSE,
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
stream_token: None,
|
|
|
|
|
is_complete: false,
|
|
|
|
|
suggestions: Vec::new(),
|
|
|
|
|
context_name: None,
|
|
|
|
|
context_length: 0,
|
|
|
|
|
context_max_length: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if response_tx.send(response).await.is_err() {
|
|
|
|
|
warn!("Response channel closed");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
analysis_buffer.clear();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
if in_analysis {
|
|
|
|
|
trace!("Accumulating thinking content, not sending to user");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
if !in_analysis {
|
|
|
|
|
full_response.push_str(&chunk);
|
|
|
|
|
|
|
|
|
|
let response = BotResponse {
|
|
|
|
|
bot_id: message.bot_id.clone(),
|
|
|
|
|
user_id: message.user_id.clone(),
|
|
|
|
|
session_id: message.session_id.clone(),
|
|
|
|
|
channel: message.channel.clone(),
|
|
|
|
|
content: chunk,
|
2025-11-28 18:15:09 -03:00
|
|
|
message_type: MessageType::BOT_RESPONSE,
|
2025-11-27 23:10:43 -03:00
|
|
|
stream_token: None,
|
|
|
|
|
is_complete: false,
|
|
|
|
|
suggestions: Vec::new(),
|
|
|
|
|
context_name: None,
|
|
|
|
|
context_length: 0,
|
|
|
|
|
context_max_length: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if response_tx.send(response).await.is_err() {
|
|
|
|
|
warn!("Response channel closed");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-22 22:55:35 -03:00
|
|
|
|
|
|
|
|
let state_for_save = self.state.clone();
|
|
|
|
|
let full_response_clone = full_response.clone();
|
|
|
|
|
tokio::task::spawn_blocking(
|
|
|
|
|
move || -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
let mut sm = state_for_save.session_manager.blocking_lock();
|
|
|
|
|
sm.save_message(session.id, user_id, 2, &full_response_clone, 2)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.await??;
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
let final_response = BotResponse {
|
|
|
|
|
bot_id: message.bot_id,
|
|
|
|
|
user_id: message.user_id,
|
|
|
|
|
session_id: message.session_id,
|
|
|
|
|
channel: message.channel,
|
|
|
|
|
content: full_response,
|
2025-11-28 18:15:09 -03:00
|
|
|
message_type: MessageType::BOT_RESPONSE,
|
2025-11-27 23:10:43 -03:00
|
|
|
stream_token: None,
|
|
|
|
|
is_complete: true,
|
|
|
|
|
suggestions: Vec::new(),
|
|
|
|
|
context_name: None,
|
|
|
|
|
context_length: 0,
|
|
|
|
|
context_max_length: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
response_tx.send(final_response).await?;
|
2025-11-22 22:55:35 -03:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-22 19:45:18 -03:00
|
|
|
#[cfg(not(feature = "llm"))]
|
|
|
|
|
pub async fn stream_response(
|
|
|
|
|
&self,
|
|
|
|
|
message: UserMessage,
|
|
|
|
|
response_tx: mpsc::Sender<BotResponse>,
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
warn!("LLM feature not enabled, cannot stream response");
|
|
|
|
|
|
|
|
|
|
let error_response = BotResponse {
|
|
|
|
|
bot_id: message.bot_id,
|
|
|
|
|
user_id: message.user_id,
|
|
|
|
|
session_id: message.session_id,
|
|
|
|
|
channel: message.channel,
|
|
|
|
|
content: "LLM feature is not enabled in this build".to_string(),
|
|
|
|
|
message_type: MessageType::BOT_RESPONSE,
|
|
|
|
|
stream_token: None,
|
|
|
|
|
is_complete: true,
|
|
|
|
|
suggestions: Vec::new(),
|
|
|
|
|
context_name: None,
|
|
|
|
|
context_length: 0,
|
|
|
|
|
context_max_length: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
response_tx.send(error_response).await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
pub async fn get_user_sessions(
|
|
|
|
|
&self,
|
|
|
|
|
user_id: Uuid,
|
|
|
|
|
) -> Result<Vec<UserSession>, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
let mut session_manager = self.state.session_manager.lock().await;
|
|
|
|
|
let sessions = session_manager.get_user_sessions(user_id)?;
|
|
|
|
|
Ok(sessions)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_conversation_history(
|
|
|
|
|
&self,
|
|
|
|
|
session_id: Uuid,
|
|
|
|
|
user_id: Uuid,
|
|
|
|
|
) -> Result<Vec<(String, String)>, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
let mut session_manager = self.state.session_manager.lock().await;
|
|
|
|
|
let history = session_manager.get_conversation_history(session_id, user_id)?;
|
|
|
|
|
Ok(history)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn websocket_handler(
|
|
|
|
|
ws: WebSocketUpgrade,
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Query(params): Query<HashMap<String, String>>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let session_id = params
|
|
|
|
|
.get("session_id")
|
|
|
|
|
.and_then(|s| Uuid::parse_str(s).ok());
|
|
|
|
|
let user_id = params.get("user_id").and_then(|s| Uuid::parse_str(s).ok());
|
|
|
|
|
|
|
|
|
|
if session_id.is_none() || user_id.is_none() {
|
|
|
|
|
return (
|
|
|
|
|
StatusCode::BAD_REQUEST,
|
|
|
|
|
Json(serde_json::json!({ "error": "session_id and user_id are required" })),
|
|
|
|
|
)
|
|
|
|
|
.into_response();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 21:26:08 -03:00
|
|
|
let session_id = session_id.unwrap_or_default();
|
|
|
|
|
let user_id = user_id.unwrap_or_default();
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
ws.on_upgrade(move |socket| {
|
2025-12-28 21:26:08 -03:00
|
|
|
handle_websocket(socket, state, session_id, user_id)
|
2025-11-22 22:55:35 -03:00
|
|
|
})
|
|
|
|
|
.into_response()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_websocket(
|
|
|
|
|
socket: WebSocket,
|
|
|
|
|
state: Arc<AppState>,
|
|
|
|
|
session_id: Uuid,
|
|
|
|
|
user_id: Uuid,
|
|
|
|
|
) {
|
|
|
|
|
let (mut sender, mut receiver) = socket.split();
|
|
|
|
|
let (tx, mut rx) = mpsc::channel::<BotResponse>(100);
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.web_adapter
|
|
|
|
|
.add_connection(session_id.to_string(), tx.clone())
|
|
|
|
|
.await;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut channels = state.response_channels.lock().await;
|
|
|
|
|
channels.insert(session_id.to_string(), tx.clone());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!(
|
|
|
|
|
"WebSocket connected for session: {}, user: {}",
|
|
|
|
|
session_id, user_id
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let welcome = serde_json::json!({
|
|
|
|
|
"type": "connected",
|
|
|
|
|
"session_id": session_id,
|
|
|
|
|
"user_id": user_id,
|
|
|
|
|
"message": "Connected to bot server"
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if let Ok(welcome_str) = serde_json::to_string(&welcome) {
|
2025-12-26 08:59:25 -03:00
|
|
|
if sender.send(Message::Text(welcome_str)).await.is_err() {
|
2025-11-27 23:10:43 -03:00
|
|
|
error!("Failed to send welcome message");
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut send_task = tokio::spawn(async move {
|
|
|
|
|
while let Some(response) = rx.recv().await {
|
|
|
|
|
if let Ok(json_str) = serde_json::to_string(&response) {
|
2025-12-26 08:59:25 -03:00
|
|
|
if sender.send(Message::Text(json_str)).await.is_err() {
|
2025-11-22 22:55:35 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let state_clone = state.clone();
|
|
|
|
|
let mut recv_task = tokio::spawn(async move {
|
|
|
|
|
while let Some(Ok(msg)) = receiver.next().await {
|
|
|
|
|
match msg {
|
|
|
|
|
Message::Text(text) => {
|
2025-11-27 23:10:43 -03:00
|
|
|
info!("Received WebSocket message: {}", text);
|
|
|
|
|
if let Ok(user_msg) = serde_json::from_str::<UserMessage>(&text) {
|
|
|
|
|
let orchestrator = BotOrchestrator::new(state_clone.clone());
|
|
|
|
|
if let Some(tx_clone) = state_clone
|
|
|
|
|
.response_channels
|
|
|
|
|
.lock()
|
2025-11-22 22:55:35 -03:00
|
|
|
.await
|
2025-11-27 23:10:43 -03:00
|
|
|
.get(&session_id.to_string())
|
|
|
|
|
{
|
|
|
|
|
if let Err(e) = orchestrator
|
|
|
|
|
.stream_response(user_msg, tx_clone.clone())
|
|
|
|
|
.await
|
2025-11-22 22:55:35 -03:00
|
|
|
{
|
2025-11-27 23:10:43 -03:00
|
|
|
error!("Failed to stream response: {}", e);
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Message::Close(_) => {
|
2025-11-27 23:10:43 -03:00
|
|
|
info!("WebSocket close message received");
|
2025-11-22 22:55:35 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tokio::select! {
|
2025-11-27 23:10:43 -03:00
|
|
|
_ = (&mut send_task) => { recv_task.abort(); }
|
|
|
|
|
_ = (&mut recv_task) => { send_task.abort(); }
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.web_adapter
|
|
|
|
|
.remove_connection(&session_id.to_string())
|
|
|
|
|
.await;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut channels = state.response_channels.lock().await;
|
|
|
|
|
channels.remove(&session_id.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!("WebSocket disconnected for session: {}", session_id);
|
|
|
|
|
}
|
|
|
|
|
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
pub fn create_bot_handler(
|
2025-11-22 22:55:35 -03:00
|
|
|
Extension(state): Extension<Arc<AppState>>,
|
|
|
|
|
Json(payload): Json<HashMap<String, String>>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let bot_name = payload
|
|
|
|
|
.get("bot_name")
|
|
|
|
|
.cloned()
|
|
|
|
|
.unwrap_or_else(|| "default".to_string());
|
|
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
let orchestrator = BotOrchestrator::new(state);
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
if let Err(e) = orchestrator.mount_all_bots() {
|
2025-11-27 23:10:43 -03:00
|
|
|
error!("Failed to mount bots: {}", e);
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
2025-11-27 23:10:43 -03:00
|
|
|
|
|
|
|
|
(
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(serde_json::json!({ "status": format!("bot '{}' created", bot_name) })),
|
|
|
|
|
)
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
pub fn mount_bot_handler(
|
2025-11-27 15:19:17 -03:00
|
|
|
Extension(state): Extension<Arc<AppState>>,
|
2025-11-22 22:55:35 -03:00
|
|
|
Json(payload): Json<HashMap<String, String>>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let bot_guid = payload.get("bot_guid").cloned().unwrap_or_default();
|
2025-11-27 15:19:17 -03:00
|
|
|
|
2025-11-27 23:10:43 -03:00
|
|
|
let orchestrator = BotOrchestrator::new(state);
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
if let Err(e) = orchestrator.mount_all_bots() {
|
2025-11-27 23:10:43 -03:00
|
|
|
error!("Failed to mount bot: {}", e);
|
|
|
|
|
}
|
2025-11-27 15:19:17 -03:00
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
(
|
|
|
|
|
StatusCode::OK,
|
2025-11-27 23:10:43 -03:00
|
|
|
Json(serde_json::json!({ "status": format!("bot '{}' mounted", bot_guid) })),
|
2025-11-22 22:55:35 -03:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn handle_user_input_handler(
|
2025-11-27 23:10:43 -03:00
|
|
|
Extension(state): Extension<Arc<AppState>>,
|
2025-11-22 22:55:35 -03:00
|
|
|
Json(payload): Json<HashMap<String, String>>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let session_id = payload.get("session_id").cloned().unwrap_or_default();
|
|
|
|
|
let user_input = payload.get("input").cloned().unwrap_or_default();
|
2025-11-27 23:10:43 -03:00
|
|
|
|
|
|
|
|
info!(
|
|
|
|
|
"Processing user input: {} for session: {}",
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
user_input, session_id
|
2025-11-27 23:10:43 -03:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let orchestrator = BotOrchestrator::new(state);
|
|
|
|
|
if let Ok(sessions) = orchestrator.get_user_sessions(Uuid::nil()).await {
|
|
|
|
|
info!("Found {} sessions", sessions.len());
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
(
|
|
|
|
|
StatusCode::OK,
|
2025-11-27 23:10:43 -03:00
|
|
|
Json(serde_json::json!({ "status": format!("processed: {}", user_input) })),
|
2025-11-22 22:55:35 -03:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_user_sessions_handler(
|
2025-11-27 23:10:43 -03:00
|
|
|
Extension(state): Extension<Arc<AppState>>,
|
|
|
|
|
Json(payload): Json<HashMap<String, String>>,
|
2025-11-22 22:55:35 -03:00
|
|
|
) -> impl IntoResponse {
|
2025-11-27 23:10:43 -03:00
|
|
|
let user_id = payload
|
|
|
|
|
.get("user_id")
|
|
|
|
|
.and_then(|id| Uuid::parse_str(id).ok())
|
|
|
|
|
.unwrap_or_else(Uuid::nil);
|
|
|
|
|
|
|
|
|
|
let orchestrator = BotOrchestrator::new(state);
|
|
|
|
|
match orchestrator.get_user_sessions(user_id).await {
|
|
|
|
|
Ok(sessions) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(serde_json::json!({ "sessions": sessions })),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to get sessions: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(serde_json::json!({ "error": e.to_string() })),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_conversation_history_handler(
|
2025-11-27 23:10:43 -03:00
|
|
|
Extension(state): Extension<Arc<AppState>>,
|
|
|
|
|
Json(payload): Json<HashMap<String, String>>,
|
2025-11-22 22:55:35 -03:00
|
|
|
) -> impl IntoResponse {
|
2025-11-27 23:10:43 -03:00
|
|
|
let session_id = payload
|
|
|
|
|
.get("session_id")
|
|
|
|
|
.and_then(|id| Uuid::parse_str(id).ok())
|
|
|
|
|
.unwrap_or_else(Uuid::nil);
|
|
|
|
|
let user_id = payload
|
|
|
|
|
.get("user_id")
|
|
|
|
|
.and_then(|id| Uuid::parse_str(id).ok())
|
|
|
|
|
.unwrap_or_else(Uuid::nil);
|
|
|
|
|
|
|
|
|
|
let orchestrator = BotOrchestrator::new(state);
|
|
|
|
|
match orchestrator
|
|
|
|
|
.get_conversation_history(session_id, user_id)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Ok(history) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(serde_json::json!({ "history": history })),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to get history: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(serde_json::json!({ "error": e.to_string() })),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn send_warning_handler(
|
2025-11-27 23:10:43 -03:00
|
|
|
Extension(state): Extension<Arc<AppState>>,
|
|
|
|
|
Json(payload): Json<HashMap<String, String>>,
|
2025-11-22 22:55:35 -03:00
|
|
|
) -> impl IntoResponse {
|
2025-11-27 23:10:43 -03:00
|
|
|
let message = payload
|
|
|
|
|
.get("message")
|
|
|
|
|
.cloned()
|
|
|
|
|
.unwrap_or_else(|| "Warning".to_string());
|
|
|
|
|
let session_id = payload.get("session_id").cloned().unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
warn!("Warning for session {}: {}", session_id, message);
|
|
|
|
|
|
|
|
|
|
let orchestrator = BotOrchestrator::new(state);
|
|
|
|
|
info!("Orchestrator created for warning");
|
|
|
|
|
|
|
|
|
|
if let Ok(sessions) = orchestrator.get_user_sessions(Uuid::nil()).await {
|
|
|
|
|
info!("Current active sessions: {}", sessions.len());
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
(
|
|
|
|
|
StatusCode::OK,
|
2025-11-27 23:10:43 -03:00
|
|
|
Json(serde_json::json!({ "status": "warning sent", "message": message })),
|
2025-11-22 22:55:35 -03:00
|
|
|
)
|
|
|
|
|
}
|