252 lines
7.4 KiB
Python
252 lines
7.4 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel, Field
|
|
|
|
from auth import require_auth
|
|
from context_builder import build_internal_context
|
|
from continuity import checkpoint_usage_session, close_usage_session
|
|
from derived_kinds import catalog
|
|
from dialogue_turn import run_turn, visible_for_role
|
|
from engine import EngineError
|
|
from dialogue_store import (
|
|
StoreError,
|
|
append_message,
|
|
create_conversation,
|
|
delete_conversation,
|
|
create_handoff,
|
|
create_space,
|
|
create_thread,
|
|
get_conversation,
|
|
insert_derived,
|
|
link_conversation_thread,
|
|
link_thread_space,
|
|
list_conversations,
|
|
list_derived_for_conversation,
|
|
list_messages,
|
|
start_usage_session,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/dialogue", tags=["dialogue"])
|
|
|
|
|
|
def _http(exc: StoreError | EngineError):
|
|
detail = {"code": exc.code, "message": exc.message}
|
|
extra = getattr(exc, "diagnostics", None)
|
|
if extra:
|
|
detail["diagnostics"] = extra
|
|
raise HTTPException(status_code=exc.status_code, detail=detail) from exc
|
|
|
|
|
|
class SessionWrite(BaseModel):
|
|
intent: str = ""
|
|
|
|
|
|
class ConversationWrite(BaseModel):
|
|
usage_session_id: str | None = None
|
|
title: str = ""
|
|
space_id: str | None = None
|
|
|
|
|
|
class MessageWrite(BaseModel):
|
|
body: str = Field(min_length=1)
|
|
role: str = "user"
|
|
id: str | None = None
|
|
|
|
|
|
class ThreadWrite(BaseModel):
|
|
title: str = ""
|
|
status: str = "open"
|
|
visibility: str = "internal"
|
|
|
|
|
|
class SpaceWrite(BaseModel):
|
|
title: str = ""
|
|
visibility: str = "internal"
|
|
|
|
|
|
class DerivedWrite(BaseModel):
|
|
kind: str
|
|
subject_type: str
|
|
subject_id: str
|
|
source_message_ids: list[str]
|
|
body: str = ""
|
|
visibility: str = "internal"
|
|
confidence: float | None = None
|
|
|
|
|
|
class HandoffWrite(BaseModel):
|
|
target: str
|
|
source_conversation_id: str | None = None
|
|
payload: dict = Field(default_factory=dict)
|
|
|
|
|
|
@router.get("/kinds")
|
|
def list_kinds(session: dict = Depends(require_auth)):
|
|
return catalog()
|
|
|
|
|
|
@router.post("/sessions")
|
|
def create_session(req: SessionWrite, session: dict = Depends(require_auth)):
|
|
return start_usage_session(session["profile_id"], req.intent)
|
|
|
|
|
|
@router.post("/sessions/{usage_session_id}/end")
|
|
def end_session(usage_session_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
return close_usage_session(session["profile_id"], usage_session_id)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/sessions/{usage_session_id}/checkpoint")
|
|
def checkpoint_session(usage_session_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
return checkpoint_usage_session(session["profile_id"], usage_session_id)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/conversations")
|
|
def post_conversation(req: ConversationWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return create_conversation(
|
|
session["profile_id"],
|
|
req.usage_session_id,
|
|
req.title,
|
|
space_id=req.space_id,
|
|
)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.get("/conversations")
|
|
def get_conversations(session: dict = Depends(require_auth)):
|
|
return list_conversations(session["profile_id"])
|
|
|
|
|
|
@router.delete("/conversations/{conversation_id}")
|
|
def remove_conversation(conversation_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
from writing_profile_store import remember_dialogue_style
|
|
|
|
result = delete_conversation(session["profile_id"], conversation_id)
|
|
remember_dialogue_style(session["profile_id"])
|
|
return result
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.get("/conversations/{conversation_id}")
|
|
def get_one_conversation(conversation_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
conv = get_conversation(session["profile_id"], conversation_id)
|
|
conv["messages"] = list_messages(session["profile_id"], conversation_id)
|
|
conv["derived"] = list_derived_for_conversation(session["profile_id"], conversation_id)
|
|
return conv
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.get("/conversations/{conversation_id}/context")
|
|
def get_internal_context(conversation_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
return build_internal_context(session["profile_id"], conversation_id)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/conversations/{conversation_id}/messages")
|
|
def post_message(conversation_id: str, req: MessageWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return append_message(session["profile_id"], conversation_id, req.body, req.role, req.id)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/conversations/{conversation_id}/turn")
|
|
def conversation_turn(conversation_id: str, req: MessageWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return visible_for_role(
|
|
run_turn(session["profile_id"], conversation_id, req.body, message_id=req.id),
|
|
session.get("role"),
|
|
)
|
|
except (StoreError, EngineError) as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.get("/conversations/{conversation_id}/messages")
|
|
def get_messages(conversation_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
return list_messages(session["profile_id"], conversation_id)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/threads")
|
|
def post_thread(req: ThreadWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return create_thread(session["profile_id"], req.title, req.status, req.visibility)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/conversations/{conversation_id}/threads/{thread_id}")
|
|
def post_conversation_thread(conversation_id: str, thread_id: str, session: dict = Depends(require_auth)):
|
|
try:
|
|
return link_conversation_thread(session["profile_id"], conversation_id, thread_id)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/spaces")
|
|
def post_space(req: SpaceWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return create_space(session["profile_id"], req.title, req.visibility)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/threads/{thread_id}/spaces/{space_id}")
|
|
def post_thread_space(thread_id: str, space_id: str, confidence: float | None = None, session: dict = Depends(require_auth)):
|
|
try:
|
|
return link_thread_space(session["profile_id"], thread_id, space_id, confidence)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.post("/derived")
|
|
def post_derived(req: DerivedWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return insert_derived(
|
|
session["profile_id"],
|
|
req.kind,
|
|
req.subject_type,
|
|
req.subject_id,
|
|
req.source_message_ids,
|
|
req.body,
|
|
req.visibility,
|
|
req.confidence,
|
|
)
|
|
except StoreError as exc:
|
|
_http(exc)
|
|
|
|
|
|
@router.get("/derived/latest")
|
|
def get_latest(subject_type: str, subject_id: str, kind: str, session: dict = Depends(require_auth)):
|
|
from data_layer import read
|
|
|
|
return read(
|
|
"latest_derived",
|
|
profile_id=session["profile_id"],
|
|
context={"subject_type": subject_type, "subject_id": subject_id, "kind": kind},
|
|
)
|
|
|
|
|
|
@router.post("/handoffs")
|
|
def post_handoff(req: HandoffWrite, session: dict = Depends(require_auth)):
|
|
try:
|
|
return create_handoff(session["profile_id"], req.target, req.source_conversation_id, req.payload)
|
|
except StoreError as exc:
|
|
_http(exc)
|