Files
compliance-scanner-agent/compliance-dashboard/src/infrastructure/chat.rs
T
sharang 56482911b8
CI / Check (push) Has been skipped
CI / Detect Changes (push) Successful in 6s
CI / Deploy Agent (push) Successful in 4m8s
CI / Deploy Dashboard (push) Successful in 4m58s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
fix(dashboard): attach Keycloak token on agent API calls (#90)
2026-06-17 18:35:59 +00:00

118 lines
3.1 KiB
Rust

use dioxus::prelude::*;
use serde::{Deserialize, Serialize};
// ── Response types ──
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatApiResponse {
pub data: ChatResponseData,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatResponseData {
pub message: String,
#[serde(default)]
pub sources: Vec<SourceRef>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SourceRef {
pub file_path: String,
pub qualified_name: String,
pub start_line: u32,
pub end_line: u32,
pub language: String,
pub snippet: String,
pub score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingStatusResponse {
pub data: Option<EmbeddingBuildData>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingBuildData {
pub repo_id: String,
pub status: String,
pub total_chunks: u32,
pub embedded_chunks: u32,
pub embedding_model: String,
pub error_message: Option<String>,
#[serde(default)]
pub started_at: Option<serde_json::Value>,
#[serde(default)]
pub completed_at: Option<serde_json::Value>,
}
// ── Chat message history type ──
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatHistoryMessage {
pub role: String,
pub content: String,
}
// ── Server functions ──
#[server]
pub async fn send_chat_message(
repo_id: String,
message: String,
history: Vec<ChatHistoryMessage>,
) -> Result<ChatApiResponse, ServerFnError> {
// Chat uses a longer timeout because the LLM round-trip can be slow;
// agent_request doesn't expose a per-call timeout so we layer one on.
let resp = super::agent_client::agent_request(
reqwest::Method::POST,
&format!("/api/v1/chat/{repo_id}"),
)
.await?
.timeout(std::time::Duration::from_secs(120))
.json(&serde_json::json!({
"message": message,
"history": history,
}))
.send()
.await
.map_err(|e| ServerFnError::new(format!("Request failed: {e}")))?;
let text = resp
.text()
.await
.map_err(|e| ServerFnError::new(format!("Failed to read response: {e}")))?;
let body: ChatApiResponse = serde_json::from_str(&text)
.map_err(|e| ServerFnError::new(format!("Failed to parse response: {e} — body: {text}")))?;
Ok(body)
}
#[server]
pub async fn trigger_embedding_build(repo_id: String) -> Result<(), ServerFnError> {
super::agent_client::agent_request(
reqwest::Method::POST,
&format!("/api/v1/chat/{repo_id}/build-embeddings"),
)
.await?
.send()
.await
.map_err(|e| ServerFnError::new(e.to_string()))?;
Ok(())
}
#[server]
pub async fn fetch_embedding_status(
repo_id: String,
) -> Result<EmbeddingStatusResponse, ServerFnError> {
let resp = super::agent_client::agent_get(&format!("/api/v1/chat/{repo_id}/status"))
.await?
.send()
.await
.map_err(|e| ServerFnError::new(e.to_string()))?;
let body: EmbeddingStatusResponse = resp
.json()
.await
.map_err(|e| ServerFnError::new(e.to_string()))?;
Ok(body)
}