Read Drive config from Vault at runtime with fallback defaults
Some checks failed
BotServer CI / build (push) Failing after 7m26s
Some checks failed
BotServer CI / build (push) Failing after 7m26s
This commit is contained in:
parent
b57c53e2ff
commit
ab1f2df476
7 changed files with 207 additions and 22 deletions
|
|
@ -28,7 +28,7 @@ fn hear_block(state: &Arc<AppState>, session_id: uuid::Uuid, variable_name: &str
|
||||||
// Mark session as waiting and store metadata in Redis (for UI hints like menus)
|
// Mark session as waiting and store metadata in Redis (for UI hints like menus)
|
||||||
let state_clone = Arc::clone(state);
|
let state_clone = Arc::clone(state);
|
||||||
let var = variable_name.to_string();
|
let var = variable_name.to_string();
|
||||||
let _ = tokio::runtime::Handle::current().block_on(async move {
|
tokio::runtime::Handle::current().block_on(async move {
|
||||||
{
|
{
|
||||||
let mut sm = state_clone.session_manager.lock().await;
|
let mut sm = state_clone.session_manager.lock().await;
|
||||||
sm.mark_waiting(session_id);
|
sm.mark_waiting(session_id);
|
||||||
|
|
@ -207,7 +207,7 @@ fn register_hear_as_menu(state: Arc<AppState>, user: UserSession, engine: &mut E
|
||||||
// Store suggestions in Redis for UI
|
// Store suggestions in Redis for UI
|
||||||
let state_for_suggestions = Arc::clone(&state_clone);
|
let state_for_suggestions = Arc::clone(&state_clone);
|
||||||
let opts_clone = options.clone();
|
let opts_clone = options.clone();
|
||||||
let _ = tokio::runtime::Handle::current().block_on(async move {
|
tokio::runtime::Handle::current().block_on(async move {
|
||||||
if let Some(redis) = &state_for_suggestions.cache {
|
if let Some(redis) = &state_for_suggestions.cache {
|
||||||
if let Ok(mut conn) = redis.get_multiplexed_async_connection().await {
|
if let Ok(mut conn) = redis.get_multiplexed_async_connection().await {
|
||||||
let key = format!("suggestions:{session_id}:{session_id}");
|
let key = format!("suggestions:{session_id}:{session_id}");
|
||||||
|
|
|
||||||
|
|
@ -450,7 +450,7 @@ impl BotOrchestrator {
|
||||||
|
|
||||||
// If a HEAR is blocking the script thread for this session, deliver the input
|
// If a HEAR is blocking the script thread for this session, deliver the input
|
||||||
// directly and return — the script continues from where it paused.
|
// directly and return — the script continues from where it paused.
|
||||||
if crate::basic::keywords::hearing::syntax::deliver_hear_input(
|
if crate::basic::keywords::hearing::deliver_hear_input(
|
||||||
&self.state,
|
&self.state,
|
||||||
session_id,
|
session_id,
|
||||||
message_content.clone(),
|
message_content.clone(),
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ pub use model_routing_config::{ModelRoutingConfig, RoutingStrategy, TaskType};
|
||||||
pub use sse_config::SseConfig;
|
pub use sse_config::SseConfig;
|
||||||
pub use user_memory_config::UserMemoryConfig;
|
pub use user_memory_config::UserMemoryConfig;
|
||||||
|
|
||||||
|
use crate::core::secrets::SecretsManager;
|
||||||
use crate::core::shared::utils::DbPool;
|
use crate::core::shared::utils::DbPool;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::r2d2::{ConnectionManager, PooledConnection};
|
use diesel::r2d2::{ConnectionManager, PooledConnection};
|
||||||
|
|
@ -277,10 +278,24 @@ impl AppConfig {
|
||||||
.and_then(|v| v.parse().ok())
|
.and_then(|v| v.parse().ok())
|
||||||
.unwrap_or(default)
|
.unwrap_or(default)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Read from Vault with fallback to defaults
|
||||||
|
let secrets = SecretsManager::from_env().ok();
|
||||||
|
let (drive_server, drive_access, drive_secret) = secrets
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.get_drive_config())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
(
|
||||||
|
crate::core::urls::InternalUrls::DRIVE.to_string(),
|
||||||
|
"minioadmin".to_string(),
|
||||||
|
"minioadmin".to_string(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let drive = DriveConfig {
|
let drive = DriveConfig {
|
||||||
server: crate::core::urls::InternalUrls::DRIVE.to_string(),
|
server: drive_server,
|
||||||
access_key: String::new(),
|
access_key: drive_access,
|
||||||
secret_key: String::new(),
|
secret_key: drive_secret,
|
||||||
};
|
};
|
||||||
let email = EmailConfig {
|
let email = EmailConfig {
|
||||||
server: get_str("EMAIL_IMAP_SERVER", "imap.gmail.com"),
|
server: get_str("EMAIL_IMAP_SERVER", "imap.gmail.com"),
|
||||||
|
|
@ -315,10 +330,23 @@ impl AppConfig {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn from_env() -> Result<Self, anyhow::Error> {
|
pub fn from_env() -> Result<Self, anyhow::Error> {
|
||||||
|
// Read from Vault with fallback to defaults
|
||||||
|
let secrets = SecretsManager::from_env().ok();
|
||||||
|
let (drive_server, drive_access, drive_secret) = secrets
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.get_drive_config())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
(
|
||||||
|
crate::core::urls::InternalUrls::DRIVE.to_string(),
|
||||||
|
"minioadmin".to_string(),
|
||||||
|
"minioadmin".to_string(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let minio = DriveConfig {
|
let minio = DriveConfig {
|
||||||
server: crate::core::urls::InternalUrls::DRIVE.to_string(),
|
server: drive_server,
|
||||||
access_key: String::new(),
|
access_key: drive_access,
|
||||||
secret_key: String::new(),
|
secret_key: drive_secret,
|
||||||
};
|
};
|
||||||
let email = EmailConfig {
|
let email = EmailConfig {
|
||||||
server: "imap.gmail.com".to_string(),
|
server: "imap.gmail.com".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,54 @@ impl SecretsManager {
|
||||||
.ok_or_else(|| anyhow!("Key '{}' not found in '{}'", key, path))
|
.ok_or_else(|| anyhow!("Key '{}' not found in '{}'", key, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_value_blocking(&self, path: &str, key: &str, default: &str) -> String {
|
||||||
|
// Try to get synchronously using blocking call
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(path)) {
|
||||||
|
if let Some(value) = secrets.get(key) {
|
||||||
|
return value.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback to env defaults
|
||||||
|
if let Ok(secrets) = Self::get_from_env(path) {
|
||||||
|
if let Some(value) = secrets.get(key) {
|
||||||
|
return value.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_drive_config(&self) -> (String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::DRIVE)) {
|
||||||
|
return (
|
||||||
|
secrets.get("host").cloned().unwrap_or_else(|| "localhost:9100".into()),
|
||||||
|
secrets.get("accesskey").cloned().unwrap_or_else(|| "minioadmin".into()),
|
||||||
|
secrets.get("secret").cloned().unwrap_or_else(|| "minioadmin".into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback
|
||||||
|
("localhost:9100".to_string(), "minioadmin".to_string(), "minioadmin".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_database_config_sync(&self) -> (String, u16, String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::TABLES)) {
|
||||||
|
return (
|
||||||
|
secrets.get("host").cloned().unwrap_or_else(|| "localhost".into()),
|
||||||
|
secrets.get("port").and_then(|p| p.parse().ok()).unwrap_or(5432),
|
||||||
|
secrets.get("database").cloned().unwrap_or_else(|| "botserver".into()),
|
||||||
|
secrets.get("username").cloned().unwrap_or_else(|| "gbuser".into()),
|
||||||
|
secrets.get("password").cloned().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback
|
||||||
|
("localhost".to_string(), 5432, "botserver".to_string(), "gbuser".to_string(), "changeme".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_drive_credentials(&self) -> Result<(String, String)> {
|
pub async fn get_drive_credentials(&self) -> Result<(String, String)> {
|
||||||
let s = self.get_secret(SecretPaths::DRIVE).await?;
|
let s = self.get_secret(SecretPaths::DRIVE).await?;
|
||||||
Ok((
|
Ok((
|
||||||
|
|
@ -297,8 +345,122 @@ impl SecretsManager {
|
||||||
self.get_value(SecretPaths::ENCRYPTION, "master_key").await
|
self.get_value(SecretPaths::ENCRYPTION, "master_key").await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_jwt_secret(&self) -> Result<String> {
|
pub fn get_cache_config(&self) -> (String, u16, Option<String>) {
|
||||||
self.get_value(SecretPaths::JWT, "secret").await
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::CACHE)) {
|
||||||
|
return (
|
||||||
|
secrets.get("host").cloned().unwrap_or_else(|| "localhost".into()),
|
||||||
|
secrets.get("port").and_then(|p| p.parse().ok()).unwrap_or(6379),
|
||||||
|
secrets.get("password").cloned(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("localhost".to_string(), 6379, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_directory_config_sync(&self) -> (String, String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::DIRECTORY)) {
|
||||||
|
return (
|
||||||
|
secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:9000".into()),
|
||||||
|
secrets.get("project_id").cloned().unwrap_or_default(),
|
||||||
|
secrets.get("client_id").cloned().unwrap_or_default(),
|
||||||
|
secrets.get("client_secret").cloned().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("http://localhost:9000".to_string(), String::new(), String::new(), String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_email_config(&self) -> (String, u16, String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::EMAIL)) {
|
||||||
|
return (
|
||||||
|
secrets.get("smtp_host").cloned().unwrap_or_else(|| "smtp.gmail.com".into()),
|
||||||
|
secrets.get("smtp_port").and_then(|p| p.parse().ok()).unwrap_or(587),
|
||||||
|
secrets.get("smtp_user").cloned().unwrap_or_default(),
|
||||||
|
secrets.get("smtp_password").cloned().unwrap_or_default(),
|
||||||
|
secrets.get("smtp_from").cloned().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("smtp.gmail.com".to_string(), 587, String::new(), String::new(), String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_llm_config(&self) -> (String, String, Option<String>, Option<String>, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::LLM)) {
|
||||||
|
return (
|
||||||
|
secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:8081".into()),
|
||||||
|
secrets.get("model").cloned().unwrap_or_else(|| "gpt-4".into()),
|
||||||
|
secrets.get("openai_key").cloned(),
|
||||||
|
secrets.get("anthropic_key").cloned(),
|
||||||
|
secrets.get("ollama_url").cloned().unwrap_or_else(|| "http://localhost:11434".into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("http://localhost:8081".to_string(), "gpt-4".to_string(), None, None, "http://localhost:11434".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_meet_config(&self) -> (String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::MEET)) {
|
||||||
|
return (
|
||||||
|
secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:7880".into()),
|
||||||
|
secrets.get("app_id").cloned().unwrap_or_default(),
|
||||||
|
secrets.get("app_secret").cloned().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("http://localhost:7880".to_string(), String::new(), String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vectordb_config_sync(&self) -> (String, Option<String>) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::VECTORDB)) {
|
||||||
|
return (
|
||||||
|
secrets.get("url").cloned().unwrap_or_else(|| "https://localhost:6334".into()),
|
||||||
|
secrets.get("api_key").cloned(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("https://localhost:6334".to_string(), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_observability_config_sync(&self) -> (String, String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::OBSERVABILITY)) {
|
||||||
|
return (
|
||||||
|
secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:8086".into()),
|
||||||
|
secrets.get("org").cloned().unwrap_or_else(|| "system".into()),
|
||||||
|
secrets.get("bucket").cloned().unwrap_or_else(|| "metrics".into()),
|
||||||
|
secrets.get("token").cloned().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("http://localhost:8086".to_string(), "system".to_string(), "metrics".to_string(), String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_alm_config(&self) -> (String, String, String) {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::ALM)) {
|
||||||
|
return (
|
||||||
|
secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:9000".into()),
|
||||||
|
secrets.get("token").cloned().unwrap_or_default(),
|
||||||
|
secrets.get("default_org").cloned().unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("http://localhost:9000".to_string(), String::new(), String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_jwt_secret_sync(&self) -> String {
|
||||||
|
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
|
||||||
|
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::JWT)) {
|
||||||
|
return secrets.get("secret").cloned().unwrap_or_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn put_secret(&self, path: &str, data: HashMap<String, String>) -> Result<()> {
|
pub async fn put_secret(&self, path: &str, data: HashMap<String, String>) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,7 @@ impl Clone for AppState {
|
||||||
kb_manager: self.kb_manager.clone(),
|
kb_manager: self.kb_manager.clone(),
|
||||||
channels: Arc::clone(&self.channels),
|
channels: Arc::clone(&self.channels),
|
||||||
response_channels: Arc::clone(&self.response_channels),
|
response_channels: Arc::clone(&self.response_channels),
|
||||||
|
hear_channels: Arc::clone(&self.hear_channels),
|
||||||
web_adapter: Arc::clone(&self.web_adapter),
|
web_adapter: Arc::clone(&self.web_adapter),
|
||||||
voice_adapter: Arc::clone(&self.voice_adapter),
|
voice_adapter: Arc::clone(&self.voice_adapter),
|
||||||
#[cfg(feature = "tasks")]
|
#[cfg(feature = "tasks")]
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ pub async fn extract_lead_from_email(
|
||||||
.split('@')
|
.split('@')
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.and_then(|d| d.split('.').next())
|
.and_then(|d| d.split('.').next())
|
||||||
.map(|c| capitalize(c));
|
.map(capitalize);
|
||||||
|
|
||||||
Ok(Json(LeadExtractionResponse {
|
Ok(Json(LeadExtractionResponse {
|
||||||
first_name,
|
first_name,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,9 @@ pub struct LLMRequestMetrics {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[derive(Default)]
|
||||||
pub enum RequestType {
|
pub enum RequestType {
|
||||||
|
#[default]
|
||||||
Chat,
|
Chat,
|
||||||
Completion,
|
Completion,
|
||||||
Embedding,
|
Embedding,
|
||||||
|
|
@ -56,11 +58,6 @@ pub enum RequestType {
|
||||||
AudioGeneration,
|
AudioGeneration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RequestType {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Chat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for RequestType {
|
impl std::fmt::Display for RequestType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
@ -225,17 +222,14 @@ pub enum TraceEventType {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[derive(Default)]
|
||||||
pub enum TraceStatus {
|
pub enum TraceStatus {
|
||||||
Ok,
|
Ok,
|
||||||
Error,
|
Error,
|
||||||
|
#[default]
|
||||||
InProgress,
|
InProgress,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TraceStatus {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::InProgress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObservabilityConfig {
|
pub struct ObservabilityConfig {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue