feat: implement interactive Knowledge Graph visualization with AI-driven content analysis and handbook synchronization tools

This commit is contained in:
RaymondVerhoef
2026-05-18 21:30:05 +02:00
parent f68d76e3d2
commit d71caa41f6
2 changed files with 83 additions and 5 deletions

View File

@@ -24,8 +24,48 @@ ALWAYS return a valid JSON object in the following format:
}
]
}
}
Return JSON only. No markdown blocks or other text.`;
const HANDBOOK_SYSTEM_PROMPT = `You are analyzing an update to the Respellion Employee Handbook.
Your task is to identify changes and extract structural knowledge.
CRITICAL INSTRUCTION:
You must explicitly identify and create relations between Roles, Processes, and Concepts.
Every Process must have a Role attached (who does it).
Every Concept must have a relation to a Process or Role.
Return a JSON object:
{
"topics": [
{ "id": "...", "label": "...", "type": "role | process | concept", "description": "...", "metadata": { "source": "github_handbook" } }
],
"relations": [
{ "source": "role-id", "target": "process-id", "type": "executes | related_to | depends_on | part_of", "description": "Brief metadata about this specific relation" }
]
}
Return JSON only. No markdown blocks or other text.`;
export async function analyzeHandbookDelta(fileContent, filePath) {
try {
const responseText = await anthropicApi.generateContent(HANDBOOK_SYSTEM_PROMPT, `Analyze the following handbook file update (${filePath}):\n\n${fileContent}`);
let extractedData;
try {
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
const jsonStr = jsonMatch ? jsonMatch[0] : responseText;
extractedData = JSON.parse(jsonStr);
} catch (e) {
console.error('[Pipeline] AI returned non-JSON response for handbook delta:', responseText?.substring(0, 500));
throw new Error(`AI response was not valid JSON. The model responded with: "${responseText?.substring(0, 120)}..."`);
}
await mergeKnowledgeGraph(extractedData);
return { success: true, data: extractedData };
} catch (error) {
throw error;
}
}
export async function processSourceText(textContent, sourceName) {
// Deduplicate: skip if a source with the same name was already successfully processed
const existing = await db.getSources();
@@ -71,7 +111,16 @@ async function mergeKnowledgeGraph(newData) {
if (newData.topics && Array.isArray(newData.topics)) {
for (const t of newData.topics) {
if (!topicsMap.has(t.id)) {
if (topicsMap.has(t.id)) {
// Upsert: merge new data into existing topic
const existing = topicsMap.get(t.id);
topicsMap.set(t.id, {
...existing,
...t,
// Keep existing description if new one is empty, or combine them if needed. Here we prefer the new one.
description: t.description || existing.description
});
} else {
topicsMap.set(t.id, t);
}
}