feat: prompt sections + memory route via CRM; legacy Notes migration

Three changes paired with SmartAssistant 2.10.0:

1. prompt_builder.py — every prompt section (tool_rules, writing_style,
   legal_assistance, personality_case, personality_office, other_rules_case,
   other_rules_office) now reads from context['assistantPrompts'][key]
   with the existing string as a fallback default. Admins editing
   AssistantPrompt rows in the EspoCRM UI immediately affect the next
   conversation. A missing/inactive row falls back to the in-code default
   so deleting everything in the UI never bricks Shira.

   Also: the user-profile section now prefers context['userProfile']
   (from the new UserProfile entity) over the explicit `user_profile`
   parameter, completing the move from /opt/data/profiles/*.md to the CRM.

2. crm_tools.py:save_memory — stops POSTing directly to /Note with a
   '🧠 category:' prefix. Routes through SmartAssistant/action/executeTool
   so the PHP saveMemory handler actually creates a CaseMemory row.
   Cause of yesterday's prod issue: CaseMemory table was empty even
   though Shira was "saving" to it.

3. scripts/migrate_legacy_memory_notes.py — one-shot migration that
   sweeps all existing '🧠 category: ...' Notes into CaseMemory rows
   and soft-deletes the originals. Idempotent; safe to re-run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 08:34:14 +00:00
parent 06b27e98a3
commit 72f08e5d15
3 changed files with 242 additions and 41 deletions
+23 -7
View File
@@ -177,16 +177,32 @@ def register_crm_tools(
except Exception as e:
return fail(f"שגיאה במחיקת {label}: {e}")
async def save_memory(category: str, content: str, importance: str = "normal") -> str:
async def save_memory(
category: str,
content: str,
importance: str = "normal",
name: str | None = None,
) -> str:
if not case_id:
return fail("צריך להיות בתוך תיק כדי לשמור בזיכרון")
try:
await crm.post("Note", {
"type": "Post",
"post": f"🧠 {category}: {content}",
"parentType": "Case",
"parentId": case_id,
})
result = await crm.post(
"SmartAssistant/action/executeTool",
{
"tool": "save_memory",
"caseId": case_id,
"params": {
"category": category,
"content": content,
"importance": importance,
"name": name or content[:100],
},
},
)
if not result.get("success", True):
return fail(
f"שגיאה בשמירת זיכרון: {result.get('message', 'unknown error')}"
)
return ok(f'נשמר בזיכרון: "{content[:60]}"')
except Exception as e:
return fail(f"שגיאה בשמירת זיכרון: {e}")