RAG_Documentation_Assistant / API_SPECIFICATION.md
aimprabu's picture
fix(ui): gracefully format dictionary error payloads to prevent TypeError
55a76a3
|
Raw History Blame Contribute Delete
13.8 kB

API_SPECIFICATION.md — API Specification

RAG-Based Technical Documentation Assistant

Version: 1.0.0 Base URL: http://localhost:8000 OpenAPI Docs: http://localhost:8000/docs ReDoc: http://localhost:8000/redoc


Global Response Conventions

All responses return Content-Type: application/json.

Standard Error Response

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Human-readable description",
    "details": {}
  },
  "request_id": "req_abc123"
}

HTTP Status Code Reference

Code Meaning
200 Success
201 Created (new resource ingested)
400 Bad Request (invalid input)
404 Not Found
422 Unprocessable Entity (Pydantic validation failure)
429 Rate Limited
503 Service Unavailable (LLM or vector store down)
500 Internal Server Error

Endpoints


POST /query

Purpose: Submit a natural language question and receive a grounded, cited answer from the documentation corpus.

Request Schema

{
  "question": "string",         // required, 1-2000 chars
  "session_id": "string",       // optional, UUID for conversation memory
  "top_k": 5,                   // optional, 1-20, default 5
  "max_retries": 2              // optional, 0-5, default from config
}

Pydantic Model

class QueryRequest(BaseModel):
    question: str = Field(..., min_length=1, max_length=2000,
                          description="Natural language question")
    session_id: Optional[str] = Field(None, description="Session ID for conversation memory")
    top_k: Optional[int] = Field(5, ge=1, le=20, description="Number of chunks to retrieve")
    max_retries: Optional[int] = Field(None, ge=0, le=5,
                                       description="Override max retry count")

Response Schema

{
  "answer": "string",           // generated answer text with inline citations
  "sources": [
    {
      "source_file": "string",
      "document_id": "string",
      "chunk_index": 0,
      "excerpt": "string"       // first 100 chars of chunk
    }
  ],
  "query_type": "string",       // conceptual | how-to | troubleshooting | api-reference
  "rewritten_query": "string",  // the query actually used for retrieval
  "retry_count": 0,             // number of query rewrites performed
  "is_fallback": false,         // true if insufficient context was found
  "response_time_ms": 1234,
  "session_id": "string"        // echoed back if provided
}

Validation Rules

  • question must be 1-2000 characters
  • question must not be empty or whitespace-only
  • top_k must be between 1 and 20 (inclusive)
  • session_id if provided must match UUID format: ^[0-9a-f-]{36}$

Error Responses

Code Error Code Condition
422 VALIDATION_ERROR question exceeds 2000 chars or is empty
503 LLM_UNAVAILABLE LLM provider API is down
503 VECTOR_STORE_ERROR ChromaDB unavailable
500 WORKFLOW_ERROR Unexpected error in LangGraph

Example Request

curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{
    "question": "How do I create a custom tool in LangChain?",
    "top_k": 5
  }'

Example Response (Success)

{
  "answer": "To create a custom tool in LangChain, you can use the @tool decorator or inherit from the BaseTool class. [Source: langchain_docs.md]\n\nUsing the decorator approach:\n```python\nfrom langchain.tools import tool\n\n@tool\ndef my_tool(input: str) -> str:\n    \"\"\"Describe what the tool does.\"\"\"\n    return f\"Result: {input}\"\n```\n[Source: langchain_docs.md, chunk 12]",
  "sources": [
    {
      "source_file": "langchain_docs.md",
      "document_id": "doc_001",
      "chunk_index": 12,
      "excerpt": "To create a custom tool, use the @tool decorator. The function's docstring..."
    }
  ],
  "query_type": "how-to",
  "rewritten_query": "LangChain custom tool creation @tool decorator BaseTool class",
  "retry_count": 0,
  "is_fallback": false,
  "response_time_ms": 2341,
  "session_id": null
}

Example Response (Fallback)

