diff --git a/surfsense_backend/app/routes/documents_routes.py b/surfsense_backend/app/routes/documents_routes.py index a5d31f07c..574bf78b5 100644 --- a/surfsense_backend/app/routes/documents_routes.py +++ b/surfsense_backend/app/routes/documents_routes.py @@ -1,7 +1,8 @@ # Force asyncio to use standard event loop before unstructured imports import asyncio -from fastapi import APIRouter, Depends, Form, HTTPException, Query, UploadFile +from fastapi import APIRouter, Depends, Form, HTTPException, Query, Request, UploadFile +from fastapi.responses import PlainTextResponse from pydantic import BaseModel as PydanticBaseModel, Field from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select @@ -34,6 +35,8 @@ from app.schemas import ( FolderRead, PaginatedResponse, ) +from app.services.export_service import resolve_document_markdown +from app.services.okf import document_to_concept from app.services.task_dispatcher import TaskDispatcher, get_task_dispatcher from app.users import get_auth_context from app.utils.rbac import check_permission @@ -1255,12 +1258,14 @@ async def get_document_chunks_paginated( @router.get("/documents/{document_id}", response_model=DocumentRead) async def read_document( document_id: int, + request: Request, session: AsyncSession = Depends(get_async_session), auth: AuthContext = Depends(get_auth_context), ): - """ - Get a specific document by ID. - Requires DOCUMENTS_READ permission for the workspace. + """Get a document by ID. + + ``Accept: text/markdown`` returns the OKF concept; otherwise the JSON record. + Requires DOCUMENTS_READ permission. """ try: result = await session.execute( @@ -1282,6 +1287,16 @@ async def read_document( "You don't have permission to read documents in this workspace", ) + # ponytail: substring match, not RFC 7231 q-values (OKF is the only non-JSON view). + if "text/markdown" in request.headers.get("accept", ""): + body = ( + await resolve_document_markdown(session, document) + or document.content + or "" + ) + concept = document_to_concept(document, body=body) + return PlainTextResponse(concept, media_type="text/markdown") + raw_content = document.content or "" return DocumentRead( id=document.id,