repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources15d ago| #1 | """ |
| #2 | Mnemosyne Plugin Tools for Hermes |
| #3 | |
| #4 | Tool implementations that wrap Mnemosyne core functionality. |
| #5 | """ |
| #6 | |
| #7 | import json |
| #8 | |
| #9 | from hermes_plugin import _get_memory, _get_triples |
| #10 | |
| #11 | # Tool Schemas (for Hermes tool registration) |
| #12 | REMEMBER_SCHEMA = { |
| #13 | "name": "mnemosyne_remember", |
| #14 | "description": "Store a memory in Mnemosyne local database. Use for important facts, preferences, or context to remember later.", |
| #15 | "parameters": { |
| #16 | "type": "object", |
| #17 | "properties": { |
| #18 | "content": { |
| #19 | "type": "string", |
| #20 | "description": "The information to remember" |
| #21 | }, |
| #22 | "importance": { |
| #23 | "type": "number", |
| #24 | "description": "Importance from 0.0 to 1.0 (0.9+ for critical facts)" |
| #25 | }, |
| #26 | "source": { |
| #27 | "type": "string", |
| #28 | "description": "Source of the memory (preference, fact, conversation, etc.)" |
| #29 | }, |
| #30 | "valid_until": { |
| #31 | "type": "string", |
| #32 | "description": "ISO timestamp when this memory expires (optional)" |
| #33 | }, |
| #34 | "scope": { |
| #35 | "type": "string", |
| #36 | "description": "'session' (default) or 'global' to make visible across all sessions", |
| #37 | "enum": ["session", "global"] |
| #38 | }, |
| #39 | "extract_entities": { |
| #40 | "type": "boolean", |
| #41 | "description": "If true, extract named entities from content and link them for fuzzy recall (e.g. 'Abdias' and 'Abdias J.' will match)", |
| #42 | "default": False |
| #43 | }, |
| #44 | "extract": { |
| #45 | "type": "boolean", |
| #46 | "description": "If true, extract structured facts from content using LLM and store as triples for fact-aware recall", |
| #47 | "default": False |
| #48 | }, |
| #49 | "author_id": { |
| #50 | "type": "string", |
| #51 | "description": "Who stored this memory (e.g., 'abdias'). Auto-set from session if not provided." |
| #52 | }, |
| #53 | "author_type": { |
| #54 | "type": "string", |
| #55 | "description": "Type: 'human', 'agent', or 'system'." |
| #56 | }, |
| #57 | "channel_id": { |
| #58 | "type": "string", |
| #59 | "description": "Channel/group this belongs to (e.g., 'fluxspeak-team')." |
| #60 | } |
| #61 | }, |
| #62 | "required": ["content"] |
| #63 | } |
| #64 | } |
| #65 | |
| #66 | RECALL_SCHEMA = { |
| #67 | "name": "mnemosyne_recall", |
| #68 | "description": "Search memories in Mnemosyne. Uses hybrid vector + full-text search across working and episodic memory. Supports temporal weighting to boost recent memories and per-query scoring weight overrides.", |
| #69 | "parameters": { |
| #70 | "type": "object", |
| #71 | "properties": { |
| #72 | "query": { |
| #73 | "type": "string", |
| #74 | "description": "What to search for" |
| #75 | }, |
| #76 | "top_k": { |
| #77 | "type": "integer", |
| #78 | "description": "Number of results to return", |
| #79 | "default": 5 |
| #80 | }, |
| #81 | "temporal_weight": { |
| #82 | "type": "number", |
| #83 | "description": "How much to boost recent memories (0.0 = ignore time, 0.2 = mild recency bias, 0.5 = strong recency bias). Default 0.0 for backward compatibility.", |
| #84 | "default": 0.0 |
| #85 | }, |
| #86 | "query_time": { |
| #87 | "type": "string", |
| #88 | "description": "ISO timestamp to treat as 'now' for temporal scoring (e.g., '2026-04-28T12:00:00'). Default is current time.", |
| #89 | "default": None |
| #90 | }, |
| #91 | "temporal_halflife": { |
| #92 | "type": "number", |
| #93 | "description": "Hours until temporal boost decays by half. Default 24. Lower = faster decay.", |
| #94 | "default": 24, |
| #95 | }, |
| #96 | "vec_weight": { |
| #97 | "type": "number", |
| #98 | "description": "Vector similarity weight in hybrid scoring. Omit (or pass null) to use MNEMOSYNE_VEC_WEIGHT env var or built-in default 0.5." |
| #99 | }, |
| #100 | "fts_weight": { |
| #101 | "type": "number", |
| #102 | "description": "Full-text search weight in hybrid scoring. Omit (or pass null) to use MNEMOSYNE_FTS_WEIGHT env var or built-in default 0.3." |
| #103 | }, |
| #104 | "importance_weight": { |
| #105 | "type": "number", |
| #106 | "description": "Importance score weight in hybrid scoring. Omit (or pass null) to use MNEMOSYNE_IMPORTANCE_WEIGHT env var or built-in default 0.2." |
| #107 | }, |
| #108 | "author_id": { |
| #109 | "type": "string", |
| #110 | "description": "Filter by author (e.g., 'abdias'). Only recalls memories by this author." |
| #111 | }, |
| #112 | "author_type": { |
| #113 | "type": "string", |
| #114 | "description": "Filter by author type: 'human', 'agent', or 'system'." |
| #115 | }, |
| #116 | "channel_id": { |
| #117 | "type": "string", |
| #118 | "description": "Filter by channel/group (e.g., 'fluxspeak-team')." |
| #119 | } |
| #120 | }, |
| #121 | "required": ["query"], |
| #122 | }, |
| #123 | } |
| #124 | |
| #125 | STATS_SCHEMA = { |
| #126 | "name": "mnemosyne_stats", |
| #127 | "description": "Get Mnemosyne memory statistics including BEAM tiers", |
| #128 | "parameters": { |
| #129 | "type": "object", |
| #130 | "properties": {} |
| #131 | } |
| #132 | } |
| #133 | |
| #134 | TRIPLE_ADD_SCHEMA = { |
| #135 | "name": "mnemosyne_triple_add", |
| #136 | "description": "Add a temporal triple to the knowledge graph. Example: (Maya, assigned_to, auth-migration, valid_from=2026-01-15)", |
| #137 | "parameters": { |
| #138 | "type": "object", |
| #139 | "properties": { |
| #140 | "subject": {"type": "string", "description": "Entity the fact is about"}, |
| #141 | "predicate": {"type": "string", "description": "Relationship or property"}, |
| #142 | "object": {"type": "string", "description": "Value or target entity"}, |
| #143 | "valid_from": {"type": "string", "description": "Date when fact became true (YYYY-MM-DD)"}, |
| #144 | "source": {"type": "string", "description": "Origin of the fact"}, |
| #145 | "confidence": {"type": "number", "description": "Confidence from 0.0 to 1.0"} |
| #146 | }, |
| #147 | "required": ["subject", "predicate", "object"] |
| #148 | } |
| #149 | } |
| #150 | |
| #151 | TRIPLE_QUERY_SCHEMA = { |
| #152 | "name": "mnemosyne_triple_query", |
| #153 | "description": "Query temporal triples. Use as_of to ask what was true at a specific date.", |
| #154 | "parameters": { |
| #155 | "type": "object", |
| #156 | "properties": { |
| #157 | "subject": {"type": "string"}, |
| #158 | "predicate": {"type": "string"}, |
| #159 | "object": {"type": "string"}, |
| #160 | "as_of": {"type": "string", "description": "Date to query historical truth (YYYY-MM-DD)"} |
| #161 | } |
| #162 | } |
| #163 | } |
| #164 | |
| #165 | SLEEP_SCHEMA = { |
| #166 | "name": "mnemosyne_sleep", |
| #167 | "description": "Run the Mnemosyne sleep/consolidation cycle. Old working memories are summarized and moved to episodic memory. Set all_sessions=true to include inactive sessions.", |
| #168 | "parameters": { |
| #169 | "type": "object", |
| #170 | "properties": { |
| #171 | "dry_run": { |
| #172 | "type": "boolean", |
| #173 | "description": "If true, preview what would be consolidated without making changes", |
| #174 | "default": False |
| #175 | }, |
| #176 | "all_sessions": { |
| #177 | "type": "boolean", |
| #178 | "description": "If true, consolidate eligible old working memories across all sessions instead of only the current session", |
| #179 | "default": False |
| #180 | } |
| #181 | } |
| #182 | } |
| #183 | } |
| #184 | |
| #185 | INVALIDATE_SCHEMA = { |
| #186 | "name": "mnemosyne_invalidate", |
| #187 | "description": "Mark a memory as expired or superseded. Use when a fact is no longer true or has been replaced.", |
| #188 | "parameters": { |
| #189 | "type": "object", |
| #190 | "properties": { |
| #191 | "memory_id": { |
| #192 | "type": "string", |
| #193 | "description": "ID of the memory to invalidate" |
| #194 | }, |
| #195 | "replacement_id": { |
| #196 | "type": "string", |
| #197 | "description": "Optional ID of the memory that replaces this one" |
| #198 | } |
| #199 | }, |
| #200 | "required": ["memory_id"] |
| #201 | } |
| #202 | } |
| #203 | |
| #204 | SCRATCHPAD_WRITE_SCHEMA = { |
| #205 | "name": "mnemosyne_scratchpad_write", |
| #206 | "description": "Write a temporary note to the Mnemosyne scratchpad.", |
| #207 | "parameters": { |
| #208 | "type": "object", |
| #209 | "properties": { |
| #210 | "content": { |
| #211 | "type": "string", |
| #212 | "description": "Content to write" |
| #213 | } |
| #214 | }, |
| #215 | "required": ["content"] |
| #216 | } |
| #217 | } |
| #218 | |
| #219 | SCRATCHPAD_READ_SCHEMA = { |
| #220 | "name": "mnemosyne_scratchpad_read", |
| #221 | "description": "Read the Mnemosyne scratchpad entries.", |
| #222 | "parameters": { |
| #223 | "type": "object", |
| #224 | "properties": {} |
| #225 | } |
| #226 | } |
| #227 | |
| #228 | SCRATCHPAD_CLEAR_SCHEMA = { |
| #229 | "name": "mnemosyne_scratchpad_clear", |
| #230 | "description": "Clear all entries from the Mnemosyne scratchpad.", |
| #231 | "parameters": { |
| #232 | "type": "object", |
| #233 | "properties": {} |
| #234 | } |
| #235 | } |
| #236 | |
| #237 | EXPORT_SCHEMA = { |
| #238 | "name": "mnemosyne_export", |
| #239 | "description": "Export all Mnemosyne memories to a JSON file for backup or migration to another machine.", |
| #240 | "parameters": { |
| #241 | "type": "object", |
| #242 | "properties": { |
| #243 | "output_path": { |
| #244 | "type": "string", |
| #245 | "description": "File path to write the export JSON (e.g., /tmp/mnemosyne_backup.json)" |
| #246 | } |
| #247 | }, |
| #248 | "required": ["output_path"] |
| #249 | } |
| #250 | } |
| #251 | |
| #252 | UPDATE_SCHEMA = { |
| #253 | "name": "mnemosyne_update", |
| #254 | "description": "Update the content or importance of an existing memory by ID.", |
| #255 | "parameters": { |
| #256 | "type": "object", |
| #257 | "properties": { |
| #258 | "memory_id": { |
| #259 | "type": "string", |
| #260 | "description": "ID of the memory to update" |
| #261 | }, |
| #262 | "content": { |
| #263 | "type": "string", |
| #264 | "description": "New content for the memory (optional)" |
| #265 | }, |
| #266 | "importance": { |
| #267 | "type": "number", |
| #268 | "description": "New importance from 0.0 to 1.0 (optional)" |
| #269 | } |
| #270 | }, |
| #271 | "required": ["memory_id"] |
| #272 | } |
| #273 | } |
| #274 | |
| #275 | FORGET_SCHEMA = { |
| #276 | "name": "mnemosyne_forget", |
| #277 | "description": "Permanently delete a memory by ID from working and legacy memory.", |
| #278 | "parameters": { |
| #279 | "type": "object", |
| #280 | "properties": { |
| #281 | "memory_id": { |
| #282 | "type": "string", |
| #283 | "description": "ID of the memory to delete" |
| #284 | } |
| #285 | }, |
| #286 | "required": ["memory_id"] |
| #287 | } |
| #288 | } |
| #289 | |
| #290 | IMPORT_SCHEMA = { |
| #291 | "name": "mnemosyne_import", |
| #292 | "description": "Import Mnemosyne memories from a JSON file or another memory provider (Mem0, etc.). Idempotent by default.", |
| #293 | "parameters": { |
| #294 | "type": "object", |
| #295 | "properties": { |
| #296 | "input_path": { |
| #297 | "type": "string", |
| #298 | "description": "File path to read the export JSON from (for file imports)" |
| #299 | }, |
| #300 | "provider": { |
| #301 | "type": "string", |
| #302 | "description": "Provider to import from: 'mem0'. Requires api_key. Use --list-providers to see supported providers." |
| #303 | }, |
| #304 | "api_key": { |
| #305 | "type": "string", |
| #306 | "description": "API key for the source provider (can also be set via env var: MEM0_API_KEY)" |
| #307 | }, |
| #308 | "user_id": { |
| #309 | "type": "string", |
| #310 | "description": "Filter imported memories by user ID (provider-specific)" |
| #311 | }, |
| #312 | "agent_id": { |
| #313 | "type": "string", |
| #314 | "description": "Filter imported memories by agent ID (provider-specific)" |
| #315 | }, |
| #316 | "base_url": { |
| #317 | "type": "string", |
| #318 | "description": "Base URL for self-hosted provider instances" |
| #319 | }, |
| #320 | "dry_run": { |
| #321 | "type": "boolean", |
| #322 | "description": "If true, validate and transform but don't write any memories", |
| #323 | "default": False |
| #324 | }, |
| #325 | "channel_id": { |
| #326 | "type": "string", |
| #327 | "description": "Channel to assign imported memories to" |
| #328 | }, |
| #329 | "force": { |
| #330 | "type": "boolean", |
| #331 | "description": "If true, overwrite existing records instead of skipping", |
| #332 | "default": False |
| #333 | } |
| #334 | }, |
| #335 | "required": [] |
| #336 | } |
| #337 | } |
| #338 | |
| #339 | DIAGNOSE_SCHEMA = { |
| #340 | "name": "mnemosyne_diagnose", |
| #341 | "description": "Run PII-safe diagnostics on Mnemosyne installation. Checks dependencies, database state, and vector search readiness. Writes a JSONL log file that can be shared for troubleshooting. Never includes memory content or API keys.", |
| #342 | "parameters": { |
| #343 | "type": "object", |
| #344 | "properties": {} |
| #345 | } |
| #346 | } |
| #347 | |
| #348 | |
| #349 | # Tool Handlers |
| #350 | def mnemosyne_remember(args: dict, **kwargs) -> str: |
| #351 | """Store a memory""" |
| #352 | try: |
| #353 | content = args.get("content", "").strip() |
| #354 | importance = args.get("importance", 0.5) |
| #355 | source = args.get("source", "conversation") |
| #356 | valid_until = args.get("valid_until") |
| #357 | scope = args.get("scope", "session") |
| #358 | extract_entities = args.get("extract_entities", False) |
| #359 | |
| #360 | if not content: |
| #361 | return json.dumps({"error": "Content is required"}) |
| #362 | |
| #363 | extract = args.get("extract", False) |
| #364 | |
| #365 | mem = _get_memory() |
| #366 | memory_id = mem.remember( |
| #367 | content, source=source, importance=importance, |
| #368 | valid_until=valid_until, scope=scope, |
| #369 | extract_entities=extract_entities, |
| #370 | extract=extract |
| #371 | ) |
| #372 | |
| #373 | return json.dumps({ |
| #374 | "status": "stored", |
| #375 | "id": memory_id, |
| #376 | "scope": scope, |
| #377 | "valid_until": valid_until, |
| #378 | "extract_entities": extract_entities, |
| #379 | "extract": extract, |
| #380 | "content_preview": content[:80] + "..." if len(content) > 80 else content |
| #381 | }) |
| #382 | |
| #383 | except Exception as e: |
| #384 | return json.dumps({"error": str(e)}) |
| #385 | |
| #386 | |
| #387 | def mnemosyne_recall(args: dict, **kwargs) -> str: |
| #388 | """Search memories""" |
| #389 | try: |
| #390 | query = args.get("query", "").strip() |
| #391 | top_k = args.get("top_k", 5) |
| #392 | temporal_weight = args.get("temporal_weight", 0.0) |
| #393 | query_time = args.get("query_time") |
| #394 | temporal_halflife_hours = args.get("temporal_halflife", 24) |
| #395 | |
| #396 | if not query: |
| #397 | return json.dumps({"error": "Query is required"}) |
| #398 | |
| #399 | # Forward configurable scoring weights ONLY when caller supplied them. |
| #400 | # mem.recall treats None as "fall back to env var or default" via |
| #401 | # _normalize_weights; passing 0.0 / 0.5 / etc. when the caller didn't |
| #402 | # ask for tuning would override that resolution and break |
| #403 | # MNEMOSYNE_*_WEIGHT env-var deployments. See issue #45. |
| #404 | recall_kwargs = { |
| #405 | "top_k": top_k, |
| #406 | "temporal_weight": temporal_weight, |
| #407 | "query_time": query_time, |
| #408 | "temporal_halflife": temporal_halflife_hours, |
| #409 | } |
| #410 | for weight_key in ("vec_weight", "fts_weight", "importance_weight"): |
| #411 | if weight_key in args: |
| #412 | recall_kwargs[weight_key] = args[weight_key] |
| #413 | |
| #414 | mem = _get_memory() |
| #415 | results = mem.recall(query, **recall_kwargs) |
| #416 | |
| #417 | return json.dumps({ |
| #418 | "query": query, |
| #419 | "results_count": len(results), |
| #420 | "temporal_weight": temporal_weight, |
| #421 | "query_time": query_time, |
| #422 | "results": results |
| #423 | }) |
| #424 | |
| #425 | except Exception as e: |
| #426 | return json.dumps({"error": str(e)}) |
| #427 | |
| #428 | |
| #429 | def mnemosyne_stats(args: dict, **kwargs) -> str: |
| #430 | """Get memory statistics""" |
| #431 | try: |
| #432 | mem = _get_memory() |
| #433 | stats = mem.get_stats() |
| #434 | |
| #435 | return json.dumps(stats) |
| #436 | |
| #437 | except Exception as e: |
| #438 | return json.dumps({"error": str(e)}) |
| #439 | |
| #440 | |
| #441 | def mnemosyne_triple_add(args: dict, **kwargs) -> str: |
| #442 | """Add a temporal triple""" |
| #443 | try: |
| #444 | kg = _get_triples() |
| #445 | triple_id = kg.add( |
| #446 | subject=args["subject"], |
| #447 | predicate=args["predicate"], |
| #448 | object=args["object"], |
| #449 | valid_from=args.get("valid_from"), |
| #450 | source=args.get("source", "conversation"), |
| #451 | confidence=args.get("confidence", 1.0) |
| #452 | ) |
| #453 | return json.dumps({"status": "added", "triple_id": triple_id}) |
| #454 | except Exception as e: |
| #455 | return json.dumps({"error": str(e)}) |
| #456 | |
| #457 | |
| #458 | def mnemosyne_triple_query(args: dict, **kwargs) -> str: |
| #459 | """Query temporal triples""" |
| #460 | try: |
| #461 | kg = _get_triples() |
| #462 | results = kg.query( |
| #463 | subject=args.get("subject"), |
| #464 | predicate=args.get("predicate"), |
| #465 | object=args.get("object"), |
| #466 | as_of=args.get("as_of") |
| #467 | ) |
| #468 | return json.dumps({"results_count": len(results), "results": results}) |
| #469 | except Exception as e: |
| #470 | return json.dumps({"error": str(e)}) |
| #471 | |
| #472 | |
| #473 | def mnemosyne_sleep(args: dict, **kwargs) -> str: |
| #474 | """Run consolidation sleep cycle""" |
| #475 | try: |
| #476 | dry_run = args.get("dry_run", False) |
| #477 | all_sessions = args.get("all_sessions", False) |
| #478 | mem = _get_memory() |
| #479 | if all_sessions and hasattr(mem, "sleep_all_sessions"): |
| #480 | result = mem.sleep_all_sessions(dry_run=dry_run) |
| #481 | else: |
| #482 | result = mem.sleep(dry_run=dry_run) |
| #483 | return json.dumps(result) |
| #484 | except Exception as e: |
| #485 | return json.dumps({"error": str(e)}) |
| #486 | |
| #487 | |
| #488 | def mnemosyne_scratchpad_write(args: dict, **kwargs) -> str: |
| #489 | """Write to scratchpad""" |
| #490 | try: |
| #491 | content = args.get("content", "").strip() |
| #492 | if not content: |
| #493 | return json.dumps({"error": "Content is required"}) |
| #494 | mem = _get_memory() |
| #495 | pad_id = mem.scratchpad_write(content) |
| #496 | return json.dumps({"status": "written", "id": pad_id}) |
| #497 | except Exception as e: |
| #498 | return json.dumps({"error": str(e)}) |
| #499 | |
| #500 | |
| #501 | def mnemosyne_scratchpad_read(args: dict, **kwargs) -> str: |
| #502 | """Read scratchpad""" |
| #503 | try: |
| #504 | mem = _get_memory() |
| #505 | entries = mem.scratchpad_read() |
| #506 | return json.dumps({"entries_count": len(entries), "entries": entries}) |
| #507 | except Exception as e: |
| #508 | return json.dumps({"error": str(e)}) |
| #509 | |
| #510 | |
| #511 | def mnemosyne_scratchpad_clear(args: dict, **kwargs) -> str: |
| #512 | """Clear scratchpad""" |
| #513 | try: |
| #514 | mem = _get_memory() |
| #515 | mem.scratchpad_clear() |
| #516 | return json.dumps({"status": "cleared"}) |
| #517 | except Exception as e: |
| #518 | return json.dumps({"error": str(e)}) |
| #519 | |
| #520 | |
| #521 | def mnemosyne_invalidate(args: dict, **kwargs) -> str: |
| #522 | """Invalidate a memory""" |
| #523 | try: |
| #524 | memory_id = args.get("memory_id", "").strip() |
| #525 | replacement_id = args.get("replacement_id") |
| #526 | if not memory_id: |
| #527 | return json.dumps({"error": "memory_id is required"}) |
| #528 | |
| #529 | mem = _get_memory() |
| #530 | ok = mem.invalidate(memory_id, replacement_id=replacement_id) |
| #531 | return json.dumps({ |
| #532 | "status": "invalidated" if ok else "not_found", |
| #533 | "memory_id": memory_id, |
| #534 | "replacement_id": replacement_id |
| #535 | }) |
| #536 | except Exception as e: |
| #537 | return json.dumps({"error": str(e)}) |
| #538 | |
| #539 | |
| #540 | def mnemosyne_export(args: dict, **kwargs) -> str: |
| #541 | """Export all memories to a JSON file""" |
| #542 | try: |
| #543 | output_path = args.get("output_path", "").strip() |
| #544 | if not output_path: |
| #545 | return json.dumps({"error": "output_path is required"}) |
| #546 | |
| #547 | mem = _get_memory() |
| #548 | result = mem.export_to_file(output_path) |
| #549 | return json.dumps(result) |
| #550 | except Exception as e: |
| #551 | return json.dumps({"error": str(e)}) |
| #552 | |
| #553 | |
| #554 | def mnemosyne_update(args: dict, **kwargs) -> str: |
| #555 | """Update an existing memory by ID""" |
| #556 | try: |
| #557 | memory_id = args.get("memory_id", "").strip() |
| #558 | if not memory_id: |
| #559 | return json.dumps({"error": "memory_id is required"}) |
| #560 | |
| #561 | content = args.get("content") |
| #562 | importance = args.get("importance") |
| #563 | |
| #564 | mem = _get_memory() |
| #565 | ok = mem.update(memory_id, content=content, importance=importance) |
| #566 | return json.dumps({ |
| #567 | "status": "updated" if ok else "not_found", |
| #568 | "memory_id": memory_id |
| #569 | }) |
| #570 | except Exception as e: |
| #571 | return json.dumps({"error": str(e)}) |
| #572 | |
| #573 | |
| #574 | def mnemosyne_forget(args: dict, **kwargs) -> str: |
| #575 | """Permanently delete a memory by ID""" |
| #576 | try: |
| #577 | memory_id = args.get("memory_id", "").strip() |
| #578 | if not memory_id: |
| #579 | return json.dumps({"error": "memory_id is required"}) |
| #580 | |
| #581 | mem = _get_memory() |
| #582 | ok = mem.forget(memory_id) |
| #583 | return json.dumps({ |
| #584 | "status": "deleted" if ok else "not_found", |
| #585 | "memory_id": memory_id |
| #586 | }) |
| #587 | except Exception as e: |
| #588 | return json.dumps({"error": str(e)}) |
| #589 | |
| #590 | |
| #591 | def mnemosyne_import(args: dict, **kwargs) -> str: |
| #592 | """Import memories from a JSON file or another memory provider.""" |
| #593 | try: |
| #594 | provider = args.get("provider", "").strip().lower() |
| #595 | input_path = args.get("input_path", "").strip() |
| #596 | dry_run = args.get("dry_run", False) |
| #597 | channel_id = args.get("channel_id") |
| #598 | force = args.get("force", False) |
| #599 | |
| #600 | mem = _get_memory() |
| #601 | |
| #602 | # Cross-provider import |
| #603 | if provider: |
| #604 | api_key = args.get("api_key", "").strip() |
| #605 | user_id = args.get("user_id", "").strip() or None |
| #606 | agent_id = args.get("agent_id", "").strip() or None |
| #607 | base_url = args.get("base_url", "").strip() or None |
| #608 | |
| #609 | if not api_key: |
| #610 | import os |
| #611 | env_key = f"{provider.upper()}_API_KEY" |
| #612 | api_key = os.environ.get(env_key, "") |
| #613 | if not api_key: |
| #614 | return json.dumps({ |
| #615 | "error": f"api_key required for {provider} import. Set {provider.upper()}_API_KEY env var or pass api_key parameter." |
| #616 | }) |
| #617 | |
| #618 | from mnemosyne.core.importers import import_from_provider |
| #619 | result = import_from_provider( |
| #620 | provider, mem, |
| #621 | api_key=api_key, |
| #622 | user_id=user_id, |
| #623 | agent_id=agent_id, |
| #624 | base_url=base_url, |
| #625 | dry_run=dry_run, |
| #626 | channel_id=channel_id, |
| #627 | ) |
| #628 | return json.dumps(result.to_dict()) |
| #629 | |
| #630 | # File import |
| #631 | if not input_path: |
| #632 | return json.dumps({ |
| #633 | "error": "Either input_path (for file import) or provider (for cross-provider import) is required" |
| #634 | }) |
| #635 | |
| #636 | stats = mem.import_from_file(input_path, force=force) |
| #637 | return json.dumps({ |
| #638 | "status": "imported", |
| #639 | "stats": stats |
| #640 | }) |
| #641 | except Exception as e: |
| #642 | return json.dumps({"error": str(e)}) |
| #643 | |
| #644 | |
| #645 | def mnemosyne_diagnose(args: dict, **kwargs) -> str: |
| #646 | """Run PII-safe diagnostics and return summary""" |
| #647 | try: |
| #648 | from mnemosyne.diagnose import run_diagnostics |
| #649 | result = run_diagnostics() |
| #650 | return json.dumps(result, indent=2) |
| #651 | except Exception as e: |
| #652 | return json.dumps({"error": str(e)}) |
| #653 |