alaselababatunde commited on
Commit
bb12219
·
1 Parent(s): 38041ae

Add application file

Browse files
Files changed (3) hide show
  1. Dockerfile +0 -0
  2. app.py +156 -0
  3. requirements.txt +8 -0
Dockerfile ADDED
File without changes
app.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main.py
2
+ import os
3
+ import tempfile
4
+ from fastapi import FastAPI, UploadFile, File, Header, HTTPException
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from pydantic import BaseModel
7
+ from spitch import Spitch # Spitch Python SDK (docs use this pattern)
8
+ from langchain.prompts import PromptTemplate
9
+ from langchain.chains import LLMChain
10
+ from langchain_community.llms import HuggingFaceHub
11
+ from langdetect import detect, DetectorFactory
12
+
13
+ DetectorFactory.seed = 0
14
+
15
+ # --------- BASIC CONFIG ----------
16
+ SPITCH_API_KEY = os.getenv("SPITCH_API_KEY")
17
+ HF_MODEL = os.getenv("HF_MODEL", "google/flan-t5-base")
18
+ FRONTEND_ORIGIN = os.getenv("ALLOWED_ORIGIN", "*") # set to Vercel domain in production
19
+ PROJECT_API_KEY = os.getenv("PROJECT_API_KEY", "") # simple bearer key for frontend -> backend auth
20
+
21
+ if not SPITCH_API_KEY:
22
+ raise RuntimeError("Set SPITCH_API_KEY in environment before starting.")
23
+
24
+ # Init Spitch (SDK reads env var; docs show this pattern)
25
+ os.environ["SPITCH_API_KEY"] = SPITCH_API_KEY
26
+ spitch_client = Spitch()
27
+
28
+ # Init LLM
29
+ llm = HuggingFaceHub(repo_id=HF_MODEL, model_kwargs={"temperature": 0.2, "max_length": 512})
30
+
31
+ # FastAPI app
32
+ app = FastAPI(title="DevAssist AI Backend (FastAPI + LangChain)")
33
+
34
+ # CORS (allow only your Vercel domain in production)
35
+ app.add_middleware(
36
+ CORSMiddleware,
37
+ allow_origins=[FRONTEND_ORIGIN] if FRONTEND_ORIGIN != "*" else ["*"],
38
+ allow_credentials=True,
39
+ allow_methods=["GET", "POST", "OPTIONS"],
40
+ allow_headers=["Authorization", "Content-Type"],
41
+ )
42
+
43
+ # --------- PROMPT TEMPLATES ----------
44
+ chat_template = """You are DevAssist, an AI coding assistant.
45
+ - Help fix bugs and explain clearly.
46
+ - Provide complete, working code in markdown.
47
+ - Be friendly yet professional; break down problems step by step.
48
+ Question: {question}
49
+ Answer:"""
50
+
51
+ stt_chat_template = """You are DevAssist, an AI coding assistant.
52
+ - The input is transcribed speech. Interpret it as a dev question.
53
+ - Provide clear answers with code examples (use markdown triple backticks).
54
+ - If input is unclear, ask a clarifying question.
55
+ Spoken Question: {speech}
56
+ Answer:"""
57
+
58
+ autodoc_template = """You are DevAssist DocBot.
59
+ - Read the code and produce professional documentation in markdown.
60
+ Code: {code}
61
+ Documentation:"""
62
+
63
+ chat_chain = LLMChain(llm=llm, prompt=PromptTemplate(input_variables=["question"], template=chat_template))
64
+ stt_chain = LLMChain(llm=llm, prompt=PromptTemplate(input_variables=["speech"], template=stt_chat_template))
65
+ autodoc_chain = LLMChain(llm=llm, prompt=PromptTemplate(input_variables=["code"], template=autodoc_template))
66
+
67
+ # --------- REQUEST MODELS ----------
68
+ class ChatRequest(BaseModel):
69
+ question: str
70
+
71
+ class AutoDocRequest(BaseModel):
72
+ code: str
73
+
74
+ # --------- AUTH ----------
75
+ def check_auth(authorization: str | None):
76
+ if not PROJECT_API_KEY:
77
+ return # you can disable for local dev (but set in production)
78
+ if not authorization or not authorization.startswith("Bearer "):
79
+ raise HTTPException(status_code=401, detail="Missing bearer token")
80
+ token = authorization.split(" ", 1)[1]
81
+ if token != PROJECT_API_KEY:
82
+ raise HTTPException(status_code=403, detail="Invalid token")
83
+
84
+ # --------- ENDPOINTS ----------
85
+ @app.get("/")
86
+ def root():
87
+ return {"status": "DevAssist AI Backend running"}
88
+
89
+ @app.post("/chat")
90
+ def chat(req: ChatRequest, authorization: str | None = Header(None)):
91
+ check_auth(authorization)
92
+ answer = chat_chain.run(question=req.question)
93
+ return {"reply": answer.strip()}
94
+
95
+ # Speech endpoint: full pipeline speech -> transcription -> translation (if needed) -> LLM
96
+ @app.post("/stt")
97
+ async def stt_audio(file: UploadFile = File(...), lang_hint: str | None = None, authorization: str | None = Header(None)):
98
+ """
99
+ POST /stt with form-data file=@audio.mp3
100
+ Optional query/form field lang_hint: two-letter code (e.g. 'yo' for Yoruba) if frontend knows spoken language
101
+ Returns: transcription, detected_language, translation (to en), reply
102
+ """
103
+ check_auth(authorization)
104
+
105
+ # save uploaded file to temp file
106
+ suffix = os.path.splitext(file.filename)[1] or ".wav"
107
+ with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tf:
108
+ content = await file.read()
109
+ tf.write(content)
110
+ tmp_path = tf.name
111
+
112
+ # 1) Transcribe using Spitch SDK (docs show client.speech.transcribe)
113
+ # If lang_hint provided, pass it; else attempt without language param and fallback
114
+ try:
115
+ if lang_hint:
116
+ resp = spitch_client.speech.transcribe(language=lang_hint, content=open(tmp_path, "rb").read())
117
+ else:
118
+ # attempt transcribe without explicit language (SDK may auto-detect)
119
+ resp = spitch_client.speech.transcribe(content=open(tmp_path, "rb").read())
120
+ except Exception as e:
121
+ # fallback: try English transcription as last resort
122
+ resp = spitch_client.speech.transcribe(language="en", content=open(tmp_path, "rb").read())
123
+
124
+ transcription = getattr(resp, "text", "") or resp.get("text", "") if isinstance(resp, dict) else ""
125
+
126
+ # 2) Detect language of transcription if not provided
127
+ try:
128
+ detected_lang = detect(transcription) if transcription.strip() else "en"
129
+ except Exception:
130
+ detected_lang = "en"
131
+
132
+ # 3) If detected_lang != 'en', translate to English so LLM reasons in English
133
+ translation = transcription
134
+ if detected_lang != "en":
135
+ try:
136
+ translation_resp = spitch_client.text.translate(text=transcription, source=detected_lang, target="en")
137
+ translation = getattr(translation_resp, "text", "") or translation_resp.get("text", "") if isinstance(translation_resp, dict) else translation
138
+ except Exception:
139
+ # if translation fails, fallback to transcription
140
+ translation = transcription
141
+
142
+ # 4) Pass translated text to LLM (LLM assumes English)
143
+ reply = stt_chain.run(speech=translation)
144
+
145
+ return {
146
+ "transcription": transcription,
147
+ "detected_language": detected_lang,
148
+ "translation": translation,
149
+ "reply": reply.strip()
150
+ }
151
+
152
+ @app.post("/autodoc")
153
+ def autodoc(req: AutoDocRequest, authorization: str | None = Header(None)):
154
+ check_auth(authorization)
155
+ docs = autodoc_chain.run(code=req.code)
156
+ return {"documentation": docs.strip()}
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ pydantic
4
+ spitch
5
+ langchain
6
+ langchain-community
7
+ langdetect
8
+ httpx