kyp-mem 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/kyp_mem/ui.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """KYP-MEM web UI — interactive interface for browsing the vault."""
2
2
 
3
+ import json
3
4
  import webbrowser
4
5
  from datetime import datetime
5
6
  from pathlib import Path
@@ -28,6 +29,29 @@ def create_app(vault_path: str = None) -> FastAPI:
28
29
  def stats():
29
30
  return JSONResponse(vault.get_stats())
30
31
 
32
+ @app.get("/api/graph")
33
+ def graph():
34
+ nodes = []
35
+ edges = []
36
+ seen_edges = set()
37
+ for path, note in vault.index.notes.items():
38
+ kind = "session" if "/Sessions/" in path else "note"
39
+ nodes.append({"id": path, "title": note.title, "kind": kind, "tags": note.tags})
40
+ for link in (note.links or []):
41
+ target = None
42
+ link_lower = link.lower()
43
+ for p in vault.index.notes:
44
+ stem = p.split("/")[-1].replace(".md", "").lower()
45
+ if stem == link_lower:
46
+ target = p
47
+ break
48
+ if target and target != path:
49
+ key = tuple(sorted([path, target]))
50
+ if key not in seen_edges:
51
+ seen_edges.add(key)
52
+ edges.append({"source": path, "target": target})
53
+ return JSONResponse({"nodes": nodes, "edges": edges})
54
+
31
55
  @app.get("/api/note/{path:path}")
32
56
  def read_note(path: str):
33
57
  note = vault.read(path)
@@ -177,12 +201,22 @@ def create_app(vault_path: str = None) -> FastAPI:
177
201
  continue
178
202
  if proj not in sessions:
179
203
  sessions[proj] = []
204
+ summary = ""
205
+ lines = (note.content or "").split("\n")
206
+ for i, line in enumerate(lines):
207
+ if line.strip() == "## Summary":
208
+ for j in range(i + 1, len(lines)):
209
+ if lines[j].strip() and not lines[j].startswith("#"):
210
+ summary = lines[j].strip()
211
+ break
212
+ break
180
213
  sessions[proj].append({
181
214
  "path": path,
182
215
  "title": note.title,
183
216
  "tags": note.tags,
184
217
  "created": note.created,
185
218
  "updated": note.updated,
219
+ "summary": summary,
186
220
  })
187
221
  for proj in sessions:
188
222
  sessions[proj].sort(key=lambda s: s["path"], reverse=True)
@@ -230,6 +264,62 @@ def create_app(vault_path: str = None) -> FastAPI:
230
264
  vault.write_note(path, content, tags, {})
231
265
  return JSONResponse({"ok": True, "path": path})
232
266
 
267
+ @app.get("/api/token-economics")
268
+ def token_economics(project: str = ""):
269
+ from .config import STATS_FILE
270
+ try:
271
+ raw = json.loads(STATS_FILE.read_text()) if STATS_FILE.exists() else {}
272
+ except (json.JSONDecodeError, OSError):
273
+ raw = {}
274
+
275
+ sessions = raw.get("sessions", [])
276
+ injections = raw.get("injections", [])
277
+
278
+ if project:
279
+ sessions = [s for s in sessions if s.get("project") == project]
280
+ injections = [i for i in injections if i.get("project") == project]
281
+
282
+ total_exploration = sum(s.get("exploration_tokens", 0) for s in sessions)
283
+ total_files_read = sum(s.get("files_read", 0) for s in sessions)
284
+ total_commands = sum(s.get("commands_run", 0) for s in sessions)
285
+ total_files_read_chars = sum(s.get("files_read_chars", 0) for s in sessions)
286
+ total_commands_chars = sum(s.get("commands_chars", 0) for s in sessions)
287
+
288
+ avg_injection_tokens = 0
289
+ latest_injection_tokens = 0
290
+ if injections:
291
+ avg_injection_tokens = sum(i.get("tokens", 0) for i in injections) // len(injections)
292
+ latest_injection_tokens = injections[-1].get("tokens", 0)
293
+
294
+ # Avg exploration per session = what a cold start costs
295
+ avg_exploration = total_exploration // len(sessions) if sessions else 0
296
+
297
+ # Compression ratio: how much memory compresses one session's worth
298
+ # of exploration into an injection. Lower = better compression.
299
+ # e.g. 10x means injection is 10x smaller than avg session exploration
300
+ compression_ratio = round(avg_exploration / latest_injection_tokens, 1) if latest_injection_tokens > 0 and avg_exploration > 0 else 0
301
+
302
+ # Per-session savings: injection replaces one cold-start exploration
303
+ per_session_savings_pct = 0
304
+ if avg_exploration > 0 and latest_injection_tokens > 0:
305
+ per_session_savings_pct = round((1 - latest_injection_tokens / avg_exploration) * 100, 1)
306
+
307
+ return JSONResponse({
308
+ "session_count": len(sessions),
309
+ "total_exploration_tokens": total_exploration,
310
+ "avg_exploration_per_session": avg_exploration,
311
+ "total_files_read": total_files_read,
312
+ "total_files_read_chars": total_files_read_chars,
313
+ "total_commands": total_commands,
314
+ "total_commands_chars": total_commands_chars,
315
+ "injection_count": len(injections),
316
+ "avg_injection_tokens": avg_injection_tokens,
317
+ "latest_injection_tokens": latest_injection_tokens,
318
+ "compression_ratio": compression_ratio,
319
+ "per_session_savings_pct": max(per_session_savings_pct, 0),
320
+ "sessions": sessions[-20:],
321
+ })
322
+
233
323
  @app.post("/api/reload")
234
324
  def reload():
235
325
  vault._load_all()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyp-mem",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "Know Your Project — Persistent & Session level knowledge base for AI agents. MCP-powered with wikilinks, backlinks, auto-learning, and neon web UI.",
5
5
  "bin": {
6
6
  "kyp-mem": "bin/cli.mjs"
package/pyproject.toml CHANGED
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "fastapi>=0.100.0",
32
32
  "uvicorn>=0.20.0",
33
33
  "chromadb>=0.4.0",
34
+ "anthropic>=0.40.0",
34
35
  ]
35
36
 
36
37
  [project.urls]