tech-envision commited on
Commit
79a843f
·
unverified ·
2 Parent(s): 94490a0 6da6be7

Merge pull request #35 from EnvisionMindCa/codex/create-api-for-llm-backend

Browse files
Files changed (2) hide show
  1. src/api.py +73 -0
  2. src/document_service.py +60 -0
src/api.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import List
4
+ from datetime import datetime
5
+
6
+ from fastapi import FastAPI, UploadFile, File, HTTPException
7
+ from pydantic import BaseModel
8
+ from fastapi.responses import PlainTextResponse
9
+
10
+ from .document_service import (
11
+ save_document,
12
+ list_documents,
13
+ get_document,
14
+ read_content,
15
+ )
16
+ from .log import get_logger
17
+
18
+
19
+ class DocumentInfo(BaseModel):
20
+ id: int
21
+ original_name: str
22
+ file_path: str
23
+ created_at: datetime
24
+
25
+ class Config:
26
+ from_attributes = True
27
+
28
+
29
+ class DocumentDetail(DocumentInfo):
30
+ content: str
31
+
32
+
33
+ def create_app() -> FastAPI:
34
+ app = FastAPI(title="LLM Backend API")
35
+ log = get_logger(__name__)
36
+
37
+ @app.post("/users/{username}/documents", response_model=DocumentInfo)
38
+ async def upload(username: str, file: UploadFile = File(...)) -> DocumentInfo:
39
+ log.info("Uploading document %s for %s", file.filename, username)
40
+ doc = save_document(username, file)
41
+ return DocumentInfo.model_validate(doc.__data__)
42
+
43
+ @app.get("/users/{username}/documents", response_model=List[DocumentInfo])
44
+ async def list_docs(username: str) -> List[DocumentInfo]:
45
+ docs = list_documents(username)
46
+ return [DocumentInfo.model_validate(d.__data__) for d in docs]
47
+
48
+ @app.get("/users/{username}/documents/{doc_id}", response_model=DocumentDetail)
49
+ async def inspect(username: str, doc_id: int) -> DocumentDetail:
50
+ doc = get_document(username, doc_id)
51
+ if not doc:
52
+ raise HTTPException(status_code=404, detail="Document not found")
53
+ content = read_content(doc)
54
+ data = DocumentDetail.model_validate(doc.__data__)
55
+ data.content = content
56
+ return data
57
+
58
+ @app.get("/users/{username}/documents/{doc_id}/raw", response_class=PlainTextResponse)
59
+ async def download(username: str, doc_id: int) -> PlainTextResponse:
60
+ doc = get_document(username, doc_id)
61
+ if not doc:
62
+ raise HTTPException(status_code=404, detail="Document not found")
63
+ return PlainTextResponse(read_content(doc))
64
+
65
+ return app
66
+
67
+
68
+ app = create_app()
69
+
70
+ if __name__ == "__main__":
71
+ import uvicorn
72
+
73
+ uvicorn.run(app, host="0.0.0.0", port=8000)
src/document_service.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ import shutil
5
+ from typing import List, Optional
6
+
7
+ from fastapi import UploadFile
8
+
9
+ from .config import UPLOAD_DIR
10
+ from .db import Document, User, init_db
11
+
12
+
13
+ def _ensure_user_dir(username: str) -> Path:
14
+ path = Path(UPLOAD_DIR) / username
15
+ path.mkdir(parents=True, exist_ok=True)
16
+ return path
17
+
18
+
19
+ def save_document(username: str, file: UploadFile) -> Document:
20
+ """Persist an uploaded file and return its database entry."""
21
+ init_db()
22
+ user, _ = User.get_or_create(username=username)
23
+ dest_dir = _ensure_user_dir(username)
24
+ dest = dest_dir / file.filename
25
+ with dest.open('wb') as buffer:
26
+ shutil.copyfileobj(file.file, buffer)
27
+ doc = Document.create(user=user, file_path=str(dest), original_name=file.filename)
28
+ return doc
29
+
30
+
31
+ def list_documents(username: str) -> List[Document]:
32
+ """Return all documents for ``username`` sorted by creation time."""
33
+ init_db()
34
+ try:
35
+ user = User.get(User.username == username)
36
+ except User.DoesNotExist:
37
+ return []
38
+ docs = Document.select().where(Document.user == user).order_by(Document.created_at)
39
+ return list(docs)
40
+
41
+
42
+ def get_document(username: str, doc_id: int) -> Optional[Document]:
43
+ """Retrieve a single document for ``username`` by id."""
44
+ init_db()
45
+ try:
46
+ user = User.get(User.username == username)
47
+ except User.DoesNotExist:
48
+ return None
49
+ try:
50
+ return Document.get(Document.id == doc_id, Document.user == user)
51
+ except Document.DoesNotExist:
52
+ return None
53
+
54
+
55
+ def read_content(doc: Document) -> str:
56
+ """Read and return the text content of ``doc``. Errors yield empty string."""
57
+ try:
58
+ return Path(doc.file_path).read_text(encoding='utf-8', errors='replace')
59
+ except Exception:
60
+ return ''