feat(agent): v5.11.0 — auto-title per request, history hover preview, write_blocks normalization, Notion-style steps accordion

This commit is contained in:
2026-09-11 14:30:26 -04:00
parent 6fe5d2f723
commit f1dd9d6181
7 changed files with 507 additions and 29 deletions
+72
View File
@@ -207,6 +207,78 @@ def test_tool_workspace_document_crud_and_search(client):
assert "introuvable" in bad.message
def test_write_blocks_normalizes_llm_blocks(client):
"""LLM blocks are normalized: `text`→`content`, `heading`→`heading_1`,
missing ids are generated, unknown types fall back to paragraph."""
from app.services.tool_registry import ToolRegistry
reg = ToolRegistry()
doc = asyncio.run(reg.execute("create_document", {"title": "Script"}))
pid = doc.data["document_id"]
bad_blocks = [
{"type": "heading", "text": "test.ps1 — Script de test PowerShell"},
{"type": "paragraph", "text": "# Test script\nWrite-Host 'Hello, World!'"},
{"type": "code", "text": "Write-Host 'hi'", "language": "powershell"},
{"type": "made_up_type", "content": "inconnu"},
]
res = asyncio.run(reg.execute("write_blocks", {"page_id": pid, "blocks": bad_blocks}))
assert res.status == "success", res.message
from app.db import get_conn
with get_conn() as conn:
row = conn.execute("SELECT content, content_format FROM pages WHERE id=?", (pid,)).fetchone()
assert row["content_format"] == "blocks"
stored = json.loads(row["content"])
assert stored[0]["type"] == "heading_1", stored[0]
assert stored[0]["content"] == "test.ps1 — Script de test PowerShell"
assert stored[0]["id"], "id must be generated"
assert stored[1]["type"] == "paragraph"
assert stored[1]["content"] == "# Test script\nWrite-Host 'Hello, World!'"
assert stored[2]["type"] == "code"
assert stored[2]["content"] == "Write-Host 'hi'"
assert stored[2]["language"] == "powershell"
assert stored[3]["type"] == "paragraph" # unknown → paragraph
assert stored[3]["content"] == "inconnu"
def test_engine_titles_conversation_on_each_request(client):
"""The conversation title is refreshed after EVERY AI request (not only
while the title is a default placeholder)."""
from app.services.agent_engine import AgentEngine
engine = AgentEngine(_admin_id(), workspace_id=None)
conv = client.post("/api/agent/conversations",
json={"title": "Nouvelle conversation"}).json()["id"]
first = "crée un document « Rapport Q3 »"
asyncio.run(_run_engine(engine, conv, first))
detail = client.get(f"/api/agent/conversations/{conv}").json()
title1 = detail["conversation"]["title"]
assert "Rapport Q3" in title1
second = "crée une collection « Launches »"
asyncio.run(_run_engine(engine, conv, second))
detail = client.get(f"/api/agent/conversations/{conv}").json()
title2 = detail["conversation"]["title"]
assert "Launches" in title2, title2
assert title2 != title1, "title must be updated on the second request too"
def test_suggest_title_strips_composer_prefixes(client):
from app.services.agent_engine import AgentEngine
title = AgentEngine._suggest_title(
"Contexte « test.ps1 »\n\nÉcrire un script de test en powershell dans la page courante",
None,
)
assert title != "Contexte", title
assert "Contexte" not in title
assert title
def test_tool_delete_document_and_undo(client):
from app.db import get_conn
from app.services.agent_engine import undo_action