Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e3f7c9a5 | ||
|
|
33eaf1b4f8 |
@@ -1,6 +1,5 @@
|
|||||||
use actix_web::{web, App, HttpServer, HttpResponse, HttpRequest, middleware::Logger};
|
use actix_web::{web, App, HttpServer, HttpResponse, HttpRequest, middleware::Logger};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono;
|
|
||||||
use mem_llm::{EmbeddingsClient, RerankClient};
|
use mem_llm::{EmbeddingsClient, RerankClient};
|
||||||
use mem_store::{init_schema, VectorStore};
|
use mem_store::{init_schema, VectorStore};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@@ -75,7 +74,6 @@ pub async fn start_server(port: u16, api_key: String, database_url: &str) -> Res
|
|||||||
.route("/memory/query", web::get().to(query_handler))
|
.route("/memory/query", web::get().to(query_handler))
|
||||||
.route("/memory/projects", web::get().to(projects_handler))
|
.route("/memory/projects", web::get().to(projects_handler))
|
||||||
.route("/memory/skills", web::get().to(skills_handler))
|
.route("/memory/skills", web::get().to(skills_handler))
|
||||||
.route("/memory/vault", web::post().to(vault_handler))
|
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", port))?
|
.bind(("0.0.0.0", port))?
|
||||||
.run()
|
.run()
|
||||||
@@ -300,101 +298,3 @@ pub async fn skills_handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// POST /memory/vault — generate Obsidian vault from memories
|
|
||||||
pub async fn vault_handler(
|
|
||||||
req: HttpRequest,
|
|
||||||
state: web::Data<AppState>,
|
|
||||||
) -> HttpResponse {
|
|
||||||
if let Err(e) = check_auth(&req, &state) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
let vault_dir = std::env::var("MEM_HOME").unwrap_or_else(|_| "/data".to_string());
|
|
||||||
|
|
||||||
// Get all projects from database
|
|
||||||
let projects_result = sqlx::query_as::<_, (String,)>(
|
|
||||||
"SELECT DISTINCT project FROM memories_l1 ORDER BY project",
|
|
||||||
)
|
|
||||||
.fetch_all(&state.pool)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match projects_result {
|
|
||||||
Ok(projects) => {
|
|
||||||
let mut generated = 0;
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
for (project,) in projects {
|
|
||||||
let project_vault_dir = format!("{}/vault/{}", vault_dir, project);
|
|
||||||
|
|
||||||
// Create project directory
|
|
||||||
if let Err(e) = std::fs::create_dir_all(&project_vault_dir) {
|
|
||||||
errors.push(format!("Failed to create {}: {}", project_vault_dir, e));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all L1 memories for this project
|
|
||||||
let l1s_result = sqlx::query_as::<_, (String, String, String)>(
|
|
||||||
"SELECT id, query_id, content FROM memories_l1 WHERE project = $1 ORDER BY updated_at DESC",
|
|
||||||
)
|
|
||||||
.bind(&project)
|
|
||||||
.fetch_all(&state.pool)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match l1s_result {
|
|
||||||
Ok(l1s) => {
|
|
||||||
for (id, query_id, content) in l1s {
|
|
||||||
let filename = format!("{}/{}.md", project_vault_dir, query_id);
|
|
||||||
let note = format!(
|
|
||||||
"---\nproject: {}\nlevel: L1\nquery_id: {}\nid: {}\nupdated: {}\n---\n\n{}",
|
|
||||||
project,
|
|
||||||
query_id,
|
|
||||||
id,
|
|
||||||
chrono::Utc::now().to_rfc3339(),
|
|
||||||
content
|
|
||||||
);
|
|
||||||
if std::fs::write(&filename, note).is_ok() {
|
|
||||||
generated += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
errors.push(format!("Failed to fetch L1s for {}: {}", project, e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get L2 synthesis
|
|
||||||
let l2_result = sqlx::query_as::<_, (String,)>(
|
|
||||||
"SELECT content FROM memories_l2 WHERE project = $1",
|
|
||||||
)
|
|
||||||
.bind(&project)
|
|
||||||
.fetch_optional(&state.pool)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if let Ok(Some((content,))) = l2_result {
|
|
||||||
let filename = format!("{}/index.md", project_vault_dir);
|
|
||||||
let note = format!(
|
|
||||||
"---\nproject: {}\nlevel: L2\ntitle: {} Synthesis\nupdated: {}\n---\n\n{}",
|
|
||||||
project,
|
|
||||||
project,
|
|
||||||
chrono::Utc::now().to_rfc3339(),
|
|
||||||
content
|
|
||||||
);
|
|
||||||
if std::fs::write(&filename, note).is_ok() {
|
|
||||||
generated += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponse::Ok().json(json!({
|
|
||||||
"status": "generated",
|
|
||||||
"vault_dir": format!("{}/vault", vault_dir),
|
|
||||||
"notes_created": generated,
|
|
||||||
"errors": errors
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
HttpResponse::InternalServerError().json(json!({"error": "database_error"}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
mod lessons_cmd;
|
mod lessons_cmd;
|
||||||
mod http_server;
|
mod http_server;
|
||||||
mod endpoints;
|
mod endpoints;
|
||||||
mod ingest_worker;
|
|
||||||
mod query_worker;
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use mem_chunk::token_counter::CharsOverFourCounter;
|
use mem_chunk::token_counter::CharsOverFourCounter;
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ spec:
|
|||||||
mountPath: /data
|
mountPath: /data
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
persistentVolumeClaim:
|
emptyDir: {}
|
||||||
claimName: poimen-memory-vault
|
|
||||||
# Tolerate control-plane nodes
|
# Tolerate control-plane nodes
|
||||||
tolerations:
|
tolerations:
|
||||||
- key: node-role.kubernetes.io/control-plane
|
- key: node-role.kubernetes.io/control-plane
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
namespace: poimen
|
namespace: poimen
|
||||||
resources:
|
resources:
|
||||||
- vault-pvc.yaml
|
|
||||||
- deployment.yaml
|
- deployment.yaml
|
||||||
- service.yaml
|
- service.yaml
|
||||||
# Secret managed separately (SealedSecret in homelab)
|
# Secret managed separately (SealedSecret in homelab)
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: poimen-memory-vault
|
|
||||||
namespace: poimen
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: longhorn
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 10Gi
|
|
||||||
+14
-20
@@ -7,13 +7,11 @@ async fn a1_bare_array_parsed() {
|
|||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/v1/rerank"))
|
.and(path("/rerank"))
|
||||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
|
.respond_with(ResponseTemplate::new(200).set_body_json(vec![
|
||||||
"results": [
|
serde_json::json!({"index": 0, "score": 0.98}),
|
||||||
{"index": 0, "score": 0.98},
|
serde_json::json!({"index": 1, "score": 0.01}),
|
||||||
{"index": 1, "score": 0.01},
|
]))
|
||||||
]
|
|
||||||
})))
|
|
||||||
.mount(&mock_server)
|
.mount(&mock_server)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -34,13 +32,11 @@ async fn a2_index_mapping() {
|
|||||||
|
|
||||||
// Return out-of-order: index 1 first, then index 0
|
// Return out-of-order: index 1 first, then index 0
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/v1/rerank"))
|
.and(path("/rerank"))
|
||||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
|
.respond_with(ResponseTemplate::new(200).set_body_json(vec![
|
||||||
"results": [
|
serde_json::json!({"index": 1, "score": 0.99}),
|
||||||
{"index": 1, "score": 0.99},
|
serde_json::json!({"index": 0, "score": 0.01}),
|
||||||
{"index": 0, "score": 0.01},
|
]))
|
||||||
]
|
|
||||||
})))
|
|
||||||
.mount(&mock_server)
|
.mount(&mock_server)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -72,12 +68,10 @@ async fn a4_apikey_sent() {
|
|||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/v1/rerank"))
|
.and(path("/rerank"))
|
||||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
|
.respond_with(ResponseTemplate::new(200).set_body_json(vec![
|
||||||
"results": [
|
serde_json::json!({"index": 0, "score": 0.95}),
|
||||||
{"index": 0, "score": 0.95},
|
]))
|
||||||
]
|
|
||||||
})))
|
|
||||||
.mount(&mock_server)
|
.mount(&mock_server)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user