feat(auth): add bot name lookup and suggestion support in bot responses
Implemented a new `bot_from_name` method in `AuthService` to retrieve a bot's UUID by its name, enabling explicit bot selection via a `bot_name` query parameter in the authentication endpoint. Updated `auth_handler` to prioritize this parameter, fall back to the first active bot when necessary, and handle cases where no active bots exist. Extended `BotOrchestrator` to import the `Suggestion` model and fetch suggestion data from Redis for each user session. Integrated these suggestions into the `BotResponse` payload, ensuring clients receive relevant suggestions alongside bot messages. These changes improve bot selection flexibility and enrich the response data with contextual suggestions.
This commit is contained in:
parent
d6fcc346fc
commit
fda0b0c9e8
3 changed files with 96 additions and 9 deletions
|
|
@ -144,6 +144,22 @@ impl AuthService {
|
||||||
|
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bot_from_name(
|
||||||
|
&mut self,
|
||||||
|
bot_name: &str,
|
||||||
|
) -> Result<Option<Uuid>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
use crate::shared::models::bots;
|
||||||
|
|
||||||
|
let bot = bots::table
|
||||||
|
.filter(bots::name.eq(bot_name))
|
||||||
|
.filter(bots::is_active.eq(true))
|
||||||
|
.select(bots::id)
|
||||||
|
.first::<Uuid>(&mut self.conn)
|
||||||
|
.optional()?;
|
||||||
|
|
||||||
|
Ok(bot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::get("/api/auth")]
|
#[actix_web::get("/api/auth")]
|
||||||
|
|
@ -152,6 +168,7 @@ async fn auth_handler(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
web::Query(params): web::Query<HashMap<String, String>>,
|
web::Query(params): web::Query<HashMap<String, String>>,
|
||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
|
let bot_name = params.get("bot_name").cloned().unwrap_or_default();
|
||||||
let _token = params.get("token").cloned().unwrap_or_default();
|
let _token = params.get("token").cloned().unwrap_or_default();
|
||||||
|
|
||||||
// Create or get anonymous user with proper UUID
|
// Create or get anonymous user with proper UUID
|
||||||
|
|
@ -168,9 +185,41 @@ async fn auth_handler(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut db_conn = data.conn.lock().unwrap();
|
let mut db_conn = data.conn.lock().unwrap();
|
||||||
let (bot_id, bot_name) = match crate::bot::bot_from_url(&mut *db_conn, req.path()) {
|
// Use bot_name query parameter if provided, otherwise fallback to path-based lookup
|
||||||
Ok((id, name)) => (id, name),
|
let bot_name_param = bot_name.clone();
|
||||||
Err(res) => return Ok(res),
|
let (bot_id, bot_name) = {
|
||||||
|
use crate::shared::models::schema::bots::dsl::*;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use actix_web::error::ErrorInternalServerError;
|
||||||
|
|
||||||
|
// Try to find bot by the provided name
|
||||||
|
match bots
|
||||||
|
.filter(name.eq(&bot_name_param))
|
||||||
|
.filter(is_active.eq(true))
|
||||||
|
.select((id, name))
|
||||||
|
.first::<(Uuid, String)>(&mut *db_conn)
|
||||||
|
.optional()
|
||||||
|
.map_err(|e| ErrorInternalServerError(e))?
|
||||||
|
{
|
||||||
|
Some((id_val, name_val)) => (id_val, name_val),
|
||||||
|
None => {
|
||||||
|
// Fallback to first active bot if not found
|
||||||
|
match bots
|
||||||
|
.filter(is_active.eq(true))
|
||||||
|
.select((id, name))
|
||||||
|
.first::<(Uuid, String)>(&mut *db_conn)
|
||||||
|
.optional()
|
||||||
|
.map_err(|e| ErrorInternalServerError(e))?
|
||||||
|
{
|
||||||
|
Some((id_val, name_val)) => (id_val, name_val),
|
||||||
|
None => {
|
||||||
|
error!("No active bots found");
|
||||||
|
return Ok(HttpResponse::ServiceUnavailable()
|
||||||
|
.json(serde_json::json!({"error": "No bots available"})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = {
|
let session = {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::context::langcache::get_langcache_client;
|
||||||
use crate::drive_monitor::DriveMonitor;
|
use crate::drive_monitor::DriveMonitor;
|
||||||
use crate::kb::embeddings::generate_embeddings;
|
use crate::kb::embeddings::generate_embeddings;
|
||||||
use crate::kb::qdrant_client::{ensure_collection_exists, get_qdrant_client, QdrantPoint};
|
use crate::kb::qdrant_client::{ensure_collection_exists, get_qdrant_client, QdrantPoint};
|
||||||
use crate::shared::models::{BotResponse, UserMessage, UserSession};
|
use crate::shared::models::{BotResponse, Suggestion, UserMessage, UserSession};
|
||||||
use crate::shared::state::AppState;
|
use crate::shared::state::AppState;
|
||||||
use actix_web::{web, HttpRequest, HttpResponse, Result};
|
use actix_web::{web, HttpRequest, HttpResponse, Result};
|
||||||
use actix_ws::Message as WsMessage;
|
use actix_ws::Message as WsMessage;
|
||||||
|
|
@ -550,6 +550,25 @@ impl BotOrchestrator {
|
||||||
message.user_id, message.session_id
|
message.user_id, message.session_id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Get suggestions from Redis
|
||||||
|
let suggestions = if let Some(redis) = &self.state.redis_client {
|
||||||
|
let mut conn = redis.get_multiplexed_async_connection().await?;
|
||||||
|
let redis_key = format!("suggestions:{}:{}", message.user_id, message.session_id);
|
||||||
|
let suggestions: Vec<String> = redis::cmd("LRANGE")
|
||||||
|
.arg(&redis_key)
|
||||||
|
.arg(0)
|
||||||
|
.arg(-1)
|
||||||
|
.query_async(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
suggestions
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|s| serde_json::from_str::<Suggestion>(&s).ok())
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
let user_id = Uuid::parse_str(&message.user_id).map_err(|e| {
|
let user_id = Uuid::parse_str(&message.user_id).map_err(|e| {
|
||||||
error!("Invalid user ID: {}", e);
|
error!("Invalid user ID: {}", e);
|
||||||
e
|
e
|
||||||
|
|
@ -733,7 +752,7 @@ impl BotOrchestrator {
|
||||||
message_type: 1,
|
message_type: 1,
|
||||||
stream_token: None,
|
stream_token: None,
|
||||||
is_complete: false,
|
is_complete: false,
|
||||||
suggestions: Vec::new(),
|
suggestions: suggestions.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if response_tx.send(partial).await.is_err() {
|
if response_tx.send(partial).await.is_err() {
|
||||||
|
|
@ -761,7 +780,7 @@ impl BotOrchestrator {
|
||||||
message_type: 1,
|
message_type: 1,
|
||||||
stream_token: None,
|
stream_token: None,
|
||||||
is_complete: true,
|
is_complete: true,
|
||||||
suggestions: Vec::new(),
|
suggestions,
|
||||||
};
|
};
|
||||||
|
|
||||||
response_tx.send(final_msg).await?;
|
response_tx.send(final_msg).await?;
|
||||||
|
|
@ -801,8 +820,23 @@ impl BotOrchestrator {
|
||||||
session.id, token
|
session.id, token
|
||||||
);
|
);
|
||||||
|
|
||||||
let bot_guid = std::env::var("BOT_GUID").unwrap_or_else(|_| String::from("default_bot"));
|
use crate::shared::models::schema::bots::dsl::*;
|
||||||
let start_script_path = format!("./{}.gbai/.gbdialog/start.bas", bot_guid);
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
let bot_id = session.bot_id;
|
||||||
|
let bot_name: String = {
|
||||||
|
let mut db_conn = state.conn.lock().unwrap();
|
||||||
|
bots.filter(id.eq(Uuid::parse_str(&bot_id.to_string())?))
|
||||||
|
.select(name)
|
||||||
|
.first(&mut *db_conn)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Failed to query bot name for {}: {}", bot_id, e);
|
||||||
|
e
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let start_script_path = format!("./work/{}.gbai/{}.gbdialog/start.ast", bot_name, bot_name);
|
||||||
|
|
||||||
let start_script = match std::fs::read_to_string(&start_script_path) {
|
let start_script = match std::fs::read_to_string(&start_script_path) {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
|
|
|
||||||
|
|
@ -1093,7 +1093,11 @@
|
||||||
async function initializeAuth() {
|
async function initializeAuth() {
|
||||||
try {
|
try {
|
||||||
updateConnectionStatus("connecting");
|
updateConnectionStatus("connecting");
|
||||||
const response = await fetch("/api/auth");
|
// Extract bot name from URL path (first segment after /)
|
||||||
|
const pathSegments = window.location.pathname.split('/').filter(s => s);
|
||||||
|
const botName = pathSegments.length > 0 ? pathSegments[0] : 'default';
|
||||||
|
|
||||||
|
const response = await fetch(`/api/auth?bot_name=${encodeURIComponent(botName)}`);
|
||||||
const authData = await response.json();
|
const authData = await response.json();
|
||||||
currentUserId = authData.user_id;
|
currentUserId = authData.user_id;
|
||||||
currentSessionId = authData.session_id;
|
currentSessionId = authData.session_id;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue