feat(agent): v5.11.0 — auto-title per request, history hover preview, write_blocks normalization, Notion-style steps accordion
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user