Various updates: dependencies, features, and bug fixes

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-01-16 11:29:22 -03:00
parent f42ae6e57c
commit 033bb504b9
24 changed files with 248 additions and 8451 deletions

View file

@ -218,7 +218,7 @@ sha256 = "f96935e7e385e3b2d0189239077c10fe8fd7e95690fea4afec455b1b6c7e3f18"
# Optional larger models (uncomment to include)
# [models.gpt_oss_20b]
# name = "GPT-OSS 20B F16 (requires 16GB+ VRAM)"
# name = "GPT-OSS 20B F16 (requires 16GB+ VRAM or MoE)"
# url = "https://huggingface.co/unsloth/gpt-oss-20b-GGUF/resolve/main/gpt-oss-20b-F16.gguf"
# filename = "gpt-oss-20b-F16.gguf"
# sha256 = ""

8201
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,41 +2,8 @@
name = "botserver"
version = "6.1.0"
edition = "2021"
keywords = ["chatbot", "ai", "llm", "automation", "bot-framework"]
categories = ["web-programming", "api-bindings", "development-tools"]
authors = [
"Pragmatismo.com.br ",
"General Bots Community ",
"Alan Perdomo",
"Ana Paula Gil",
"Arenas.io",
"Atylla L",
"Christopher de Castilho",
"Dario Junior",
"David Lerner",
"Experimentation Garage",
"Flavio Andrade",
"Heraldo Almeida",
"Joao Parana",
"Jonathas C",
"J Ramos",
"Lucas Picanco",
"Marcos Velasco",
"Matheus 39x",
"Oerlabs Henrique",
"Othon Lima",
"PH Nascimento",
"Phpussente",
"Robson Dantas",
"Rodrigo Rodriguez ",
"Sarah Lourenco",
"Thi Patriota",
"Webgus",
"Zuilho Se",
]
description = "General Bots Server - Open-source bot platform by Pragmatismo.com.br"
license = "AGPL-3.0"
repository = "https://github.com/GeneralBots/BotServer"
# ... [authors, description, license, repository sections remain the same]
[dependencies.botlib]
path = "../botlib"
@ -44,62 +11,106 @@ features = ["database", "i18n"]
[features]
# ===== DEFAULT FEATURE SET =====
default = ["console", "chat", "automation", "tasks", "drive", "llm", "cache", "progress-bars", "directory", "calendar", "meet", "email", "whatsapp", "telegram", "learn"]
default = ["chat", "drive", "tasks", "automation"]
# ===== UI FEATURES =====
console = ["dep:crossterm", "dep:ratatui", "monitoring"]
# ===== CORE INTEGRATIONS =====
vectordb = ["dep:qdrant-client"]
llm = []
nvidia = []
# ===== COMMUNICATION CHANNELS =====
email = ["dep:imap", "dep:lettre", "dep:mailparse", "dep:native-tls"]
# ===== COMMUNICATION APPS =====
chat = []
people = []
mail = ["email", "imap", "lettre", "mailparse", "native-tls"]
meet = ["dep:livekit"]
social = []
whatsapp = []
telegram = []
instagram = []
msteams = []
communications = ["chat", "people", "mail", "meet", "social", "whatsapp", "telegram", "instagram", "msteams", "cache"]
# ===== PRODUCTIVITY FEATURES =====
chat = []
drive = ["dep:aws-config", "dep:aws-sdk-s3", "dep:pdf-extract", "dep:zip", "dep:downloader", "dep:flate2", "dep:tar"]
tasks = ["dep:cron"]
# ===== PRODUCTIVITY APPS =====
calendar = []
meet = ["dep:livekit"]
mail = ["email"]
tasks = ["dep:cron"]
project = []
goals = []
workspace = []
productivity = ["calendar", "tasks", "project", "goals", "workspace", "cache"]
# ===== DOCUMENT APPS =====
paper = ["docx-rs", "ooxmlsdk", "dep:pdf-extract"]
docs = ["docx-rs", "ooxmlsdk"]
sheet = ["umya-spreadsheet", "calamine", "rust_xlsxwriter", "spreadsheet-ods"]
slides = ["ooxmlsdk"]
drive = ["dep:aws-config", "dep:aws-sdk-s3", "dep:pdf-extract", "dep:zip", "dep:downloader", "dep:flate2", "dep:tar"]
documents = ["paper", "docs", "sheet", "slides", "drive"]
# ===== MEDIA APPS =====
video = []
player = []
canvas = []
media = ["video", "player", "canvas"]
# ===== LEARNING & RESEARCH APPS =====
learn = []
research = ["llm", "vectordb"]
sources = []
learning = ["learn", "research", "sources"]
# ===== ENTERPRISE FEATURES =====
compliance = ["dep:csv"]
attendance = ["drive"]
directory = []
weba = []
timeseries = []
# ===== OPTIONAL INFRASTRUCTURE =====
cache = ["dep:redis"]
# ===== ANALYTICS APPS =====
analytics = []
dashboards = []
monitoring = ["dep:sysinfo"]
automation = ["dep:rhai"]
grpc = ["dep:tonic"]
progress-bars = ["dep:indicatif"]
jemalloc = ["dep:tikv-jemallocator", "dep:tikv-jemalloc-ctl"]
analytics_suite = ["analytics", "dashboards", "monitoring"]
# ===== META FEATURES (BUNDLES) =====
# ===== DEVELOPMENT TOOLS =====
designer = []
editor = []
automation = ["dep:rhai", "llm"]
development = ["designer", "editor", "automation"]
# ===== ADMIN APPS =====
attendant = []
security = []
settings = []
admin = ["attendant", "security", "settings"]
# ===== CORE TECHNOLOGIES =====
llm = []
vectordb = ["dep:qdrant-client"]
nvidia = []
email = []
cache = ["dep:redis"]
compliance = ["dep:csv"]
timeseries = []
weba = []
directory = []
progress-bars = ["dep:indicatif"]
grpc = ["dep:tonic"]
jemalloc = ["dep:tikv-jemallocator", "dep:tikv-jemalloc-ctl"]
console = ["dep:crossterm", "dep:ratatui", "monitoring"]
# ===== BUNDLE FEATURES =====
full = [
"console",
"vectordb", "llm", "nvidia", "timeseries",
"email", "whatsapp", "instagram", "msteams",
"chat", "drive", "tasks", "calendar", "meet", "mail",
"compliance", "attendance", "directory", "weba",
"cache", "monitoring", "automation", "grpc", "progress-bars"
# Communication
"chat", "people", "mail", "meet", "social", "whatsapp", "telegram", "instagram", "msteams",
# Productivity
"calendar", "tasks", "project", "goals", "workspace",
# Documents
"paper", "docs", "sheet", "slides", "drive",
# Media
"video", "player", "canvas",
# Learning
"learn", "research", "sources",
# Analytics
"analytics", "dashboards", "monitoring",
# Development
"designer", "editor", "automation",
# Admin
"attendant", "security", "settings",
# Core tech
"llm", "vectordb", "nvidia", "cache", "compliance", "timeseries", "weba", "directory",
"progress-bars", "grpc", "jemalloc", "console"
]
communications = ["email", "whatsapp", "instagram", "msteams", "chat", "cache"]
productivity = ["chat", "drive", "tasks", "calendar", "meet", "mail", "cache", "learn"]
enterprise = ["compliance", "attendance", "directory", "llm", "vectordb", "monitoring", "timeseries"]
minimal = ["chat"]
lightweight = ["chat", "drive", "tasks"]
lightweight = ["chat", "drive", "tasks", "people"]
[dependencies]
# === CORE RUNTIME (Always Required) ===
@ -144,7 +155,6 @@ tokio-stream = "0.1"
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "fs", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt"] }
urlencoding = "2.1"
uuid = { version = "1.11", features = ["serde", "v4", "v5"] }
@ -162,9 +172,9 @@ time = { version = "0.3", features = ["formatting", "parsing"] }
jsonwebtoken = "9.3"
tower-cookies = "0.10"
# === FEATURE-SPECIFIC DEPENDENCIES (Optional) ===
# === APP-SPECIFIC DEPENDENCIES ===
# Email Integration (email feature)
# Email Integration (mail feature)
imap = { version = "3.0.0-alpha.15", optional = true }
lettre = { version = "0.11", features = ["smtp-transport", "builder", "tokio1", "tokio1-native-tls"], optional = true }
mailparse = { version = "0.15", optional = true }
@ -176,6 +186,14 @@ livekit = { version = "0.7", optional = true }
# Vector Database (vectordb feature)
qdrant-client = { version = "1.12", optional = true }
# Document Processing (paper, docs, sheet, slides features)
docx-rs = { version = "0.4", optional = true }
ooxmlsdk = { version = "0.3", features = ["docx", "pptx", "parts", "office2021"], optional = true }
umya-spreadsheet = { version = "2.3", optional = true }
calamine = { version = "0.26", optional = true }
rust_xlsxwriter = { version = "0.79", optional = true }
spreadsheet-ods = { version = "1.0", optional = true }
# File Storage & Drive (drive feature)
aws-config = { version = "1.8.8", features = ["behavior-version-latest"], optional = true }
aws-sdk-s3 = { version = "1.109.0", features = ["behavior-version-latest"], optional = true }
@ -183,7 +201,6 @@ pdf-extract = { version = "0.10.0", optional = true }
quick-xml = { version = "0.37", features = ["serialize"] }
zip = { version = "2.2", optional = true }
downloader = { version = "0.2", optional = true }
flate2 = { version = "1.0", optional = true }
tar = { version = "0.4", optional = true }
@ -200,24 +217,10 @@ csv = { version = "1.3", optional = true }
crossterm = { version = "0.29.0", optional = true }
ratatui = { version = "0.29", optional = true }
# QR Code Generation (using png directly to avoid image's ravif/paste dependency)
# QR Code Generation
png = "0.18"
qrcode = { version = "0.14", default-features = false }
# Excel/Spreadsheet Support - MS Office 100% Compatibility
# umya-spreadsheet preserves: Charts, styles, images, formulas, macros, comments
umya-spreadsheet = "2.3"
calamine = "0.26"
rust_xlsxwriter = "0.79"
spreadsheet-ods = "1.0"
# Word/PowerPoint Support - MS Office 100% Compatibility
# ooxmlsdk preserves: Full document structure at XML level (100% round-trip)
docx-rs = "0.4"
ooxmlsdk = { version = "0.3", features = ["docx", "pptx", "parts", "office2021"] }
# ppt-rs disabled due to version conflict - using ooxmlsdk for PPTX support instead
# ppt-rs = { version = "0.2", default-features = false }
# Error handling
thiserror = "2.0"
@ -268,6 +271,5 @@ http-body-util = "0.1.3"
mockito = "1.7.0"
tempfile = "3"
# === SECURITY AND CODE QUALITY CONFIGURATION ===
[lints]
workspace = true