{
  "answer": "I was unable to find relevant information in the documentation corpus to answer your question. Please check that your question is related to the indexed documents, or consider rephrasing it.",
  "sources": [],
  "query_type": "conceptual",
  "rewritten_query": "React hooks useEffect tutorial",
  "retry_count": 2,
  "is_fallback": true,
  "response_time_ms": 5876,
  "session_id": null
}

POST /ingest

Purpose: Ingest a new document into the corpus. Accepts a file upload or a URL. The document is chunked, embedded, and stored in the vector store.

Request Schema (multipart/form-data)

file:    File (optional) — .md, .txt, .html, .pdf, max 10MB
url:     string (optional) — publicly accessible URL to fetch

At least one of file or url must be provided.

Validation Rules

  • Exactly one of file or url must be provided (not both, not neither)
  • Allowed file extensions: .md, .txt, .html, .pdf
  • Maximum file size: 10MB
  • URL must be a valid http:// or https:// URL
  • File content must not be empty after loading

Response Schema

{
  "document_id": "string",       // assigned document ID
  "filename": "string",          // original filename or URL-derived name
  "chunks_indexed": 42,          // number of chunks created and indexed
  "file_size_bytes": 12345,
  "status": "indexed",           // indexed | failed
  "message": "string",           // human-readable summary
  "duplicate": false             // true if document was already indexed (dedup check)
}

Error Responses

Code Error Code Condition
400 MISSING_SOURCE Neither file nor url provided
400 BOTH_SOURCES Both file and url provided
400 INVALID_FILE_TYPE File extension not allowed
400 FILE_TOO_LARGE File exceeds 10MB
400 EMPTY_DOCUMENT File has no extractable text content
400 INVALID_URL URL is malformed
400 URL_FETCH_FAILED URL could not be fetched (404, timeout, etc.)
503 VECTOR_STORE_ERROR ChromaDB unavailable

Example Request (File Upload)

curl -X POST http://localhost:8000/ingest \
  -F "file=@langchain_docs.md"

Example Request (URL)

curl -X POST http://localhost:8000/ingest \
  -F "url=https://raw.githubusercontent.com/langchain-ai/langchain/master/README.md"

Example Response

{
  "document_id": "doc_007",
  "filename": "langchain_docs.md",
  "chunks_indexed": 47,
  "file_size_bytes": 23041,
  "status": "indexed",
  "message": "Document 'langchain_docs.md' successfully indexed with 47 chunks.",
  "duplicate": false
}

Example Response (Duplicate)

{
  "document_id": "doc_001",
  "filename": "langchain_docs.md",
  "chunks_indexed": 0,
  "file_size_bytes": 23041,
  "status": "indexed",
  "message": "Document 'langchain_docs.md' was already indexed. Skipping.",
  "duplicate": true
}

GET /documents

Purpose: List all documents that have been ingested into the corpus.

Query Parameters

Parameter Type Default Description
status string all Filter by status: indexed, failed, all
limit int 50 Max records to return (1-100)
offset int 0 Pagination offset

Response Schema

{
  "documents": [
    {
      "document_id": "string",
      "filename": "string",
      "source_url": "string",
      "chunk_count": 47,
      "file_type": "md",
      "status": "indexed",
      "ingestion_timestamp": "2025-06-11T10:00:00Z",
      "file_size_bytes": 23041
    }
  ],
  "total": 5,
  "limit": 50,
  "offset": 0
}

Error Responses

Code Error Code Condition
422 VALIDATION_ERROR Invalid query parameter value
503 REGISTRY_ERROR Document registry unavailable

Example Request

curl http://localhost:8000/documents
curl "http://localhost:8000/documents?status=indexed&limit=10"

Example Response

{
  "documents": [
    {
      "document_id": "doc_001",
      "filename": "langchain_docs.md",
      "source_url": "",
      "chunk_count": 47,
      "file_type": "md",
      "status": "indexed",
      "ingestion_timestamp": "2025-06-11T10:00:00Z",
      "file_size_bytes": 23041
    },
    {
      "document_id": "doc_002",
      "filename": "fastapi_tutorial.md",
      "source_url": "https://fastapi.tiangolo.com/tutorial/",
      "chunk_count": 31,
      "file_type": "md",
      "status": "indexed",
      "ingestion_timestamp": "2025-06-11T10:05:00Z",
      "file_size_bytes": 15600
    }
  ],
  "total": 2,
  "limit": 50,
  "offset": 0
}

