feat(agent): v4.15.3 - mentions @ par espace courant + skills composes en arriere-plan
FlowDeck CI / test (push) Failing after 11s
FlowDeck CI / docker (push) Skipped

- /api/agent/mentions : reponse en sections (fichiers de l'espace courant ouvert, collections, pages de collections, autres documents), workspace_id transmis par le frontend (localWsId) et prioritaire
- Navigation clavier up/down : la liste defile pour garder la selection visible (menus @ et /)
- Deduplication des propositions (meme type + meme titre affiches une seule fois)
- Skills : plus d'injection du texte du skill dans la boite d'edition; requete composee en arriere-plan (contexte + skills + texte) a l'envoi; les skills integres injectent leur template, les enregistres partent via skill_ids
- Tests : shape des mentions, dedup, scoping workspace
This commit is contained in:
2026-09-06 13:57:03 -04:00
parent 917a04d543
commit 2391de418d
6 changed files with 248 additions and 74 deletions
+30 -3
View File
@@ -526,17 +526,44 @@ def test_router_mentions(client):
r = client.get("/api/agent/mentions")
assert r.status_code == 200
items = r.json()["mentions"]
tokens = {it["token"] for it in items}
data = r.json()
sections = data["sections"]
assert isinstance(sections, list) and sections
tokens = {it["token"] for grp in sections for it in grp["items"]}
assert f"document:{doc_id}" in tokens
assert f"collection:{coll_id}" in tokens
assert f"page:{page_id}" in tokens
# search filter
r2 = client.get("/api/agent/mentions", params={"q": "Compte rendu"})
found = r2.json()["mentions"]
found = [it for grp in r2.json()["sections"] for it in grp["items"]]
assert found and all(it["type"] == "document" and it["label"] == "Compte rendu projet" for it in found)
# déduplication : deux fichiers avec le même titre n'apparaissent qu'une fois
with get_conn() as conn:
conn.execute(
"INSERT INTO pages (workspace, title, content, content_format) "
"VALUES ('', 'Compte rendu projet', 'Bis', 'markdown')")
conn.commit()
r3 = client.get("/api/agent/mentions")
dup_docs = [it for grp in r3.json()["sections"] for it in grp["items"]
if it["type"] == "document" and it["label"] == "Compte rendu projet"]
assert len(dup_docs) == 1, dup_docs
# scoping : les fichiers de l'espace courant ouvert sont proposés en section dédiée
with get_conn() as conn:
conn.execute("INSERT INTO workspaces (name, owner_id) VALUES ('Espace Scope', ?)", (_admin_id(),))
ws_id = conn.execute("SELECT last_insert_rowid()").fetchone()[0]
conn.execute(
"INSERT INTO pages (workspace, workspace_id, title, content, content_format) "
"VALUES ('Espace Scope', ?, 'Fichier du ws', '', 'markdown')", (ws_id,))
conn.commit()
r4 = client.get("/api/agent/mentions", params={"workspace_id": ws_id})
secs = r4.json()["sections"]
assert secs[0]["key"] == "files" and secs[0]["label"] == "Espace Scope"
file_tokens = {it["token"] for it in secs[0]["items"]}
assert any(t.startswith("document:") and (r4.json()["workspace_id"] == ws_id) for t in file_tokens)
def test_router_feedback(client):
cid = _make_conversation(client)