feat(v5.9.0): AI Writing Assist - slash /ai, autocompletion, AI properties
FlowDeck CI / test (push) Failing after 25s
FlowDeck CI / docker (push) Skipped

- Service app/services/ai_writing.py: 6 actions sans outils (write, summarize,
  translate, continue, autocomplete, properties) + replis deterministes offline
- Endpoints POST /api/agent/writing et /api/agent/writing/properties
- Editeur: groupe slash AI (Write/Summarize/Translate/Continue) via E.aiSlash
- Autocompletion inline AIAC (suggestion ~900ms, Tab accepte, Escape rejette)
- Database: bouton AI fill (suggestions de proprietes Status/Priority/Resume)
- Tests tests/test_ai_writing.py (+29) ; version 5.9.0 (VERSION, main.py)
- CHANGELOG + ROADMAP v5.9.0 completes
This commit is contained in:
2026-09-10 11:24:21 -04:00
parent 3b27c57230
commit f09de98406
9 changed files with 885 additions and 15 deletions
+76
View File
@@ -367,6 +367,82 @@ async def agent_generate(request: Request):
}
# ── AI Writing Assist (v5.9.0) ──
@router.post("/writing")
async def agent_writing(request: Request):
"""Headless writing actions for the editor (v5.9.0).
`action` is one of: write, summarize, translate, continue, autocomplete.
Returns plain Markdown (`text`) plus `model`/`offline` metadata. The
`properties` action is served by `/writing/properties`.
"""
from app.services.ai_writing import AIWritingService, WRITING_ACTIONS
user_id = await _current_user_id(request)
body = await request.json() if request.headers.get("content-type") else {}
action = (body.get("action") or "").strip().lower()
if not action:
raise HTTPException(status_code=400, detail="action est requis")
if action == "properties":
raise HTTPException(status_code=400, detail="Utilisez /writing/properties pour l'action properties")
if action not in WRITING_ACTIONS:
raise HTTPException(status_code=400, detail=f"Action inconnue: {action}")
prompt = (body.get("prompt") or "").strip()
if action == "write" and not prompt and not (body.get("title") or "").strip():
raise HTTPException(status_code=400, detail="prompt est requis pour l'action write")
service = AIWritingService(
user_id=user_id,
provider=(body.get("provider") or "").strip() or None,
model=(body.get("model") or "").strip() or None,
)
result = await service.run(
action,
prompt=prompt,
context=(body.get("context") or ""),
target_language=(body.get("target_language") or "English"),
prefix=(body.get("prefix") or ""),
title=(body.get("title") or ""),
)
return result
@router.post("/writing/properties")
async def agent_writing_properties(request: Request):
"""Suggest values for a collection page's properties (v5.9.0).
Body: `{title, content?, properties: [{name, type}], provider?, model?}`.
Returns `{ok, suggestions: {property_name: value}, model, offline}`.
"""
from app.services.ai_writing import AIWritingService
user_id = await _current_user_id(request)
body = await request.json() if request.headers.get("content-type") else {}
properties = body.get("properties") or []
if not isinstance(properties, list) or not properties:
raise HTTPException(status_code=400, detail="properties est requis (liste non vide)")
service = AIWritingService(
user_id=user_id,
provider=(body.get("provider") or "").strip() or None,
model=(body.get("model") or "").strip() or None,
)
suggestions = await service.suggest_properties(
context=(body.get("content") or ""),
title=(body.get("title") or ""),
properties=properties,
)
return {
"ok": True,
"suggestions": suggestions,
"model": service.model or "",
"offline": service._offline_hint(),
}
# ── Audit & rollback ──