POST /feedback

Purpose: Submit user feedback (thumbs up/down) on a generated answer. Used for offline quality monitoring.

Request Schema

{
  "query": "string",            // required, the question that was asked
  "answer": "string",           // required, the answer that was given
  "rating": "thumbs_up",        // required: "thumbs_up" | "thumbs_down"
  "comment": "string",          // optional, free text comment
  "session_id": "string",       // optional, for correlation
  "response_time_ms": 1234      // optional, from query response
}

Pydantic Model

class FeedbackRequest(BaseModel):
    query: str = Field(..., min_length=1, max_length=2000)
    answer: str = Field(..., min_length=1, max_length=10000)
    rating: Literal["thumbs_up", "thumbs_down"]
    comment: Optional[str] = Field(None, max_length=1000)
    session_id: Optional[str] = None
    response_time_ms: Optional[int] = Field(None, ge=0)

Response Schema

{
  "feedback_id": "string",     // UUID assigned to this feedback record
  "status": "recorded",
  "message": "Thank you for your feedback."
}

Error Responses

Code Error Code Condition
422 VALIDATION_ERROR rating not one of allowed values
422 VALIDATION_ERROR query or answer empty
503 STORAGE_ERROR Feedback database unavailable

Example Request

curl -X POST http://localhost:8000/feedback \
  -H "Content-Type: application/json" \
  -d '{
    "query": "How do I create a custom tool in LangChain?",
    "answer": "To create a custom tool, use the @tool decorator...",
    "rating": "thumbs_up",
    "comment": "Very helpful, exactly what I needed"
  }'

Example Response

{
  "feedback_id": "fb_d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
  "status": "recorded",
  "message": "Thank you for your feedback."
}

GET /health

Purpose: Health check endpoint for load balancers and monitoring systems.

Response Schema

{
  "status": "healthy",           // healthy | degraded | unhealthy
  "version": "1.0.0",
  "components": {
    "vector_store": "ok",        // ok | error
    "document_registry": "ok",
    "llm_provider": "ok",
    "embedding_model": "ok"
  },
  "corpus_size": 5,              // number of indexed documents
  "timestamp": "2025-06-11T10:00:00Z"
}

Example Request

curl http://localhost:8000/health

DELETE /documents/{document_id}

Purpose: Remove a document and all its chunks from the vector store and registry.

Path Parameters

Parameter Type Description
document_id string The document ID from GET /documents

Response Schema

{
  "document_id": "string",
  "chunks_removed": 47,
  "status": "deleted",
  "message": "Document 'doc_001' and 47 chunks have been removed."
}

Error Responses

Code Error Code Condition
404 DOCUMENT_NOT_FOUND document_id does not exist
503 VECTOR_STORE_ERROR ChromaDB unavailable

Example Request

curl -X DELETE http://localhost:8000/documents/doc_001

GET /feedback

Purpose: Retrieve stored feedback records for analysis (admin use).

Query Parameters

Parameter Type Default Description
rating string all Filter: thumbs_up, thumbs_down, all
limit int 50 Max records (1-100)
offset int 0 Pagination offset

Example Request

curl "http://localhost:8000/feedback?rating=thumbs_down&limit=20"

Example Response

{
  "feedback": [
    {
      "feedback_id": "fb_abc123",
      "query": "How do I install FastAPI?",
      "rating": "thumbs_down",
      "comment": "Answer was about the wrong version",
      "created_at": "2025-06-11T11:00:00Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

FastAPI Application Setup

# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.routes import query, ingest, documents, feedback, health

app = FastAPI(
    title="RAG Technical Documentation Assistant",
    description="Self-corrective RAG system powered by LangGraph",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # restrict in production
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(query.router)
app.include_router(ingest.router)
app.include_router(documents.router)
app.include_router(feedback.router)
app.include_router(health.router)