102 lines
3.4 KiB
Rust
102 lines
3.4 KiB
Rust
use std::sync::Arc;
|
|
|
|
use tonic::{Request, Response, Status};
|
|
use tracing::info;
|
|
|
|
use cxcloud_proto::bot::{
|
|
bot_service_server::{BotService, BotServiceServer},
|
|
ExecutionRequest, ExecutionResponse, ListToolsRequest, ListToolsResponse, ToolInfo,
|
|
ToolResult,
|
|
};
|
|
|
|
use crate::AppState;
|
|
|
|
pub struct BotServiceImpl {
|
|
state: Arc<AppState>,
|
|
}
|
|
|
|
#[tonic::async_trait]
|
|
impl BotService for BotServiceImpl {
|
|
async fn execute(
|
|
&self,
|
|
request: Request<ExecutionRequest>,
|
|
) -> Result<Response<ExecutionResponse>, Status> {
|
|
let req = request.into_inner();
|
|
info!(plan_id = %req.plan_id, tools = req.tool_calls.len(), "Execute request via gRPC");
|
|
|
|
let tool_calls: Vec<serde_json::Value> = req
|
|
.tool_calls
|
|
.iter()
|
|
.map(|tc| {
|
|
serde_json::json!({
|
|
"id": tc.id,
|
|
"tool_name": tc.tool_name,
|
|
"parameters": tc.parameters.as_ref().map(|p| format!("{:?}", p)).unwrap_or_default(),
|
|
})
|
|
})
|
|
.collect();
|
|
|
|
let results = crate::executor::execute_tool_calls(&self.state.registry, &tool_calls).await;
|
|
|
|
let tool_results: Vec<ToolResult> = results
|
|
.iter()
|
|
.map(|r| ToolResult {
|
|
tool_call_id: r.get("tool_call_id").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
|
tool_name: r.get("tool_name").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
|
success: r.get("success").and_then(|v| v.as_bool()).unwrap_or(false),
|
|
output: None,
|
|
error_message: r.get("error_message").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
|
error_code: String::new(),
|
|
retries_used: 0,
|
|
duration_ms: r.get("duration_ms").and_then(|v| v.as_i64()).unwrap_or(0),
|
|
})
|
|
.collect();
|
|
|
|
let all_success = tool_results.iter().all(|r| r.success);
|
|
|
|
Ok(Response::new(ExecutionResponse {
|
|
execution_id: uuid::Uuid::now_v7().to_string(),
|
|
results: tool_results,
|
|
status: if all_success { 1 } else { 2 },
|
|
started_at: None,
|
|
completed_at: None,
|
|
duration_ms: 0,
|
|
}))
|
|
}
|
|
|
|
async fn list_tools(
|
|
&self,
|
|
_request: Request<ListToolsRequest>,
|
|
) -> Result<Response<ListToolsResponse>, Status> {
|
|
let tools: Vec<ToolInfo> = self
|
|
.state
|
|
.registry
|
|
.list_tools()
|
|
.iter()
|
|
.map(|t| ToolInfo {
|
|
name: t.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
|
description: t.get("description").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
|
category: t.get("category").and_then(|v| v.as_str()).unwrap_or("").to_string(),
|
|
available: t.get("available").and_then(|v| v.as_bool()).unwrap_or(false),
|
|
schema: None,
|
|
})
|
|
.collect();
|
|
|
|
Ok(Response::new(ListToolsResponse { tools }))
|
|
}
|
|
}
|
|
|
|
pub async fn serve(state: Arc<AppState>, port: u16) -> anyhow::Result<()> {
|
|
let addr = format!("0.0.0.0:{port}").parse()?;
|
|
let service = BotServiceImpl { state };
|
|
|
|
info!(port, "Bot gRPC server starting");
|
|
|
|
tonic::transport::Server::builder()
|
|
.add_service(BotServiceServer::new(service))
|
|
.serve(addr)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|