Vineetiitg commited on
Commit
f4f923e
·
1 Parent(s): 7aad172

feat: add FastAPI chat endpoints and streaming response support

Browse files
Files changed (2) hide show
  1. app/core/config.py +21 -0
  2. app/main.py +71 -0
app/core/config.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pydantic_settings import BaseSettings
3
+
4
+ class Settings(BaseSettings):
5
+ PROJECT_NAME: str = "Support Docs Copilot"
6
+
7
+ # Ollama LLM Config
8
+ OLLAMA_BASE_URL: str = os.getenv("OLLAMA_BASE_URL", "http://ollama:11434")
9
+ OLLAMA_MODEL: str = os.getenv("OLLAMA_MODEL", "llama3")
10
+
11
+ # Qdrant Vector DB Config
12
+ QDRANT_URL: str = os.getenv("QDRANT_URL", "")
13
+ QDRANT_LOCATION: str = os.getenv("QDRANT_LOCATION", "./qdrant_data")
14
+ COLLECTION_NAME: str = "support_docs"
15
+
16
+ # Embeddings Config
17
+ DENSE_EMBEDDING_MODEL: str = "BAAI/bge-small-en-v1.5"
18
+ SPARSE_EMBEDDING_MODEL: str = "Qdrant/bm25"
19
+ RERANKER_MODEL: str = "BAAI/bge-reranker-base"
20
+
21
+ settings = Settings()
app/main.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from fastapi import FastAPI, HTTPException
3
+ from fastapi.responses import StreamingResponse
4
+ from pydantic import BaseModel
5
+ from guardrails import Guard
6
+ from langchain_core.prompts import PromptTemplate
7
+ from langchain_ollama import ChatOllama
8
+
9
+ from app.core.config import settings
10
+ from app.graph.workflow import compile_workflow
11
+ from app.guardrails.validators import DetectPromptInjection
12
+
13
+ app = FastAPI(title=settings.PROJECT_NAME)
14
+ rag_agent = compile_workflow()
15
+ input_guard = Guard().use(DetectPromptInjection, on_fail="exception")
16
+
17
+ class ChatRequest(BaseModel):
18
+ query: str
19
+
20
+ class ChatResponse(BaseModel):
21
+ query: str
22
+ answer: str
23
+
24
+ @app.post("/chat", response_model=ChatResponse)
25
+ async def chat_endpoint(request: ChatRequest):
26
+ try:
27
+ input_guard.validate(request.query)
28
+ except Exception as e:
29
+ raise HTTPException(status_code=400, detail=str(getattr(e, "message", e)))
30
+
31
+ initial_state = {"question": request.query, "run_count": 0}
32
+ try:
33
+ final_state = rag_agent.invoke(initial_state)
34
+ answer = final_state.get("generation", "Unable to compile answer.")
35
+ except Exception as e:
36
+ raise HTTPException(status_code=500, detail=str(e))
37
+
38
+ return ChatResponse(query=request.query, answer=answer)
39
+
40
+ @app.post("/chat/stream")
41
+ async def chat_stream_endpoint(request: ChatRequest):
42
+ try:
43
+ input_guard.validate(request.query)
44
+ except Exception as e:
45
+ raise HTTPException(status_code=400, detail=str(getattr(e, "message", e)))
46
+
47
+ async def token_generator():
48
+ initial_state = {"question": request.query, "run_count": 0}
49
+ final_state = rag_agent.invoke(initial_state)
50
+ documents = final_state.get("documents", [])
51
+
52
+ if not documents:
53
+ yield "I am sorry, no reliable matching documentation was found."
54
+ return
55
+
56
+ context = "\n\n".join(doc.page_content for doc in documents)
57
+ prompt = PromptTemplate(
58
+ template="""You are a Support Docs Copilot. Use the retrieved context to answer the question concisely. If you don't know the answer, say "I don't know".
59
+ Question: {question}
60
+ Context: {context} \n\nAnswer:""",
61
+ input_variables=["question", "context"],
62
+ )
63
+ async_llm = ChatOllama(model=settings.OLLAMA_MODEL, temperature=0, base_url=settings.OLLAMA_BASE_URL)
64
+ rag_chain = prompt | async_llm
65
+
66
+ async for chunk in rag_chain.astream({"context": context, "question": request.query}):
67
+ if chunk.content:
68
+ yield chunk.content
69
+ await asyncio.sleep(0.01)
70
+
71
+ return StreamingResponse(token_generator(), media_type="text/event-stream")