mirror of
https://github.com/perstarkse/minne.git
synced 2026-03-25 10:52:07 +01:00
wip: node relationships and improved prompt
This commit is contained in:
@@ -33,6 +33,7 @@ use zettle_db::{
|
||||
gdpr::{accept_gdpr, deny_gdpr},
|
||||
index::{delete_job, delete_text_content, index_handler},
|
||||
ingress_form::{hide_ingress_form, process_ingress_form, show_ingress_form},
|
||||
knowledge::entities::show_knowledge_page,
|
||||
privacy_policy::show_privacy_policy,
|
||||
search_result::search_result_handler,
|
||||
signin::{authenticate_user, show_signin_form},
|
||||
@@ -170,6 +171,7 @@ fn html_routes(
|
||||
.route("/hide-ingress-form", get(hide_ingress_form))
|
||||
.route("/text-content/:id", delete(delete_text_content))
|
||||
.route("/jobs/:job_id", delete(delete_job))
|
||||
.route("/knowledge", get(show_knowledge_page))
|
||||
.route("/account", get(show_account_page))
|
||||
.route("/admin", get(show_admin_panel))
|
||||
.route("/toggle-registrations", patch(toggle_registration_status))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde_json::{json, Value};
|
||||
|
||||
pub static INGRESS_ANALYSIS_SYSTEM_MESSAGE: &str = r#"
|
||||
You are an expert document analyzer. You will receive a document's text content, along with user instructions and a category. Your task is to provide a structured JSON object representing the content in a graph format suitable for a graph database. You will also be presented with some existing knowledge_entities from the database, do not replicate these!
|
||||
You are an AI assistant. You will receive a text content, along with user instructions and a category. Your task is to provide a structured JSON object representing the content in a graph format suitable for a graph database. You will also be presented with some existing knowledge_entities from the database, do not replicate these! Your task is to create meaningful knowledge entities from the submitted content. Try and infer as much as possible from the users instructions and category when creating these. If the user submits a large content, create more general entities. If the user submits a narrow and precise content, try and create precise knowledge entities.
|
||||
|
||||
The JSON should have the following structure:
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ impl LLMGraphAnalysisResult {
|
||||
.await?;
|
||||
|
||||
// Process relationships
|
||||
let relationships = self.process_relationships(source_id, Arc::clone(&mapper))?;
|
||||
let relationships = self.process_relationships(source_id, user_id, Arc::clone(&mapper))?;
|
||||
|
||||
Ok((entities, relationships))
|
||||
}
|
||||
@@ -117,6 +117,7 @@ impl LLMGraphAnalysisResult {
|
||||
fn process_relationships(
|
||||
&self,
|
||||
source_id: &str,
|
||||
user_id: &str,
|
||||
mapper: Arc<Mutex<GraphMapper>>,
|
||||
) -> Result<Vec<KnowledgeRelationship>, AppError> {
|
||||
let mut mapper_guard = mapper
|
||||
@@ -131,9 +132,9 @@ impl LLMGraphAnalysisResult {
|
||||
Ok(KnowledgeRelationship::new(
|
||||
source_db_id.to_string(),
|
||||
target_db_id.to_string(),
|
||||
user_id.to_string(),
|
||||
source_id.to_string(),
|
||||
rel.type_.clone(),
|
||||
None,
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -26,16 +26,42 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
page_data!(KnowledgeEntitiesData, "todo", {
|
||||
gdpr_accepted: bool,
|
||||
user: Option<User>,
|
||||
latest_text_contents: Vec<TextContent>,
|
||||
active_jobs: Vec<Job>
|
||||
page_data!(KnowledgeBaseData, "knowledge/base.html", {
|
||||
entities: Vec<KnowledgeEntity>,
|
||||
relationships: Vec<KnowledgeRelationship>,
|
||||
user: User
|
||||
});
|
||||
pub async fn index_handler(
|
||||
|
||||
pub async fn show_knowledge_page(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
session: Session<SessionSurrealPool<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
Ok("Hi".into_response())
|
||||
// Early return if the user is not authenticated
|
||||
let user = match auth.current_user {
|
||||
Some(user) => user,
|
||||
None => return Ok(Redirect::to("/signin").into_response()),
|
||||
};
|
||||
|
||||
let entities = User::get_knowledge_entities(&user.id, &state.surreal_db_client)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
info!("Got entities ok");
|
||||
|
||||
let relationships = User::get_knowledge_relationships(&user.id, &state.surreal_db_client)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
let output = render_template(
|
||||
KnowledgeBaseData::template_name(),
|
||||
KnowledgeBaseData {
|
||||
entities,
|
||||
relationships,
|
||||
user,
|
||||
},
|
||||
state.templates,
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
|
||||
1
src/server/routes/html/knowledge/mod.rs
Normal file
1
src/server/routes/html/knowledge/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod entities;
|
||||
@@ -11,6 +11,7 @@ pub mod documentation;
|
||||
pub mod gdpr;
|
||||
pub mod index;
|
||||
pub mod ingress_form;
|
||||
pub mod knowledge;
|
||||
pub mod privacy_policy;
|
||||
pub mod search_result;
|
||||
pub mod signin;
|
||||
|
||||
@@ -1,46 +1,62 @@
|
||||
use crate::{error::AppError, storage::db::SurrealDbClient, stored_object};
|
||||
use crate::storage::types::file_info::deserialize_flexible_id;
|
||||
use crate::{error::AppError, storage::db::SurrealDbClient};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
stored_object!(KnowledgeRelationship, "relates_to", {
|
||||
#[serde(rename = "in")]
|
||||
in_: String,
|
||||
out: String,
|
||||
relationship_type: String,
|
||||
source_id: String,
|
||||
metadata: Option<serde_json::Value>
|
||||
});
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct RelationshipMetadata {
|
||||
pub user_id: String,
|
||||
pub source_id: String,
|
||||
pub relationship_type: String,
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct KnowledgeRelationship {
|
||||
#[serde(deserialize_with = "deserialize_flexible_id")]
|
||||
pub id: String,
|
||||
#[serde(rename = "in", deserialize_with = "deserialize_flexible_id")]
|
||||
pub in_: String,
|
||||
#[serde(deserialize_with = "deserialize_flexible_id")]
|
||||
pub out: String,
|
||||
pub metadata: RelationshipMetadata,
|
||||
}
|
||||
|
||||
impl KnowledgeRelationship {
|
||||
pub fn new(
|
||||
in_: String,
|
||||
out: String,
|
||||
user_id: String,
|
||||
source_id: String,
|
||||
relationship_type: String,
|
||||
metadata: Option<serde_json::Value>,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
in_,
|
||||
out,
|
||||
source_id,
|
||||
relationship_type,
|
||||
metadata,
|
||||
metadata: RelationshipMetadata {
|
||||
user_id,
|
||||
source_id,
|
||||
relationship_type,
|
||||
},
|
||||
}
|
||||
}
|
||||
pub async fn store_relationship(&self, db_client: &Surreal<Any>) -> Result<(), AppError> {
|
||||
let query = format!(
|
||||
"RELATE knowledge_entity:`{}` -> relates_to -> knowledge_entity:`{}`",
|
||||
self.in_, self.out
|
||||
r#"RELATE knowledge_entity:`{}`->relates_to:`{}`->knowledge_entity:`{}`
|
||||
SET
|
||||
metadata.user_id = '{}',
|
||||
metadata.source_id = '{}',
|
||||
metadata.relationship_type = '{}'"#,
|
||||
self.in_,
|
||||
self.id,
|
||||
self.out,
|
||||
self.metadata.user_id,
|
||||
self.metadata.source_id,
|
||||
self.metadata.relationship_type
|
||||
);
|
||||
|
||||
let result = db_client.query(query).await?;
|
||||
|
||||
debug!("{:?}", result);
|
||||
db_client.query(query).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -50,7 +66,7 @@ impl KnowledgeRelationship {
|
||||
db_client: &SurrealDbClient,
|
||||
) -> Result<(), AppError> {
|
||||
let query = format!(
|
||||
"DELETE knowledge_entity -> relates_to WHERE source_id = `{}`",
|
||||
"DELETE knowledge_entity -> relates_to WHERE metadata.source_id = '{}'",
|
||||
source_id
|
||||
);
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ use crate::{
|
||||
};
|
||||
use axum_session_auth::Authentication;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{
|
||||
knowledge_entity::KnowledgeEntity, system_settings::SystemSettings, text_content::TextContent,
|
||||
knowledge_entity::KnowledgeEntity, knowledge_relationship::KnowledgeRelationship,
|
||||
system_settings::SystemSettings, text_content::TextContent,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -199,7 +201,8 @@ impl User {
|
||||
) -> Result<Vec<KnowledgeEntity>, AppError> {
|
||||
let entities: Vec<KnowledgeEntity> = db
|
||||
.client
|
||||
.query("SELECT * FROM knowledge_entity WHERE user_id = $user_id")
|
||||
.query("SELECT * FROM type::table($table) WHERE user_id = $user_id")
|
||||
.bind(("table", KnowledgeEntity::table_name()))
|
||||
.bind(("user_id", user_id.to_owned()))
|
||||
.await?
|
||||
.take(0)?;
|
||||
@@ -207,6 +210,21 @@ impl User {
|
||||
Ok(entities)
|
||||
}
|
||||
|
||||
pub async fn get_knowledge_relationships(
|
||||
user_id: &str,
|
||||
db: &SurrealDbClient,
|
||||
) -> Result<Vec<KnowledgeRelationship>, AppError> {
|
||||
let relationships: Vec<KnowledgeRelationship> = db
|
||||
.client
|
||||
.query("SELECT * FROM type::table($table) WHERE metadata.user_id = $user_id")
|
||||
.bind(("table", "relates_to"))
|
||||
.bind(("user_id", user_id.to_owned()))
|
||||
.await?
|
||||
.take(0)?;
|
||||
|
||||
Ok(relationships)
|
||||
}
|
||||
|
||||
pub async fn get_latest_text_contents(
|
||||
user_id: &str,
|
||||
db: &SurrealDbClient,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<div class="p-4 shadow mt-4 rounded-box">
|
||||
<div class="flex gap-4">
|
||||
<button class="btn btn-primary" hx-get="/ingress-form" hx-swap="outerHTML">Add Content</button>
|
||||
<a class="btn btn-secondary" href="/knowledge" hx-boost="true">View Knowledge</a>
|
||||
</div>
|
||||
</div>
|
||||
20
templates/knowledge/base.html
Normal file
20
templates/knowledge/base.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{% extends 'body_base.html' %}
|
||||
{% block main %}
|
||||
<main class="flex justify-center grow mt-2 sm:mt-4 gap-6">
|
||||
<div class="container">
|
||||
<div class="p-4">
|
||||
|
||||
<h2>Entities</h2>
|
||||
|
||||
{% for entity in entities %}
|
||||
<p>{{entity.description}} /p>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<h2 class="mt-10">Relationships</h2>
|
||||
<p>{{relationships}}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user