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)
|
||||
}
|
||||
|
||||
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")]
|
||||
|
|
@ -152,6 +168,7 @@ async fn auth_handler(
|
|||
data: web::Data<AppState>,
|
||||
web::Query(params): web::Query<HashMap<String, String>>,
|
||||
) -> Result<HttpResponse> {
|
||||
let bot_name = params.get("bot_name").cloned().unwrap_or_default();
|
||||
let _token = params.get("token").cloned().unwrap_or_default();
|
||||
|
||||
// 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 (bot_id, bot_name) = match crate::bot::bot_from_url(&mut *db_conn, req.path()) {
|
||||
Ok((id, name)) => (id, name),
|
||||
Err(res) => return Ok(res),
|
||||
// Use bot_name query parameter if provided, otherwise fallback to path-based lookup
|
||||
let bot_name_param = bot_name.clone();
|
||||
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 = {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::context::langcache::get_langcache_client;
|
|||
use crate::drive_monitor::DriveMonitor;
|
||||
use crate::kb::embeddings::generate_embeddings;
|
||||
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 actix_web::{web, HttpRequest, HttpResponse, Result};
|
||||
use actix_ws::Message as WsMessage;
|
||||
|
|
@ -550,6 +550,25 @@ impl BotOrchestrator {
|
|||
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| {
|
||||
error!("Invalid user ID: {}", e);
|
||||
e
|
||||
|
|
@ -733,7 +752,7 @@ impl BotOrchestrator {
|
|||
message_type: 1,
|
||||
stream_token: None,
|
||||
is_complete: false,
|
||||
suggestions: Vec::new(),
|
||||
suggestions: suggestions.clone(),
|
||||
};
|
||||
|
||||
if response_tx.send(partial).await.is_err() {
|
||||
|
|
@ -761,7 +780,7 @@ impl BotOrchestrator {
|
|||
message_type: 1,
|
||||
stream_token: None,
|
||||
is_complete: true,
|
||||
suggestions: Vec::new(),
|
||||
suggestions,
|
||||
};
|
||||
|
||||
response_tx.send(final_msg).await?;
|
||||
|
|
@ -801,8 +820,23 @@ impl BotOrchestrator {
|
|||
session.id, token
|
||||
);
|
||||
|
||||
let bot_guid = std::env::var("BOT_GUID").unwrap_or_else(|_| String::from("default_bot"));
|
||||
let start_script_path = format!("./{}.gbai/.gbdialog/start.bas", bot_guid);
|
||||
use crate::shared::models::schema::bots::dsl::*;
|
||||
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) {
|
||||
Ok(content) => content,
|
||||
|
|
|
|||
|
|
@ -1093,7 +1093,11 @@
|
|||
async function initializeAuth() {
|
||||
try {
|
||||
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();
|
||||
currentUserId = authData.user_id;
|
||||
currentSessionId = authData.session_id;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue