Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions deeptutor/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ async def selective_access_log(request, call_next):
chat,
co_writer,
dashboard,
immersive_reading,
kids_admin,
kids,
imports,
knowledge,
mastery_path,
Expand Down Expand Up @@ -391,6 +394,25 @@ async def selective_access_log(request, call_next):
notebook.router, prefix="/api/v1/notebook", tags=["notebook"], dependencies=_auth
)
app.include_router(book.router, prefix="/api/v1/book", tags=["book"], dependencies=_auth)
app.include_router(
immersive_reading.router,
prefix="/api/v1/immersive-reading",
tags=["immersive-reading"],
dependencies=_auth,
)
# Kids parent-management endpoints require adult authentication.
app.include_router(
kids_admin.router,
prefix="/api/v1/kids-admin",
tags=["kids-admin"],
dependencies=_auth,
)
# Kids child-facing endpoints use a lightweight device token, no adult auth.
app.include_router(
kids.router,
prefix="/api/v1/kids",
tags=["kids"],
)
app.include_router(memory.router, prefix="/api/v1/memory", tags=["memory"], dependencies=_auth)
app.include_router(
capabilities_settings.router,
Expand Down
44 changes: 44 additions & 0 deletions deeptutor/api/routers/book.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ class RebuildBookRequest(BaseModel):
book_id: str
auto_compile: bool = True

class CharacterGraphRequest(BaseModel):
book_id: str
chapter_id: str
scope: str = "current" # "current" | "through_current"
force_refresh: bool = False



# ─────────────────────────────────────────────────────────────────────────────
# REST endpoints
Expand Down Expand Up @@ -472,6 +479,43 @@ async def rebuild_book(req: RebuildBookRequest) -> dict[str, Any]:
return {"pages": [p.model_dump(mode="json") for p in pages]}



# ─────────────────────────────────────────────────────────────────────────────
# Character relationship graph
# ─────────────────────────────────────────────────────────────────────────────


@router.post("/books/character-graph")
async def generate_character_graph(req: CharacterGraphRequest) -> dict[str, Any]:
"""Generate (or load from cache) a chapter-scoped character relationship graph.

The LLM only sees text from the requested scope (current chapter or all
chapters up to and including the current one). Future chapters are never
included.
"""
from deeptutor.book.character_graph import render_character_graph_mermaid

engine = get_book_engine()
try:
graph = await engine.generate_character_graph(
book_id=req.book_id,
chapter_id=req.chapter_id,
scope=req.scope,
force_refresh=req.force_refresh,
)
except ValueError as exc:
raise HTTPException(status_code=404, detail=str(exc))
except Exception as exc: # noqa: BLE001
logger.error(f"generate_character_graph failed: {exc}", exc_info=True)
raise HTTPException(status_code=500, detail=str(exc))

mermaid_src = render_character_graph_mermaid(graph)
return {
"graph": graph.model_dump(mode="json"),
"mermaid": mermaid_src,
}


# ─────────────────────────────────────────────────────────────────────────────
# WebSocket – streamed Book events
# ─────────────────────────────────────────────────────────────────────────────
Expand Down
Loading