View file

@ -27,6 +27,7 @@ General Bots is a **self-hosted AI automation platform** that provides:
- **Rust** (1.75+) - [Install from rustup.rs](https://rustup.rs/)
- **Git** - [Download from git-scm.com](https://git-scm.com/downloads)
- Mold sudo apt-get install mold
### Installation

View file

@ -13,8 +13,7 @@ command -v xclip >/dev/null 2>&1 || { echo "xclip is required but not installed"
echo "Please, fix this consolidated LLM Context" > "$OUTPUT_FILE"
prompts=(
"./prompts/dev/platform/fix-errors.md"
"./prompts/dev/platform/shared.md"
"./PROMPT.md"
"./Cargo.toml"
)
@ -32,29 +31,7 @@ for file in "${prompts[@]}"; do
done
dirs=(
# "auth"
# "automation"
#"basic"
# "bot"
"bootstrap"
# "package_manager"
# "channels"
# "config"
# "context"
# "email"
# "file"
# "llm"
"drive_monitor"
# "llm_legacy"
# "org"
# "session"
"file"
"kb"
"shared"
#"tests"
# "tools"
# "web_automation"
# "whatsapp"
)
for dir in "${dirs[@]}"; do
if [ -d "$PROJECT_ROOT/src/$dir" ]; then
@ -72,12 +49,36 @@ done
echo "$PROJECT_ROOT/src/main.rs" >> "$OUTPUT_FILE"
cat "$PROJECT_ROOT/src/main.rs" >> "$OUTPUT_FILE"
echo "$PROJECT_ROOT/src/basic/keywords/get.rs" >> "$OUTPUT_FILE"
cat "$PROJECT_ROOT/src/basic/keywords/get.rs" >> "$OUTPUT_FILE"
# Files with config import errors
error_files=(
"src/main.rs"
"src/basic/keywords/kb_statistics.rs"
"src/core/bootstrap/mod.rs"
"src/core/kb/kb_indexer.rs"
"src/core/kb/website_crawler_service.rs"
"src/core/shared/utils.rs"
"src/multimodal/mod.rs"
"src/console/status_panel.rs"
"src/drive/drive_monitor/mod.rs"
"src/email/mod.rs"
"src/llm/cache.rs"
"src/llm/local.rs"
"src/llm/episodic_memory.rs"
"src/basic/keywords/create_site.rs"
"src/basic/keywords/save_from_unstructured.rs"
)
for file in "${error_files[@]}"; do
echo "$PROJECT_ROOT/$file" >> "$OUTPUT_FILE"
cat "$PROJECT_ROOT/$file" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "---" >> "$OUTPUT_FILE"
done
echo "" >> "$OUTPUT_FILE"
echo "Compiling..."
cargo build --message-format=short 2>&1 | grep -E 'error' >> "$OUTPUT_FILE"
cargo build --message-format=short 2>&1 | grep -E 'error' >> "$OUTPUT_FILE"
# Calculate and display token count (approximation: words * 1.3)

View file

@ -57,7 +57,7 @@ pub fn create_site_keyword(state: &AppState, user: UserSession, engine: &mut Eng
}
async fn create_site(
config: crate::config::AppConfig,
config: crate::core::config::AppConfig,
s3: Option<std::sync::Arc<aws_sdk_s3::Client>>,
bucket: String,
bot_id: String,

View file

@ -1,4 +1,4 @@
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::shared::models::UserSession;
use crate::shared::state::AppState;
use crate::shared::utils::create_tls_client;

View file

@ -1,5 +1,4 @@
use crate::bot::get_default_bot;
use crate::core::shared::schema::products;
use crate::multimodal::BotModelsClient;
use crate::shared::models::UserSession;
use crate::shared::state::AppState;
@ -9,7 +8,6 @@ use diesel::sql_types::{Integer, Text};
use log::{error, trace};
use rhai::{Dynamic, Engine};
use serde_json::{json, Value};
use std::sync::Arc;
use std::time::Duration;
#[derive(QueryableByName)]

View file

@ -260,7 +260,7 @@ Return ONLY the JSON object, no explanations or markdown formatting."#,
}
async fn call_llm_for_extraction(state: &AppState, prompt: &str) -> Result<Value, String> {
let config_manager = crate::config::ConfigManager::new(state.conn.clone());
let config_manager = crate::core::config::ConfigManager::new(state.conn.clone());
let model = config_manager
.get_config(&Uuid::nil(), "llm-model", None)
.unwrap_or_else(|_| "gpt-3.5-turbo".to_string());

View file

@ -1,7 +1,6 @@
use axum::{
extract::{Path, Query, State},
response::IntoResponse,
Json,
extract::{Path, Query, State},
Json,
};
use chrono::Utc;
use diesel::prelude::*;
@ -583,8 +582,8 @@ pub async fn handle_get_report(
}
pub async fn handle_upload_evidence(
State(_state): State<Arc<AppState>>,
axum::extract::Multipart(mut multipart): axum::extract::Multipart,
State(_state): State<Arc<AppState>>,
mut multipart: axum::extract::Multipart,
) -> Result<Json<serde_json::Value>, ComplianceError> {
let mut file_name = String::new();
let mut category = String::new();

View file

@ -411,13 +411,13 @@ impl VulnerabilityScannerService {
let mut vulnerabilities = Vec::new();
let now = Utc::now();
let secret_patterns = vec![
("API Key Pattern", r"(?i)(api[_-]?key|apikey)\s*[:=]\s*['\"]?[\w-]{20,}", "CWE-798"),
("AWS Access Key", r"AKIA[0-9A-Z]{16}", "CWE-798"),
("Private Key", r"-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----", "CWE-321"),
("JWT Token", r"eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_.+/]*", "CWE-522"),
("Database URL", r"(?i)(postgres|mysql|mongodb)://[^\s]+:[^\s]+@", "CWE-798"),
];
let secret_patterns = vec![
("API Key Pattern", r#"(?i)(api[_-]?key|apikey)\s*[:=]\s*['"]?[\w-]{20,}"#, "CWE-798"),
("AWS Access Key ", r"AKIA[0-9A-Z]{16}", "CWE-798"),
("Private Key ", r"-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----", "CWE-321"),
("JWT Token ", r#"eyJ[A-Za-z0-9-]+.[A-Za-z0-9-]+.[A-Za-z0-9-_.+/]*"#, "CWE-522"),
("Database URL ", r#"(?i)(postgres|mysql|mongodb)://[^\s]+:[^\s]+@"#, "CWE-798"),
];
for (name, pattern, cwe) in secret_patterns {
let regex_result = regex::Regex::new(pattern);

View file

@ -1,4 +1,4 @@
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
#[cfg(feature = "nvidia")]
use crate::nvidia::get_system_metrics;
use crate::security::command_guard::SafeCommand;

View file

@ -1,4 +1,4 @@
use crate::config::AppConfig;
use crate::core::config::AppConfig;
use crate::package_manager::setup::{DirectorySetup, EmailSetup, VectorDbSetup};
use crate::package_manager::{InstallMode, PackageManager};
use crate::security::command_guard::SafeCommand;

View file

@ -5,7 +5,7 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};
use uuid::Uuid;
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::core::shared::memory_monitor::{log_jemalloc_stats, MemoryStats};
use crate::shared::utils::{create_tls_client, DbPool};

View file

@ -1,4 +1,4 @@
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::core::kb::web_crawler::{WebCrawler, WebsiteCrawlConfig};
use crate::core::kb::KnowledgeBaseManager;
use crate::shared::state::AppState;

View file

@ -1,4 +1,4 @@
use crate::config::DriveConfig;
use crate::core::config::DriveConfig;
use crate::core::secrets::SecretsManager;
use anyhow::{Context, Result};
use aws_config::BehaviorVersion;

View file

@ -1,5 +1,5 @@
use crate::basic::compiler::BasicCompiler;
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::core::kb::embedding_generator::is_embedding_server_ready;
use crate::core::kb::KnowledgeBaseManager;
use crate::core::shared::memory_monitor::{log_jemalloc_stats, MemoryStats};

View file

@ -1,6 +1,6 @@
pub mod ui;
use crate::{config::EmailConfig, core::urls::ApiUrls, shared::state::AppState};
use crate::{core::config::EmailConfig, core::urls::ApiUrls, shared::state::AppState};
use crate::core::middleware::AuthenticatedUser;
use axum::{
extract::{Path, Query, State},

View file

@ -10,7 +10,7 @@ use tokio::sync::mpsc;
use uuid::Uuid;
use super::LLMProvider;
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::shared::utils::{estimate_token_count, DbPool};
#[derive(Clone, Debug)]

View file

@ -141,7 +141,7 @@ async fn process_episodic_memory(
let llm_provider = state.llm_provider.clone();
let mut filtered = String::new();
let config_manager = crate::config::ConfigManager::new(state.conn.clone());
let config_manager = crate::core::config::ConfigManager::new(state.conn.clone());
let model = config_manager
.get_config(&Uuid::nil(), "llm-model", None)
.unwrap_or_default();

View file

@ -1,4 +1,4 @@
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::core::kb::embedding_generator::set_embedding_server_ready;
use crate::core::shared::memory_monitor::{log_jemalloc_stats, MemoryStats};
use crate::security::command_guard::SafeCommand;

View file

@ -45,7 +45,7 @@ pub mod botmodels;
pub mod legal;
pub mod settings;
#[cfg(feature = "attendance")]
#[cfg(feature = "attendant")]
pub mod attendance;
#[cfg(feature = "calendar")]
@ -139,8 +139,6 @@ use std::sync::Arc;
use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer;
use crate::embedded_ui;
async fn ensure_vendor_files_in_minio(drive: &aws_sdk_s3::Client) {
use aws_sdk_s3::primitives::ByteStream;
@ -213,7 +211,6 @@ use crate::shared::state::AppState;
use crate::shared::utils::create_conn;
use crate::shared::utils::create_s3_operator;
use crate::BootstrapProgress;
async fn health_check(State(state): State<Arc<AppState>>) -> (StatusCode, Json<serde_json::Value>) {
let db_ok = state.conn.get().is_ok();
@ -410,7 +407,7 @@ async fn run_axum_server(
.route(ApiUrls::SESSION_HISTORY, get(get_session_history))
.route(ApiUrls::SESSION_START, post(start_session))
.route(ApiUrls::WS, get(websocket_handler))
.merge(botserver::drive::configure());
.merge(crate::drive::configure());
#[cfg(feature = "directory")]
{
@ -434,14 +431,14 @@ async fn run_axum_server(
#[cfg(feature = "calendar")]
{
let calendar_engine =
Arc::new(botserver::basic::keywords::book::CalendarEngine::new(app_state.conn.clone()));
Arc::new(crate::basic::keywords::book::CalendarEngine::new(app_state.conn.clone()));
api_router = api_router.merge(crate::calendar::caldav::create_caldav_router(
calendar_engine,
));
}
api_router = api_router.merge(botserver::tasks::configure_task_routes());
api_router = api_router.merge(crate::tasks::configure_task_routes());
#[cfg(feature = "calendar")]
{
@ -449,61 +446,61 @@ async fn run_axum_server(
api_router = api_router.merge(crate::calendar::ui::configure_calendar_ui_routes());
}
api_router = api_router.merge(botserver::analytics::configure_analytics_routes());
api_router = api_router.merge(crate::analytics::configure_analytics_routes());
api_router = api_router.merge(crate::core::i18n::configure_i18n_routes());
api_router = api_router.merge(botserver::docs::configure_docs_routes());
api_router = api_router.merge(botserver::paper::configure_paper_routes());
api_router = api_router.merge(botserver::sheet::configure_sheet_routes());
api_router = api_router.merge(botserver::slides::configure_slides_routes());
api_router = api_router.merge(botserver::video::configure_video_routes());
api_router = api_router.merge(botserver::video::ui::configure_video_ui_routes());
api_router = api_router.merge(botserver::research::configure_research_routes());
api_router = api_router.merge(botserver::research::ui::configure_research_ui_routes());
api_router = api_router.merge(botserver::sources::configure_sources_routes());
api_router = api_router.merge(botserver::sources::ui::configure_sources_ui_routes());
api_router = api_router.merge(botserver::designer::configure_designer_routes());
api_router = api_router.merge(botserver::designer::ui::configure_designer_ui_routes());
api_router = api_router.merge(botserver::dashboards::configure_dashboards_routes());
api_router = api_router.merge(botserver::dashboards::ui::configure_dashboards_ui_routes());
api_router = api_router.merge(botserver::legal::configure_legal_routes());
api_router = api_router.merge(botserver::legal::ui::configure_legal_ui_routes());
api_router = api_router.merge(crate::docs::configure_docs_routes());
api_router = api_router.merge(crate::paper::configure_paper_routes());
api_router = api_router.merge(crate::sheet::configure_sheet_routes());
api_router = api_router.merge(crate::slides::configure_slides_routes());
api_router = api_router.merge(crate::video::configure_video_routes());
api_router = api_router.merge(crate::video::ui::configure_video_ui_routes());
api_router = api_router.merge(crate::research::configure_research_routes());
api_router = api_router.merge(crate::research::ui::configure_research_ui_routes());
api_router = api_router.merge(crate::sources::configure_sources_routes());
api_router = api_router.merge(crate::sources::ui::configure_sources_ui_routes());
api_router = api_router.merge(crate::designer::configure_designer_routes());
api_router = api_router.merge(crate::designer::ui::configure_designer_ui_routes());
api_router = api_router.merge(crate::dashboards::configure_dashboards_routes());
api_router = api_router.merge(crate::dashboards::ui::configure_dashboards_ui_routes());
api_router = api_router.merge(crate::legal::configure_legal_routes());
api_router = api_router.merge(crate::legal::ui::configure_legal_ui_routes());
#[cfg(feature = "compliance")]
{
api_router = api_router.merge(botserver::compliance::configure_compliance_routes());
api_router = api_router.merge(botserver::compliance::ui::configure_compliance_ui_routes());
api_router = api_router.merge(crate::compliance::configure_compliance_routes());
api_router = api_router.merge(crate::compliance::ui::configure_compliance_ui_routes());
}
api_router = api_router.merge(botserver::monitoring::configure());
api_router = api_router.merge(botserver::security::configure_protection_routes());
api_router = api_router.merge(botserver::settings::configure_settings_routes());
api_router = api_router.merge(botserver::basic::keywords::configure_db_routes());
api_router = api_router.merge(botserver::basic::keywords::configure_app_server_routes());
api_router = api_router.merge(botserver::auto_task::configure_autotask_routes());
api_router = api_router.merge(crate::monitoring::configure());
api_router = api_router.merge(crate::security::configure_protection_routes());
api_router = api_router.merge(crate::settings::configure_settings_routes());
api_router = api_router.merge(crate::basic::keywords::configure_db_routes());
api_router = api_router.merge(crate::basic::keywords::configure_app_server_routes());
api_router = api_router.merge(crate::auto_task::configure_autotask_routes());
api_router = api_router.merge(crate::core::shared::admin::configure());
api_router = api_router.merge(botserver::workspaces::configure_workspaces_routes());
api_router = api_router.merge(botserver::workspaces::ui::configure_workspaces_ui_routes());
api_router = api_router.merge(botserver::project::configure());
api_router = api_router.merge(botserver::analytics::goals::configure_goals_routes());
api_router = api_router.merge(botserver::analytics::goals_ui::configure_goals_ui_routes());
api_router = api_router.merge(botserver::player::configure_player_routes());
api_router = api_router.merge(botserver::canvas::configure_canvas_routes());
api_router = api_router.merge(botserver::canvas::ui::configure_canvas_ui_routes());
api_router = api_router.merge(botserver::social::configure_social_routes());
api_router = api_router.merge(botserver::social::ui::configure_social_ui_routes());
api_router = api_router.merge(botserver::email::ui::configure_email_ui_routes());
api_router = api_router.merge(botserver::learn::ui::configure_learn_ui_routes());
api_router = api_router.merge(botserver::meet::ui::configure_meet_ui_routes());
api_router = api_router.merge(botserver::contacts::crm_ui::configure_crm_routes());
api_router = api_router.merge(botserver::contacts::crm::configure_crm_api_routes());
api_router = api_router.merge(botserver::billing::billing_ui::configure_billing_routes());
api_router = api_router.merge(botserver::billing::api::configure_billing_api_routes());
api_router = api_router.merge(botserver::products::configure_products_routes());
api_router = api_router.merge(botserver::products::api::configure_products_api_routes());
api_router = api_router.merge(botserver::tickets::configure_tickets_routes());
api_router = api_router.merge(botserver::tickets::ui::configure_tickets_ui_routes());
api_router = api_router.merge(botserver::people::configure_people_routes());
api_router = api_router.merge(botserver::people::ui::configure_people_ui_routes());
api_router = api_router.merge(botserver::attendant::configure_attendant_routes());
api_router = api_router.merge(botserver::attendant::ui::configure_attendant_ui_routes());
api_router = api_router.merge(crate::workspaces::configure_workspaces_routes());
api_router = api_router.merge(crate::workspaces::ui::configure_workspaces_ui_routes());
api_router = api_router.merge(crate::project::configure());
api_router = api_router.merge(crate::analytics::goals::configure_goals_routes());
api_router = api_router.merge(crate::analytics::goals_ui::configure_goals_ui_routes());
api_router = api_router.merge(crate::player::configure_player_routes());
api_router = api_router.merge(crate::canvas::configure_canvas_routes());
api_router = api_router.merge(crate::canvas::ui::configure_canvas_ui_routes());
api_router = api_router.merge(crate::social::configure_social_routes());
api_router = api_router.merge(crate::social::ui::configure_social_ui_routes());
api_router = api_router.merge(crate::email::ui::configure_email_ui_routes());
api_router = api_router.merge(crate::learn::ui::configure_learn_ui_routes());
api_router = api_router.merge(crate::meet::ui::configure_meet_ui_routes());
api_router = api_router.merge(crate::contacts::crm_ui::configure_crm_routes());
api_router = api_router.merge(crate::contacts::crm::configure_crm_api_routes());
api_router = api_router.merge(crate::billing::billing_ui::configure_billing_routes());
api_router = api_router.merge(crate::billing::api::configure_billing_api_routes());
api_router = api_router.merge(crate::products::configure_products_routes());
api_router = api_router.merge(crate::products::api::configure_products_api_routes());
api_router = api_router.merge(crate::tickets::configure_tickets_routes());
api_router = api_router.merge(crate::tickets::ui::configure_tickets_ui_routes());
api_router = api_router.merge(crate::people::configure_people_routes());
api_router = api_router.merge(crate::people::ui::configure_people_ui_routes());
api_router = api_router.merge(crate::attendant::configure_attendant_routes());
api_router = api_router.merge(crate::attendant::ui::configure_attendant_ui_routes());
#[cfg(feature = "whatsapp")]
{
@ -512,10 +509,10 @@ async fn run_axum_server(
#[cfg(feature = "telegram")]
{
api_router = api_router.merge(botserver::telegram::configure());
api_router = api_router.merge(crate::telegram::configure());
}
#[cfg(feature = "attendance")]
#[cfg(feature = "attendant")]
{
api_router = api_router.merge(crate::attendance::configure_attendance_routes());
}
@ -608,21 +605,21 @@ async fn run_axum_server(
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
let rbac = Arc::clone(&rbac_manager_for_middleware);
async move {
botserver::security::rbac_middleware_fn(req, next, rbac).await
crate::security::rbac_middleware_fn(req, next, rbac).await
}
}))
// Authentication middleware - MUST run before RBAC (so added after)
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
let state = auth_middleware_state.clone();
async move {
botserver::security::auth_middleware_with_providers(req, next, state).await
crate::security::auth_middleware_with_providers(req, next, state).await
}
}))
// Panic handler catches panics and returns safe 500 responses
.layer(middleware::from_fn(move |req, next| {
let config = panic_config.clone();
async move {
botserver::security::panic_handler_middleware_with_config(req, next, &config).await
crate::security::panic_handler_middleware_with_config(req, next, &config).await
}
}))
.layer(Extension(app_state.clone()))
@ -749,8 +746,8 @@ async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", &rust_log);
use crate::llm::local::ensure_llama_servers_running;
use botserver::config::ConfigManager;
use crate::llm::local::ensure_llama_servers_running;
use crate::core::config::ConfigManager;
if no_console || no_ui {
botlib::logging::init_compact_logger_with_style("info");
@ -800,7 +797,7 @@ async fn main() -> std::io::Result<()> {
std::thread::Builder::new()
.name("ui-thread".to_string())
.spawn(move || {
let mut ui = botserver::console::XtreeUI::new();
let mut ui =crate::console::XtreeUI::new();
ui.set_progress_channel(progress_rx);
ui.set_state_channel(state_rx);
@ -1063,7 +1060,7 @@ async fn main() -> std::io::Result<()> {
info!("Loaded Zitadel config from {}: url={}", config_path, base_url);
botserver::directory::client::ZitadelConfig {
crate::directory::client::ZitadelConfig {
issuer_url: base_url.to_string(),
issuer: base_url.to_string(),
client_id: client_id.to_string(),
@ -1075,7 +1072,7 @@ async fn main() -> std::io::Result<()> {
}
} else {
warn!("Failed to parse directory_config.json, using defaults");
botserver::directory::client::ZitadelConfig {
crate::directory::client::ZitadelConfig {
issuer_url: "http://localhost:8300".to_string(),
issuer: "http://localhost:8300".to_string(),
client_id: String::new(),
@ -1088,7 +1085,7 @@ async fn main() -> std::io::Result<()> {
}
} else {
warn!("directory_config.json not found, using default Zitadel config");
botserver::directory::client::ZitadelConfig {
crate::directory::client::ZitadelConfig {
issuer_url: "http://localhost:8300".to_string(),
issuer: "http://localhost:8300".to_string(),
client_id: String::new(),
@ -1102,7 +1099,7 @@ async fn main() -> std::io::Result<()> {
};
#[cfg(feature = "directory")]
let auth_service = Arc::new(tokio::sync::Mutex::new(
botserver::directory::AuthService::new(zitadel_config.clone()).map_err(|e| std::io::Error::other(format!("Failed to create auth service: {}", e)))?,
crate::directory::AuthService::new(zitadel_config.clone()).map_err(|e| std::io::Error::other(format!("Failed to create auth service: {}", e)))?,
));
#[cfg(feature = "directory")]
@ -1113,22 +1110,22 @@ async fn main() -> std::io::Result<()> {
Ok(pat_token) => {
let pat_token = pat_token.trim().to_string();
info!("Using admin PAT token for bootstrap authentication");
botserver::directory::client::ZitadelClient::with_pat_token(zitadel_config, pat_token)
crate::directory::client::ZitadelClient::with_pat_token(zitadel_config, pat_token)
.map_err(|e| std::io::Error::other(format!("Failed to create bootstrap client with PAT: {}", e)))?
}
Err(e) => {
warn!("Failed to read admin PAT token: {}, falling back to OAuth2", e);
botserver::directory::client::ZitadelClient::new(zitadel_config)
crate::directory::client::ZitadelClient::new(zitadel_config)
.map_err(|e| std::io::Error::other(format!("Failed to create bootstrap client: {}", e)))?
}
}
} else {
info!("Admin PAT not found, using OAuth2 client credentials for bootstrap");
botserver::directory::client::ZitadelClient::new(zitadel_config)
crate::directory::client::ZitadelClient::new(zitadel_config)
.map_err(|e| std::io::Error::other(format!("Failed to create bootstrap client: {}", e)))?
};
match botserver::directory::bootstrap::check_and_bootstrap_admin(&bootstrap_client).await {
match crate::directory::bootstrap::check_and_bootstrap_admin(&bootstrap_client).await {
Ok(Some(_)) => {
info!("Bootstrap completed - admin credentials displayed in console");
}
@ -1165,14 +1162,14 @@ async fn main() -> std::io::Result<()> {
.get_config(&default_bot_id, "llm-key", Some(""))
.unwrap_or_default();
let base_llm_provider = botserver::llm::create_llm_provider_from_url(
let base_llm_provider =crate::llm::create_llm_provider_from_url(
&llm_url,
if llm_model.is_empty() { None } else { Some(llm_model.clone()) },
);
let dynamic_llm_provider = Arc::new(botserver::llm::DynamicLLMProvider::new(base_llm_provider));
let dynamic_llm_provider = Arc::new(crate::llm::DynamicLLMProvider::new(base_llm_provider));
let llm_provider: Arc<dyn botserver::llm::LLMProvider> = if let Some(ref cache) = redis_client {
let llm_provider: Arc<dyn crate::llm::LLMProvider> = if let Some(ref cache) = redis_client {
let embedding_url = config_manager
.get_config(
&default_bot_id,
@ -1186,13 +1183,13 @@ async fn main() -> std::io::Result<()> {
info!("Embedding URL: {}", embedding_url);
info!("Embedding Model: {}", embedding_model);
let embedding_service = Some(Arc::new(botserver::llm::cache::LocalEmbeddingService::new(
let embedding_service = Some(Arc::new(crate::llm::cache::LocalEmbeddingService::new(
embedding_url,
embedding_model,
))
as Arc<dyn botserver::llm::cache::EmbeddingService>);
as Arc<dyn crate::llm::cache::EmbeddingService>);
let cache_config = botserver::llm::cache::CacheConfig {
let cache_config =crate::llm::cache::CacheConfig {
ttl: 3600,
semantic_matching: true,
similarity_threshold: 0.85,
@ -1200,31 +1197,31 @@ async fn main() -> std::io::Result<()> {
key_prefix: "llm_cache".to_string(),
};
Arc::new(botserver::llm::cache::CachedLLMProvider::with_db_pool(
dynamic_llm_provider.clone() as Arc<dyn botserver::llm::LLMProvider>,
Arc::new(crate::llm::cache::CachedLLMProvider::with_db_pool(
dynamic_llm_provider.clone() as Arc<dyn crate::llm::LLMProvider>,
cache.clone(),
cache_config,
embedding_service,
pool.clone(),
))
} else {
dynamic_llm_provider.clone() as Arc<dyn botserver::llm::LLMProvider>
dynamic_llm_provider.clone() as Arc<dyn crate::llm::LLMProvider>
};
let kb_manager = Arc::new(botserver::core::kb::KnowledgeBaseManager::new("work"));
let kb_manager = Arc::new(crate::core::kb::KnowledgeBaseManager::new("work"));
let task_engine = Arc::new(botserver::tasks::TaskEngine::new(pool.clone()));
let task_engine = Arc::new(crate::tasks::TaskEngine::new(pool.clone()));
let metrics_collector = botserver::core::shared::analytics::MetricsCollector::new();
let metrics_collector =crate::core::shared::analytics::MetricsCollector::new();
let task_scheduler = None;
let (attendant_tx, _attendant_rx) = tokio::sync::broadcast::channel::<
botserver::core::shared::state::AttendantNotification,
crate::core::shared::state::AttendantNotification,
>(1000);
let (task_progress_tx, _task_progress_rx) = tokio::sync::broadcast::channel::<
botserver::core::shared::state::TaskProgressEvent,
crate::core::shared::state::TaskProgressEvent,
>(1000);
// Initialize BotDatabaseManager for per-bot database support
@ -1269,7 +1266,7 @@ async fn main() -> std::io::Result<()> {
let mut map = HashMap::new();
map.insert(
"web".to_string(),
web_adapter.clone() as Arc<dyn botserver::core::bot::channels::ChannelAdapter>,
web_adapter.clone() as Arc<dyn crate::core::bot::channels::ChannelAdapter>,
);
map
})),
@ -1279,7 +1276,7 @@ async fn main() -> std::io::Result<()> {
kb_manager: Some(kb_manager.clone()),
task_engine,
extensions: {
let ext = botserver::core::shared::state::Extensions::new();
let ext =crate::core::shared::state::Extensions::new();
ext.insert_blocking(Arc::clone(&dynamic_llm_provider));
ext
},
@ -1287,20 +1284,20 @@ async fn main() -> std::io::Result<()> {
task_progress_broadcast: Some(task_progress_tx),
billing_alert_broadcast: None,
task_manifests: Arc::new(std::sync::RwLock::new(HashMap::new())),
project_service: Arc::new(tokio::sync::RwLock::new(botserver::project::ProjectService::new())),
legal_service: Arc::new(tokio::sync::RwLock::new(botserver::legal::LegalService::new())),
project_service: Arc::new(tokio::sync::RwLock::new(crate::project::ProjectService::new())),
legal_service: Arc::new(tokio::sync::RwLock::new(crate::legal::LegalService::new())),
jwt_manager: None,
auth_provider_registry: None,
rbac_manager: None,
});
let task_scheduler = Arc::new(botserver::tasks::scheduler::TaskScheduler::new(
let task_scheduler = Arc::new(crate::tasks::scheduler::TaskScheduler::new(
app_state.clone(),
));
task_scheduler.start();
if let Err(e) = botserver::core::kb::ensure_crawler_service_running(app_state.clone()).await {
if let Err(e) =crate::core::kb::ensure_crawler_service_running(app_state.clone()).await {
log::warn!("Failed to start website crawler service: {}", e);
}
@ -1334,7 +1331,7 @@ async fn main() -> std::io::Result<()> {
tokio::spawn(async move {
register_thread("drive-monitor", "drive");
trace!("DriveMonitor::new starting...");
let monitor = botserver::DriveMonitor::new(
let monitor =crate::DriveMonitor::new(
drive_monitor_state,
bucket_name.clone(),
monitor_bot_id,

View file

@ -1,4 +1,4 @@
use crate::config::ConfigManager;
use crate::core::config::ConfigManager;
use crate::shared::utils::create_tls_client;
use crate::shared::state::AppState;
use log::{error, info, trace};

View file

@ -318,7 +318,7 @@ async fn process_attendant_command(
let current_session = get_attendant_active_session(state, phone).await;
#[cfg(feature = "attendance")]
#[cfg(feature = "attendant")]
{
match crate::attendance::llm_assist::process_attendant_command(
state,
@ -333,7 +333,7 @@ async fn process_attendant_command(
}
}
#[cfg(not(feature = "attendance"))]
#[cfg(not(feature = "attendant"))]
{
let _ = current_session;
Some(format!(