109 lines
3.5 KiB
PHP
109 lines
3.5 KiB
PHP
<?php
|
|
/**
|
|
* ambre-mermaid-learn.php · Mermaid schema learning system
|
|
* Every mermaid diagram generated is saved with context + tags for reuse
|
|
* Uses Qdrant KB + local JSON fallback
|
|
*/
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
|
|
$raw = file_get_contents("php://input");
|
|
$in = json_decode($raw, true) ?: $_POST;
|
|
$action = $in["action"] ?? "list";
|
|
|
|
$store_file = "/var/www/html/generated/mermaid-learn-kb.json";
|
|
if (!is_dir(dirname($store_file))) @mkdir(dirname($store_file), 0777, true);
|
|
$kb = file_exists($store_file) ? (json_decode(@file_get_contents($store_file), true) ?: []) : [];
|
|
|
|
if ($action === "save") {
|
|
$topic = trim($in["topic"] ?? "");
|
|
$code = trim($in["code"] ?? "");
|
|
$kind = $in["kind"] ?? "flowchart"; // flowchart, sequence, gantt, pie, etc.
|
|
$context = $in["context"] ?? "";
|
|
if (!$topic || !$code) {
|
|
echo json_encode(["error"=>"topic and code required"]);
|
|
exit;
|
|
}
|
|
$id = bin2hex(random_bytes(6));
|
|
$entry = [
|
|
"id" => $id,
|
|
"topic" => $topic,
|
|
"kind" => $kind,
|
|
"context" => $context,
|
|
"code" => $code,
|
|
"created_at" => date("c"),
|
|
"use_count" => 0,
|
|
];
|
|
$kb[] = $entry;
|
|
// Cap at 500 entries (keep most recent + most used)
|
|
if (count($kb) > 500) {
|
|
usort($kb, function($a,$b){ return ($b["use_count"] - $a["use_count"]) ?: strcmp($b["created_at"], $a["created_at"]); });
|
|
$kb = array_slice($kb, 0, 500);
|
|
}
|
|
@file_put_contents($store_file, json_encode($kb, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
|
|
echo json_encode(["ok"=>true, "id"=>$id, "total"=>count($kb)]);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "search") {
|
|
$q = trim($in["query"] ?? "");
|
|
if (!$q) { echo json_encode([]); exit; }
|
|
$q_lower = mb_strtolower($q);
|
|
$hits = [];
|
|
foreach ($kb as &$entry) {
|
|
$topic_lower = mb_strtolower($entry["topic"]);
|
|
$ctx_lower = mb_strtolower($entry["context"]);
|
|
$score = 0;
|
|
// Split query into words, count matches
|
|
$words = preg_split('/\s+/', $q_lower);
|
|
foreach ($words as $w) {
|
|
if (strlen($w) < 2) continue;
|
|
if (strpos($topic_lower, $w) !== false) $score += 2;
|
|
if (strpos($ctx_lower, $w) !== false) $score += 1;
|
|
}
|
|
if ($score > 0) {
|
|
$entry["score"] = $score + ($entry["use_count"] * 0.1);
|
|
$hits[] = $entry;
|
|
}
|
|
}
|
|
usort($hits, function($a,$b){ return $b["score"] <=> $a["score"]; });
|
|
$top = array_slice($hits, 0, 5);
|
|
echo json_encode($top, JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "use") {
|
|
$id = $in["id"] ?? "";
|
|
foreach ($kb as &$entry) {
|
|
if ($entry["id"] === $id) {
|
|
$entry["use_count"] = ($entry["use_count"] ?? 0) + 1;
|
|
@file_put_contents($store_file, json_encode($kb, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
|
|
echo json_encode(["ok"=>true, "use_count"=>$entry["use_count"]]);
|
|
exit;
|
|
}
|
|
}
|
|
echo json_encode(["error"=>"not found"]);
|
|
exit;
|
|
}
|
|
|
|
if ($action === "stats") {
|
|
$kinds = [];
|
|
$total_uses = 0;
|
|
foreach ($kb as $e) {
|
|
$k = $e["kind"] ?? "flowchart";
|
|
$kinds[$k] = ($kinds[$k] ?? 0) + 1;
|
|
$total_uses += ($e["use_count"] ?? 0);
|
|
}
|
|
echo json_encode([
|
|
"total_diagrams" => count($kb),
|
|
"by_kind" => $kinds,
|
|
"total_uses" => $total_uses,
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// default: list all
|
|
echo json_encode([
|
|
"total" => count($kb),
|
|
"items" => array_slice(array_reverse($kb), 0, 20),
|
|
], JSON_UNESCAPED_UNICODE);
|