feat(v5.9.0): AI Writing Assist - slash /ai, autocompletion, AI properties
- 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:
@@ -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 ──
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user