AdrianFernandes commited on
Commit
3dc1341
·
verified ·
1 Parent(s): e2966fc

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -0
app.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import asyncio
4
+ import logging
5
+ from fastapi import FastAPI
6
+ from pydantic import BaseModel
7
+ from fastapi.responses import StreamingResponse
8
+ from huggingface_hub import hf_hub_download
9
+ from llama_cpp import Llama
10
+ from tavily import TavilyClient
11
+ from groq import Groq
12
+
13
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
14
+
15
+ groq_client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
16
+ tavily_client = TavilyClient(api_key=os.environ.get("TAVILY_API_KEY"))
17
+
18
+ logging.info("Downloading Konkani GGUF Model from friend's repo...")
19
+ model_path = hf_hub_download(
20
+ repo_id="dom0804/konkani_companion_GGUF",
21
+ filename="qwen2.5-3b-instruct.Q4_K_M.gguf"
22
+ )
23
+
24
+ logging.info("Loading into CPU...")
25
+ llm = Llama(model_path=model_path, n_ctx=2048, n_threads=2)
26
+
27
+ app = FastAPI()
28
+
29
+ class ChatPayload(BaseModel):
30
+ messages: list
31
+ stream: bool = False
32
+
33
+ @app.post("/openai/v1/chat/completions")
34
+ async def chat_endpoint(payload: ChatPayload):
35
+ user_query = payload.messages[-1]["content"]
36
+
37
+ # --- PHASE 1: SMART REWRITER ---
38
+ history_text = "\n".join([f"{m['role']}: {m['content']}" for m in payload.messages[:-1]])
39
+ rewriter_instruction = f"""You are a Search Query Optimizer.
40
+ RULES:
41
+ 1. If the query is purely conversational (e.g., "hi", "how are you"), output EXACTLY: SKIP_SEARCH
42
+ 2. If it is factual, generate a SINGLE optimized Google search string.
43
+ 3. Output ONLY the raw search query or SKIP_SEARCH.
44
+ History: {history_text}
45
+ Latest Query: {user_query}
46
+ """
47
+
48
+ try:
49
+ rewriter_response = groq_client.chat.completions.create(
50
+ messages=[{"role": "user", "content": rewriter_instruction}],
51
+ model="llama-3.1-8b-instant",
52
+ temperature=0.1
53
+ )
54
+ optimized_query = rewriter_response.choices[0].message.content.strip()
55
+ except Exception:
56
+ optimized_query = user_query
57
+
58
+ # --- PHASE 2: SMART SEARCH ---
59
+ if "SKIP_SEARCH" in optimized_query:
60
+ fact = "This is a conversational query. Respond naturally."
61
+ else:
62
+ try:
63
+ search_data = tavily_client.search(query=optimized_query, search_depth="basic")
64
+ fact = "\n".join([result['content'] for result in search_data['results'][:2]])
65
+ except Exception:
66
+ fact = "No live info available."
67
+
68
+ # --- PHASE 3: GROQ BRAIN ---
69
+ brain_instruction = f"""<system_role>
70
+ You are the Core Reasoning Engine for a cross-cultural Konkani conversational AI.
71
+ </system_role>
72
+ <live_context>{fact}</live_context>
73
+ <operational_rules>
74
+ 1. CONVERSATIONAL FLUIDITY: Respond warmly and naturally to social queries.
75
+ 2. FACTUAL GROUNDING: Base answers on the <live_context>. Do not hallucinate.
76
+ 3. DIRECT TRANSLATION OVERRIDE: If asked to translate, output EXACTLY the target English text.
77
+ 4. DOWNSTREAM SAFETY: Max 3 sentences per paragraph. NO markdown.
78
+ </operational_rules>"""
79
+
80
+ groq_messages = [{"role": "system", "content": brain_instruction}]
81
+ for msg in payload.messages[:-1]:
82
+ groq_messages.append({"role": msg["role"], "content": msg["content"]})
83
+ groq_messages.append({"role": "user", "content": user_query})
84
+
85
+ response = groq_client.chat.completions.create(
86
+ messages=groq_messages,
87
+ model="llama-3.3-70b-versatile"
88
+ )
89
+ english_paragraphs = [p for p in response.choices[0].message.content.strip().split('\n') if p.strip()]
90
+
91
+ # --- PHASE 4: GGUF TRANSLATION STREAM ---
92
+ async def stream_generator():
93
+ for para in english_paragraphs:
94
+ formatted_prompt = f"<|im_start|>system\nYou are a highly accurate translation model. Translate the following English text into Konkani.<|im_end|>\n<|im_start|>user\n{para}<|im_end|>\n<|im_start|>assistant\n"
95
+
96
+ stream = llm(formatted_prompt, max_tokens=1024, stop=["<|im_end|>"], stream=True, temperature=0.1)
97
+ for chunk in stream:
98
+ yield f"data: {json.dumps({'id': 'chatcmpl-custom', 'object': 'chat.completion.chunk', 'choices': [{'delta': {'content': chunk['choices'][0]['text']}}]})}\n\n"
99
+ await asyncio.sleep(0.01)
100
+ yield f"data: {json.dumps({'choices': [{'delta': {'content': '\n\n'}}]})}\n\n"
101
+ yield "data: [DONE]\n\n"
102
+
103
+ if payload.stream:
104
+ return StreamingResponse(stream_generator(), media_type="text/event-stream")
105
+ else:
106
+ final_text = ""
107
+ async for chunk in stream_generator():
108
+ if chunk != "data: [DONE]\n\n":
109
+ data_dict = json.loads(chunk.replace("data: ", "").strip())
110
+ if "content" in data_dict["choices"][0]["delta"]:
111
+ final_text += data_dict["choices"][0]["delta"]["content"]
112
+ return {"choices": [{"message": {"role": "assistant", "content": final_text.strip()}}]}