fix: resolve all 75 mem-cli compilation errors across 25 files
CI / CI (pull_request) Successful in 4m17s
CI / CI (pull_request) Successful in 4m17s
- embed_text -> embed_one + Vector to Vec<f32> conversion (semantic.rs, unified_query.rs) - DefaultAgent: remove shadowing type alias, re-export concrete struct - Position: add Default derive for unwrap_or_default() - streaming_body -> streaming + yield Result<Bytes> for SSE - borrow-after-move: compute len before move in 6 places - add missing imports: sqlx::Row, chrono::Timelike, std::pin::Pin - add missing derives: Serialize on CompactionStats, FacetFilters - extract_token: extract Authorization header from HttpRequest first - AuthError variants: match actual enum (TokenExpired, not ExpiredToken) - validate_bearer_token -> extract_bearer_token (sync check) - check_limit -> check with correct args - link_entities -> link_mentions (async, correct signature) - InferenceEngine::new + infer_facts: match actual 2-arg/3-arg API - RoutedResult: add missing confidence_score + is_valid fields - client_sdk: fix ownership (remove borrow, save status before .text()) - index_chunk -> index_document (match OpenSearchClient API) - recursive async dfs_paths: Box::pin for infinite future size - log -> tracing crate in authentik_service_account - CI: add SQLX_OFFLINE=true env var for offline builds
This commit is contained in:
@@ -11,6 +11,7 @@ env:
|
|||||||
REGISTRY: forgejo.riotpiao.com
|
REGISTRY: forgejo.riotpiao.com
|
||||||
IMAGE: forgejo.riotpiao.com/rock/poimen-memory
|
IMAGE: forgejo.riotpiao.com/rock/poimen-memory
|
||||||
DOCKER_HOST: tcp://localhost:2375
|
DOCKER_HOST: tcp://localhost:2375
|
||||||
|
SQLX_OFFLINE: "true"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ impl SynthesisClient {
|
|||||||
) -> Vec<Result<ClientResponse, String>> {
|
) -> Vec<Result<ClientResponse, String>> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for req in requests {
|
for req in requests {
|
||||||
results.push(self.execute(&req).await);
|
results.push(self.execute(req).await);
|
||||||
}
|
}
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
@@ -280,11 +280,12 @@ impl SynthesisClient {
|
|||||||
tracing::debug!("Workflow executed in {}ms", elapsed_ms);
|
tracing::debug!("Workflow executed in {}ms", elapsed_ms);
|
||||||
Ok(body)
|
Ok(body)
|
||||||
} else {
|
} else {
|
||||||
|
let status = response.status();
|
||||||
let error_text = response
|
let error_text = response
|
||||||
.text()
|
.text()
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| "unknown error".to_string());
|
.unwrap_or_else(|_| "unknown error".to_string());
|
||||||
Err(format!("Workflow failed ({}): {}", response.status(), error_text))
|
Err(format!("Workflow failed ({}): {}", status, error_text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,4 @@ pub use agent_interface::{Agent, AgentConfig, AgentCapability};
|
|||||||
pub use webhook_handler::{WebhookEvent, WebhookPayload};
|
pub use webhook_handler::{WebhookEvent, WebhookPayload};
|
||||||
pub use observability::{AgentMetrics, MetricsCollector};
|
pub use observability::{AgentMetrics, MetricsCollector};
|
||||||
pub use client_sdk::{SynthesisClient, ClientRequest, ClientResponse};
|
pub use client_sdk::{SynthesisClient, ClientRequest, ClientResponse};
|
||||||
|
pub use agent_interface::DefaultAgent;
|
||||||
/// Default agent implementation (type alias for Agent)
|
|
||||||
pub type DefaultAgent = Agent;
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::sync::{Arc, RwLock};
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use log::{debug, warn, error};
|
use tracing::{debug, warn, error};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AuthentikServiceAccountConfig {
|
pub struct AuthentikServiceAccountConfig {
|
||||||
|
|||||||
@@ -59,15 +59,17 @@ pub fn auth_error_response(error: &AuthError) -> HttpResponse {
|
|||||||
let (status, message) = match error {
|
let (status, message) = match error {
|
||||||
AuthError::MissingToken => ("Unauthorized", "Missing or invalid Authorization header"),
|
AuthError::MissingToken => ("Unauthorized", "Missing or invalid Authorization header"),
|
||||||
AuthError::InvalidSignature => ("Unauthorized", "Invalid token signature"),
|
AuthError::InvalidSignature => ("Unauthorized", "Invalid token signature"),
|
||||||
AuthError::ExpiredToken => ("Unauthorized", "Token has expired"),
|
AuthError::TokenExpired => ("Unauthorized", "Token has expired"),
|
||||||
AuthError::InvalidIssuer => ("Unauthorized", "Invalid token issuer"),
|
AuthError::InvalidIssuer => ("Unauthorized", "Invalid token issuer"),
|
||||||
AuthError::AccessDenied => ("Forbidden", "Access denied for this resource"),
|
AuthError::InvalidAudience => ("Unauthorized", "Invalid token audience"),
|
||||||
AuthError::InvalidClaims => ("Unauthorized", "Invalid or missing required claims"),
|
AuthError::ProviderUnavailable(_) => ("ServiceUnavailable", "Auth provider unavailable"),
|
||||||
|
AuthError::Other(_) => ("Unauthorized", "Authentication error"),
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpResponse::build(match status {
|
HttpResponse::build(match status {
|
||||||
"Unauthorized" => actix_web::http::StatusCode::UNAUTHORIZED,
|
"Unauthorized" => actix_web::http::StatusCode::UNAUTHORIZED,
|
||||||
"Forbidden" => actix_web::http::StatusCode::FORBIDDEN,
|
"Forbidden" => actix_web::http::StatusCode::FORBIDDEN,
|
||||||
|
"ServiceUnavailable" => actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||||
_ => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
_ => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
})
|
})
|
||||||
.json(json!({
|
.json(json!({
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub trait LlmCaller: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compaction statistics
|
/// Compaction statistics
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default, serde::Serialize)]
|
||||||
pub struct CompactionStats {
|
pub struct CompactionStats {
|
||||||
pub duplicate_edges_deleted: usize,
|
pub duplicate_edges_deleted: usize,
|
||||||
pub stale_facts_deleted: usize,
|
pub stale_facts_deleted: usize,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub fn validate_and_rate_limit(
|
|||||||
}))
|
}))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
jwt_validator.validate_bearer_token(auth_header).map_err(|e| {
|
crate::jwt_validator::JwtValidator::extract_bearer_token(auth_header).map_err(|e| {
|
||||||
HttpResponse::Unauthorized().json(json!({
|
HttpResponse::Unauthorized().json(json!({
|
||||||
"error": format!("JWT validation failed: {}", e)
|
"error": format!("JWT validation failed: {}", e)
|
||||||
}))
|
}))
|
||||||
@@ -51,10 +51,10 @@ pub fn validate_and_rate_limit(
|
|||||||
// 2. Rate limiting (if enabled)
|
// 2. Rate limiting (if enabled)
|
||||||
state
|
state
|
||||||
.rate_limiter
|
.rate_limiter
|
||||||
.check_limit(endpoint, rate_limit)
|
.check("default", endpoint)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
HttpResponse::TooManyRequests().json(json!({
|
HttpResponse::TooManyRequests().json(json!({
|
||||||
"error": format!("Rate limit exceeded: {}", e)
|
"error": format!("Rate limit exceeded: {}", e.reason())
|
||||||
}))
|
}))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ pub struct RankedResult {
|
|||||||
/// GET /memory/ranking/profiles
|
/// GET /memory/ranking/profiles
|
||||||
pub async fn get_ranking_profiles(req: HttpRequest) -> HttpResponse {
|
pub async fn get_ranking_profiles(req: HttpRequest) -> HttpResponse {
|
||||||
// Verify auth
|
// Verify auth
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ pub async fn rebuild(
|
|||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
// Verify auth
|
// Verify auth
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
@@ -155,7 +155,7 @@ pub async fn rebuild_status(
|
|||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
// Verify auth
|
// Verify auth
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -142,8 +142,8 @@ pub async fn search_entities_handler(
|
|||||||
body.query, body.entity_type, body.start_time, body.end_time);
|
body.query, body.entity_type, body.start_time, body.end_time);
|
||||||
|
|
||||||
// 3. Embed query
|
// 3. Embed query
|
||||||
let query_embedding = match state.embeddings.embed_text(&body.query).await {
|
let query_embedding = match state.embeddings.embed_one(&body.query).await {
|
||||||
Ok(emb) => emb,
|
Ok(emb) => emb.to_vec(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Embedding failed: {}", e);
|
error!("Embedding failed: {}", e);
|
||||||
return crate::handlers::response_builder::internal_error(
|
return crate::handlers::response_builder::internal_error(
|
||||||
@@ -278,8 +278,8 @@ pub async fn search_edges_handler(
|
|||||||
body.query, body.relation_type, body.start_time, body.end_time);
|
body.query, body.relation_type, body.start_time, body.end_time);
|
||||||
|
|
||||||
// 3. Embed query
|
// 3. Embed query
|
||||||
let query_embedding = match state.embeddings.embed_text(&body.query).await {
|
let query_embedding = match state.embeddings.embed_one(&body.query).await {
|
||||||
Ok(emb) => emb,
|
Ok(emb) => emb.to_vec(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Embedding failed: {}", e);
|
error!("Embedding failed: {}", e);
|
||||||
return crate::handlers::response_builder::internal_error(
|
return crate::handlers::response_builder::internal_error(
|
||||||
@@ -361,8 +361,8 @@ pub async fn hybrid_search_handler(
|
|||||||
body.query, body.semantic_weight, body.lexical_weight);
|
body.query, body.semantic_weight, body.lexical_weight);
|
||||||
|
|
||||||
// 3. Embed query
|
// 3. Embed query
|
||||||
let query_embedding = match state.embeddings.embed_text(&body.query).await {
|
let query_embedding = match state.embeddings.embed_one(&body.query).await {
|
||||||
Ok(emb) => emb,
|
Ok(emb) => emb.to_vec(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Embedding failed: {}", e);
|
error!("Embedding failed: {}", e);
|
||||||
return crate::handlers::response_builder::internal_error(
|
return crate::handlers::response_builder::internal_error(
|
||||||
|
|||||||
@@ -517,11 +517,12 @@ pub async fn reasoning_paths_handler(
|
|||||||
let elapsed = start_time.elapsed().as_millis();
|
let elapsed = start_time.elapsed().as_millis();
|
||||||
info!("Paths: {} found in {}ms", paths.len(), elapsed);
|
info!("Paths: {} found in {}ms", paths.len(), elapsed);
|
||||||
|
|
||||||
|
let path_count = paths.len();
|
||||||
crate::handlers::response_builder::success_response(ReasoningPathsResponse {
|
crate::handlers::response_builder::success_response(ReasoningPathsResponse {
|
||||||
source_id: body.source_id.clone(),
|
source_id: body.source_id.clone(),
|
||||||
target_id: body.target_id.clone(),
|
target_id: body.target_id.clone(),
|
||||||
paths,
|
paths,
|
||||||
path_count: paths.len(),
|
path_count,
|
||||||
process_time_ms: elapsed,
|
process_time_ms: elapsed,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ pub async fn unified_query_handler(
|
|||||||
body.search_type, body.query, body.entity_type, body.relation_type);
|
body.search_type, body.query, body.entity_type, body.relation_type);
|
||||||
|
|
||||||
// 3. Embed query once (reused for all search types)
|
// 3. Embed query once (reused for all search types)
|
||||||
let query_embedding = match state.embeddings.embed_text(&body.query).await {
|
let query_embedding = match state.embeddings.embed_one(&body.query).await {
|
||||||
Ok(emb) => emb,
|
Ok(emb) => emb.to_vec(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Embedding failed: {}", e);
|
error!("Embedding failed: {}", e);
|
||||||
return crate::handlers::response_builder::internal_error(
|
return crate::handlers::response_builder::internal_error(
|
||||||
|
|||||||
@@ -158,12 +158,12 @@ pub async fn unified_synthesis_handler(
|
|||||||
// Entity Linking
|
// Entity Linking
|
||||||
if body.link_entities {
|
if body.link_entities {
|
||||||
let linker = EntityLinker::new(state.pool.clone());
|
let linker = EntityLinker::new(state.pool.clone());
|
||||||
match linker.link_entities(&body.content) {
|
match linker.link_mentions(&body.content, &body.project).await {
|
||||||
Ok(links) => {
|
Ok((links, _unlinked)) => {
|
||||||
let alias_count = links.iter().filter(|l| l.confidence > 0.85).count();
|
let alias_count = links.iter().filter(|l| l.confidence > 0.85).count();
|
||||||
entity_linking = Some(EntityLinkingResult {
|
entity_linking = Some(EntityLinkingResult {
|
||||||
mention_links: links.iter().map(|l| MentionLinkResponse {
|
mention_links: links.iter().map(|l| MentionLinkResponse {
|
||||||
mention: l.mention.clone(),
|
mention: l.mention_text.clone(),
|
||||||
entity_id: l.entity_id.clone(),
|
entity_id: l.entity_id.clone(),
|
||||||
confidence: l.confidence,
|
confidence: l.confidence,
|
||||||
}).collect(),
|
}).collect(),
|
||||||
@@ -179,14 +179,14 @@ pub async fn unified_synthesis_handler(
|
|||||||
|
|
||||||
// Inference
|
// Inference
|
||||||
if body.infer_facts {
|
if body.infer_facts {
|
||||||
let engine = InferenceEngine::new(state.pool.clone());
|
let engine = InferenceEngine::new(state.pool.clone(), vec![]);
|
||||||
match engine.infer_facts(&body.content, 5, 0.6, &body.project) {
|
match engine.infer_facts(&body.project, &body.content, 5).await {
|
||||||
Ok(facts) => {
|
Ok(facts) => {
|
||||||
inference = Some(InferenceResult {
|
inference = Some(InferenceResult {
|
||||||
inferred_facts: facts.iter().map(|f| InferredFactResponse {
|
inferred_facts: facts.iter().map(|f| InferredFactResponse {
|
||||||
source: f.source.clone(),
|
source: f.source_id.clone(),
|
||||||
relation: f.relation.clone(),
|
relation: f.relation_type.clone(),
|
||||||
target: f.target.clone(),
|
target: f.target_id.clone(),
|
||||||
confidence: f.confidence,
|
confidence: f.confidence,
|
||||||
}).collect(),
|
}).collect(),
|
||||||
fact_count: facts.len(),
|
fact_count: facts.len(),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub async fn get_entity_versions(
|
|||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
// Verify auth
|
// Verify auth
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
@@ -46,7 +46,7 @@ pub async fn get_entity_version(
|
|||||||
path: web::Path<(String, i32)>,
|
path: web::Path<(String, i32)>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
@@ -80,7 +80,7 @@ pub async fn get_entity_diff(
|
|||||||
query: web::Query<DiffQuery>,
|
query: web::Query<DiffQuery>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
@@ -120,7 +120,7 @@ pub async fn get_entity_at_time(
|
|||||||
query: web::Query<TimeQuery>,
|
query: web::Query<TimeQuery>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
@@ -164,7 +164,7 @@ pub async fn get_edge_versions(
|
|||||||
path: web::Path<Uuid>,
|
path: web::Path<Uuid>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
@@ -196,7 +196,7 @@ pub async fn get_edge_diff(
|
|||||||
query: web::Query<DiffQuery>,
|
query: web::Query<DiffQuery>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
if let Err(e) = AuthGuard::extract_token(&req) {
|
if let Err(e) = AuthGuard::extract_token(req.headers().get("Authorization").and_then(|v| v.to_str().ok()).unwrap_or("")) {
|
||||||
return HttpResponse::Unauthorized().json(json!({
|
return HttpResponse::Unauthorized().json(json!({
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -145,13 +145,15 @@ pub async fn visualize_stream_handler(
|
|||||||
match execute_streaming_visualization(&state, req_body).await {
|
match execute_streaming_visualization(&state, req_body).await {
|
||||||
Ok(events) => {
|
Ok(events) => {
|
||||||
for event in events {
|
for event in events {
|
||||||
yield format_sse_event(event);
|
let data = format_sse_event(event);
|
||||||
|
yield Ok::<actix_web::web::Bytes, actix_web::Error>(actix_web::web::Bytes::from(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
yield format_sse_event(VisualizeEvent::Error {
|
let data = format_sse_event(VisualizeEvent::Error {
|
||||||
message: e,
|
message: e,
|
||||||
});
|
});
|
||||||
|
yield Ok::<actix_web::web::Bytes, actix_web::Error>(actix_web::web::Bytes::from(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -161,7 +163,7 @@ pub async fn visualize_stream_handler(
|
|||||||
.insert_header(("Cache-Control", "no-cache"))
|
.insert_header(("Cache-Control", "no-cache"))
|
||||||
.insert_header(("Connection", "keep-alive"))
|
.insert_header(("Connection", "keep-alive"))
|
||||||
.insert_header(("Transfer-Encoding", "chunked"))
|
.insert_header(("Transfer-Encoding", "chunked"))
|
||||||
.streaming_body(Box::pin(stream))
|
.streaming(Box::pin(stream))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute streaming visualization (generates events)
|
/// Execute streaming visualization (generates events)
|
||||||
|
|||||||
@@ -116,13 +116,13 @@ impl ParallelDualWriteIndexer {
|
|||||||
|
|
||||||
// Spawn background task (non-blocking)
|
// Spawn background task (non-blocking)
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let result = opensearch.index_chunk(
|
let result = opensearch.index_document(
|
||||||
&chunk_id,
|
&chunk_id,
|
||||||
&chunk.content,
|
&chunk.content,
|
||||||
&chunk.source,
|
&chunk.source,
|
||||||
&chunk.project,
|
|
||||||
&chunk.level,
|
&chunk.level,
|
||||||
&chunk.breadcrumb.join(" > "),
|
chunk.breadcrumb.clone(),
|
||||||
|
"", // jwt_token - not available in background task
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@@ -139,8 +139,8 @@ impl ParallelDualWriteIndexer {
|
|||||||
&self,
|
&self,
|
||||||
chunks: Vec<(&IndexableChunk, Vec<f32>)>,
|
chunks: Vec<(&IndexableChunk, Vec<f32>)>,
|
||||||
) -> Vec<DualWriteResult> {
|
) -> Vec<DualWriteResult> {
|
||||||
let futures = chunks.into_iter().map(|(chunk, embedding)| {
|
let futures = chunks.into_iter().map(|(chunk, embedding)| async move {
|
||||||
self.index_parallel(chunk, &embedding)
|
self.index_parallel(chunk, &embedding).await
|
||||||
});
|
});
|
||||||
|
|
||||||
futures::future::join_all(futures)
|
futures::future::join_all(futures)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::{Pool, Postgres};
|
use sqlx::{Pool, Postgres, Row};
|
||||||
|
|
||||||
/// A node in the traversal result
|
/// A node in the traversal result
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -196,6 +196,7 @@ impl BfsGraphTraversal {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let edge_count = edges.len();
|
||||||
Ok(GraphData {
|
Ok(GraphData {
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
@@ -203,7 +204,7 @@ impl BfsGraphTraversal {
|
|||||||
requested_depth: config.max_depth,
|
requested_depth: config.max_depth,
|
||||||
max_depth_reached: max_depth,
|
max_depth_reached: max_depth,
|
||||||
node_count: visited.len(),
|
node_count: visited.len(),
|
||||||
edge_count: edges.len(),
|
edge_count,
|
||||||
depth_breakdown,
|
depth_breakdown,
|
||||||
traversal_time_ms: start_time.elapsed().as_millis() as u64,
|
traversal_time_ms: start_time.elapsed().as_millis() as u64,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -185,9 +185,9 @@ impl CommunityDetector {
|
|||||||
|
|
||||||
communities_vec.push(Community {
|
communities_vec.push(Community {
|
||||||
id: comm_id,
|
id: comm_id,
|
||||||
|
size: members.len(),
|
||||||
entity_ids: members.into_iter().collect(),
|
entity_ids: members.into_iter().collect(),
|
||||||
entity_names,
|
entity_names,
|
||||||
size: members.len(),
|
|
||||||
modularity_contribution: modularity_contrib,
|
modularity_contribution: modularity_contrib,
|
||||||
average_strength: strength,
|
average_strength: strength,
|
||||||
density,
|
density,
|
||||||
@@ -196,9 +196,9 @@ impl CommunityDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. Calculate total modularity
|
// 5. Calculate total modularity
|
||||||
let total_modularity = communities_vec
|
let total_modularity: f64 = communities_vec
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.modularity_contribution)
|
.map(|c| c.modularity_contribution as f64)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
let average_community_size = if communities_vec.is_empty() {
|
let average_community_size = if communities_vec.is_empty() {
|
||||||
@@ -210,9 +210,9 @@ impl CommunityDetector {
|
|||||||
let result = CommunityDetectionResult {
|
let result = CommunityDetectionResult {
|
||||||
entity_count: entities.len(),
|
entity_count: entities.len(),
|
||||||
edge_count: edges.len(),
|
edge_count: edges.len(),
|
||||||
communities: communities_vec,
|
|
||||||
community_count: communities_vec.len(),
|
community_count: communities_vec.len(),
|
||||||
total_modularity: total_modularity.max(-1.0).min(1.0),
|
communities: communities_vec,
|
||||||
|
total_modularity: total_modularity.max(-1.0).min(1.0) as f32,
|
||||||
average_community_size,
|
average_community_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! Enables multi-dimensional filtering across entities and edges.
|
//! Enables multi-dimensional filtering across entities and edges.
|
||||||
//! Supports entity types, relation types, date ranges, confidence levels, and more.
|
//! Supports entity types, relation types, date ranges, confidence levels, and more.
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Timelike, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{Pool, Postgres};
|
use sqlx::{Pool, Postgres};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -42,7 +42,7 @@ pub struct AvailableFacets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Facet filters for a query
|
/// Facet filters for a query
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct FacetFilters {
|
pub struct FacetFilters {
|
||||||
/// Filter by entity types (OR within facet, AND across facets)
|
/// Filter by entity types (OR within facet, AND across facets)
|
||||||
pub entity_types: Option<Vec<String>>,
|
pub entity_types: Option<Vec<String>>,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use super::bfs_graph_traversal::{GraphData, TraversalNode, TraversalEdge};
|
use super::bfs_graph_traversal::{GraphData, TraversalNode, TraversalEdge};
|
||||||
|
|
||||||
/// 2D position (X, Y coordinates)
|
/// 2D position (X, Y coordinates)
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
//! confidence propagation through reasoning chains.
|
//! confidence propagation through reasoning chains.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
@@ -293,18 +295,19 @@ impl InferenceEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DFS to find all paths
|
/// DFS to find all paths
|
||||||
async fn dfs_paths(
|
fn dfs_paths<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
current: &str,
|
current: &'a str,
|
||||||
target: &str,
|
target: &'a str,
|
||||||
project_id: &str,
|
project_id: &'a str,
|
||||||
remaining_hops: usize,
|
remaining_hops: usize,
|
||||||
path: &mut Vec<String>,
|
path: &'a mut Vec<String>,
|
||||||
relations: &mut Vec<String>,
|
relations: &'a mut Vec<String>,
|
||||||
confidences: &mut Vec<f32>,
|
confidences: &'a mut Vec<f32>,
|
||||||
visited: &mut HashSet<String>,
|
visited: &'a mut HashSet<String>,
|
||||||
results: &mut Vec<ReasoningPath>,
|
results: &'a mut Vec<ReasoningPath>,
|
||||||
) -> Result<(), String> {
|
) -> Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
if remaining_hops == 0 {
|
if remaining_hops == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -350,6 +353,7 @@ impl InferenceEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}) // Box::pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{Pool, Postgres};
|
use sqlx::{Pool, Postgres};
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
/// A single path through the graph
|
/// A single path through the graph
|
||||||
@@ -123,13 +125,14 @@ impl PathFinder {
|
|||||||
info!("Found shortest path: {} → {} (distance: {})",
|
info!("Found shortest path: {} → {} (distance: {})",
|
||||||
source_id, target_id, final_entities.len() - 1);
|
source_id, target_id, final_entities.len() - 1);
|
||||||
|
|
||||||
|
let distance = final_entities.len() - 1;
|
||||||
return Ok(Some(Path {
|
return Ok(Some(Path {
|
||||||
source_id: source_id.to_string(),
|
source_id: source_id.to_string(),
|
||||||
target_id: target_id.to_string(),
|
target_id: target_id.to_string(),
|
||||||
entity_ids: final_entities,
|
entity_ids: final_entities,
|
||||||
entity_names: vec![], // Could fetch from DB if needed
|
entity_names: vec![], // Could fetch from DB if needed
|
||||||
relation_types: final_relations,
|
relation_types: final_relations,
|
||||||
distance: final_entities.len() - 1,
|
distance,
|
||||||
total_confidence: final_confidence.max(0.0).min(1.0),
|
total_confidence: final_confidence.max(0.0).min(1.0),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -293,19 +296,20 @@ impl PathFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DFS helper for finding all paths
|
/// DFS helper for finding all paths
|
||||||
async fn dfs_paths(
|
fn dfs_paths<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
source_id: &str,
|
source_id: &'a str,
|
||||||
target_id: &str,
|
target_id: &'a str,
|
||||||
current_path: Vec<String>,
|
current_path: Vec<String>,
|
||||||
relations_path: Vec<String>,
|
relations_path: Vec<String>,
|
||||||
confidence: f32,
|
confidence: f32,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
paths_found: &mut Vec<Path>,
|
paths_found: &'a mut Vec<Path>,
|
||||||
visited: &mut HashSet<String>,
|
visited: &'a mut HashSet<String>,
|
||||||
max_paths: usize,
|
max_paths: usize,
|
||||||
) -> Result<(), String> {
|
) -> Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
if paths_found.len() >= max_paths {
|
if paths_found.len() >= max_paths {
|
||||||
return Ok(()); // Found enough paths
|
return Ok(()); // Found enough paths
|
||||||
}
|
}
|
||||||
@@ -328,13 +332,14 @@ impl PathFinder {
|
|||||||
|
|
||||||
let final_confidence = confidence * edge.confidence;
|
let final_confidence = confidence * edge.confidence;
|
||||||
|
|
||||||
|
let distance = final_path.len() - 1;
|
||||||
paths_found.push(Path {
|
paths_found.push(Path {
|
||||||
source_id: source_id.to_string(),
|
source_id: source_id.to_string(),
|
||||||
target_id: target_id.to_string(),
|
target_id: target_id.to_string(),
|
||||||
entity_ids: final_path,
|
entity_ids: final_path,
|
||||||
entity_names: vec![],
|
entity_names: vec![],
|
||||||
relation_types: final_relations,
|
relation_types: final_relations,
|
||||||
distance: final_path.len() - 1,
|
distance,
|
||||||
total_confidence: final_confidence.max(0.0).min(1.0),
|
total_confidence: final_confidence.max(0.0).min(1.0),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -370,6 +375,7 @@ impl PathFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}) // Box::pin
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch direct neighbors of an entity
|
/// Fetch direct neighbors of an entity
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ impl SemanticRetriever {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Database error: {}", e))?;
|
.map_err(|e| format!("Database error: {}", e))?;
|
||||||
|
|
||||||
let entities = results
|
let entities: Vec<_> = results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(id, name, entity_type, score, metadata)| EntityResult {
|
.map(|(id, name, entity_type, score, metadata)| EntityResult {
|
||||||
id,
|
id,
|
||||||
@@ -213,7 +213,7 @@ impl SemanticRetriever {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Database error: {}", e))?;
|
.map_err(|e| format!("Database error: {}", e))?;
|
||||||
|
|
||||||
let edges = results
|
let edges: Vec<_> = results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(id, src_id, tgt_id, src_name, tgt_name, rel_type, fact, score, conf)| {
|
.map(|(id, src_id, tgt_id, src_name, tgt_name, rel_type, fact, score, conf)| {
|
||||||
EdgeResult {
|
EdgeResult {
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ impl Summarizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Split text into sentences
|
/// Split text into sentences
|
||||||
fn split_sentences(&self, text: &str) -> Vec<&str> {
|
fn split_sentences<'a>(&self, text: &'a str) -> Vec<&'a str> {
|
||||||
text.split('.').map(|s| s.trim()).filter(|s| !s.is_empty()).collect()
|
text.split('.').map(|s| s.trim()).filter(|s| !s.is_empty()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ impl Summarizer {
|
|||||||
|
|
||||||
let overlap = entities1
|
let overlap = entities1
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|e| entities2.contains(e))
|
.filter(|e| entities2.contains(*e))
|
||||||
.count();
|
.count();
|
||||||
coherence += overlap as f32 / (entities1.len().max(entities2.len()) as f32).max(1.0);
|
coherence += overlap as f32 / (entities1.len().max(entities2.len()) as f32).max(1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,6 +245,8 @@ impl QueryRouter {
|
|||||||
prefilter_size,
|
prefilter_size,
|
||||||
metrics,
|
metrics,
|
||||||
latency_ms,
|
latency_ms,
|
||||||
|
confidence_score: 1.0,
|
||||||
|
is_valid: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user