Spaces:
Sleeping
Sleeping
File size: 8,928 Bytes
bb12219 3c9086c 2bab06e bb12219 f0502f7 bb12219 cab03d6 bb12219 351bb59 d48566f bb12219 d48566f bb12219 ee2e099 f0502f7 2bab06e bb12219 3c9086c f0502f7 5e51aba 26fd760 d48566f f0502f7 bb12219 3c9086c bb12219 2bab06e 2df5630 e92af5d d48566f e92af5d 2df5630 e92af5d bb12219 d48566f f51917e bb12219 f51917e bb12219 d48566f f51917e bb12219 f51917e bb12219 7b32b5c caa2aff d48566f caa2aff d48566f c27fb7c d48566f caa2aff d48566f 170f71d f51917e 5e51aba 7b32b5c 2bab06e bb12219 f51917e d48566f 3c9086c f0502f7 bb12219 3c9086c 2bab06e 3c9086c bb12219 3c9086c 2bab06e 3c9086c d48566f bb12219 d48566f bb12219 3c9086c bb12219 2bab06e 3c9086c 2bab06e 3c9086c f0502f7 3c9086c bb12219 f51917e d48566f bb12219 d48566f bb12219 d48566f bb12219 3c9086c 2bab06e 3c9086c 2bab06e bb12219 d48566f bb12219 3c9086c f0502f7 f96cd6f d48566f c27fb7c f51917e 2bab06e 3c9086c 9ec1122 3c9086c 9ec1122 3c9086c 9ec1122 3c9086c 9ec1122 3c9086c f96cd6f 351bb59 f0502f7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
import os
import tempfile
import traceback
from fastapi import FastAPI, UploadFile, File, Header, HTTPException, Body
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from spitch import Spitch
from langchain.prompts import PromptTemplate
from langchain_huggingface import HuggingFaceEndpoint
from langdetect import detect, DetectorFactory
from huggingface_hub.utils import HfHubHTTPError
from smebuilder_vector import retriever # Retriever for context injection
# ----------------- CONFIG -----------------
DetectorFactory.seed = 0
SPITCH_API_KEY = os.getenv("SPITCH_API_KEY")
HF_MODEL = os.getenv("HF_MODEL", "deepseek-ai/deepseek-coder-1.3b-instruct")
FRONTEND_ORIGIN = os.getenv("ALLOWED_ORIGIN", "*")
PROJECT_API_KEY = os.getenv("PROJECT_API_KEY")
if not SPITCH_API_KEY:
raise RuntimeError("Set SPITCH_API_KEY in environment before starting.")
os.environ["SPITCH_API_KEY"] = SPITCH_API_KEY
spitch_client = Spitch()
# HuggingFace LLM
llm = HuggingFaceEndpoint(
repo_id=HF_MODEL,
temperature=0.7,
top_p=0.9,
do_sample=True,
repetition_penalty=1.1,
max_new_tokens=2048
)
# ----------------- FASTAPI -----------------
app = FastAPI(title="DevAssist AI Backend (FastAPI + LangChain)")
app.add_middleware(
CORSMiddleware,
allow_origins=[FRONTEND_ORIGIN] if FRONTEND_ORIGIN != "*" else ["*"],
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["Authorization", "Content-Type"],
)
# ----------------- PROMPT TEMPLATES -----------------
chat_template = """You are DevAssist, an AI coding assistant.
Guidelines:
- Always format responses in Markdown.
- Use section headers: Explanation:, Steps:, Fixed Code:
- Use bullet points for steps.
- Use fenced code blocks for code.
- Be friendly yet professional.
Question: {question}
Answer:
"""
stt_chat_template = """You are DevAssist, an AI coding assistant.
The input is transcribed speech. Interpret it as a developer question.
Provide clear answers with code examples.
If unclear, ask for clarification.
Spoken Question: {speech}
Answer:
"""
autodoc_template = """You are DevAssist DocBot.
Read the code and produce professional documentation in markdown.
Code: {code}
Documentation:
"""
sme_template = """
You are a senior full-stack engineer specializing in modern front-end development.
Your job is to generate **production-ready code** for websites and apps.
Guidelines:
- Always return three separate files: index.html, styles.css, and script.js
- HTML must be semantic, responsive, and mobile-first
- CSS should use Flexbox/Grid with hover/transition effects
- JavaScript must add interactivity (animations, toggles, button actions)
- Include hero, feature grid, testimonials, and footer
- Use realistic content (no lorem ipsum, no placeholders)
Prompt: {user_prompt}
Context: {context}
Output:
"""
# ----------------- CHAINS -----------------
chat_chain = PromptTemplate(input_variables=["question"], template=chat_template) | llm
stt_chain = PromptTemplate(input_variables=["speech"], template=stt_chat_template) | llm
autodoc_chain = PromptTemplate(input_variables=["code"], template=autodoc_template) | llm
sme_chain = PromptTemplate(input_variables=["user_prompt", "context"], template=sme_template) | llm
# ----------------- REQUEST MODELS -----------------
class ChatRequest(BaseModel):
question: str
class AutoDocRequest(BaseModel):
code: str
# ----------------- AUTH -----------------
def check_auth(authorization: str | None):
if not PROJECT_API_KEY:
return
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing bearer token")
token = authorization.split(" ", 1)[1]
if token != PROJECT_API_KEY:
raise HTTPException(status_code=403, detail="Invalid token")
# ----------------- HELPER FUNCTIONS -----------------
def run_chain(chain, input_dict: dict):
"""
Safely run a LangChain PromptTemplate | HuggingFaceEndpoint chain.
Converts output to string and captures errors.
"""
try:
# Render template
if hasattr(chain, "prompt"):
prompt_text = chain.prompt.format(**input_dict)
else:
prompt_text = str(input_dict)
# Generate using HuggingFaceEndpoint (expects str input)
output = chain.llm.generate([{"role": "user", "content": prompt_text}])
return output.generations[0][0].text.strip()
except Exception:
return {"success": False, "error": "⚠️ LLM error", "details": traceback.format_exc()}
async def process_audio(file: UploadFile, lang_hint: str | None = None):
suffix = os.path.splitext(file.filename)[1] or ".wav"
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tf:
tf.write(await file.read())
tmp_path = tf.name
with open(tmp_path, "rb") as f:
audio_bytes = f.read()
try:
if lang_hint:
resp = spitch_client.speech.transcribe(language=lang_hint, content=audio_bytes)
else:
resp = spitch_client.speech.transcribe(content=audio_bytes)
except Exception:
resp = spitch_client.speech.transcribe(language="en", content=audio_bytes)
transcription = getattr(resp, "text", "") or (resp.get("text", "") if isinstance(resp, dict) else "")
detected_lang = "en"
try:
detected_lang = detect(transcription) if transcription.strip() else "en"
except Exception:
pass
translation = transcription
if detected_lang != "en":
try:
translation_resp = spitch_client.text.translate(text=transcription, source=detected_lang, target="en")
translation = getattr(translation_resp, "text", "") or translation_resp.get("text", "")
except Exception:
translation = transcription
return transcription, detected_lang, translation
# ----------------- ENDPOINTS -----------------
@app.get("/")
def root():
return {"status": "✅ DevAssist AI Backend running"}
@app.post("/chat")
def chat(req: ChatRequest, authorization: str | None = Header(None)):
check_auth(authorization)
result = run_chain(chat_chain, {"question": req.question})
return result if isinstance(result, dict) else {"reply": result}
@app.post("/stt")
async def stt_audio(file: UploadFile = File(...), lang_hint: str | None = None, authorization: str | None = Header(None)):
check_auth(authorization)
transcription, detected_lang, translation = await process_audio(file, lang_hint)
result = run_chain(stt_chain, {"speech": translation})
return {
"transcription": transcription,
"detected_language": detected_lang,
"translation": translation,
"reply": result if isinstance(result, str) else result.get("reply", "")
}
@app.post("/autodoc")
def autodoc(req: AutoDocRequest, authorization: str | None = Header(None)):
check_auth(authorization)
result = run_chain(autodoc_chain, {"code": req.code})
return result if isinstance(result, dict) else {"documentation": result}
@app.post("/sme/generate")
async def sme_generate(payload: dict = Body(...), authorization: str | None = Header(None)):
check_auth(authorization)
try:
user_prompt = payload.get("user_prompt", "")
context_docs = retriever.get_relevant_documents(user_prompt)
context = "\n".join([doc.page_content for doc in context_docs]) if context_docs else "No extra context"
result = run_chain(sme_chain, {"user_prompt": user_prompt, "context": context})
return {"success": True, "data": result if isinstance(result, str) else result.get("reply", "")}
except Exception:
return {"success": False, "error": "⚠️ LLM error", "details": traceback.format_exc()}
@app.post("/sme/speech-generate")
async def sme_speech_generate(file: UploadFile = File(...), lang_hint: str | None = None, authorization: str | None = Header(None)):
check_auth(authorization)
transcription, detected_lang, translation = await process_audio(file, lang_hint)
try:
context_docs = retriever.get_relevant_documents(translation)
context = "\n".join([doc.page_content for doc in context_docs]) if context_docs else "No extra context"
result = run_chain(sme_chain, {"user_prompt": translation, "context": context})
return {
"success": True,
"transcription": transcription,
"detected_language": detected_lang,
"translation": translation,
"sme_site": result if isinstance(result, str) else result.get("reply", "")
}
except Exception:
return {"success": False, "error": "⚠️ LLM error", "details": traceback.format_exc()}
# ----------------- MAIN -----------------
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False)
|