Texbase / AgenticControl /MarketRAG.py
Asad999's picture
Initial clean deployment for Hugging Face Spaces (v5 - final fix)
d712cef
Raw
History Blame Contribute Delete
6.73 kB
import os
import json
import sys
import chromadb
from groq import Groq
from dotenv import load_dotenv
# Load env from backend/.env
ROOT_DIR = os.environ.get('WORKSPACE_ROOT', '.')
load_dotenv(os.path.join(ROOT_DIR, 'backend/.env'))
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
RISK_FACTORS_PATH = os.path.join(ROOT_DIR, 'Excel_Generator/Stats_data_collection/risk_factors.json')
DB_PATH = os.path.join(ROOT_DIR, 'Database/ChromaMarket')
def get_market_context():
if not os.path.exists(RISK_FACTORS_PATH):
return []
with open(RISK_FACTORS_PATH, 'r') as f:
data = json.load(f)
chunks = []
# 1. Strategic Analysis
if data.get("llm_strategic_analysis"):
chunks.append(f"Strategic Analysis: {data['llm_strategic_analysis']}")
# 2. Data Snapshot (Prices)
snap = data.get("data_snapshot", {})
price_info = "Market Prices Snapshot:\n"
for k, v in snap.items():
if isinstance(v, (int, float)):
price_info += f"- {k.replace('_', ' ').title()}: {v}\n"
chunks.append(price_info)
# 3. Regional Prices
for key in ["tpa_regions", "eg_regions"]:
if key in snap:
region_info = f"Regional Prices ({key.replace('_', ' ').title()}):\n"
for r in snap[key]:
region_info += f"- {r['region']}: {r['price']} ({r['change']})\n"
chunks.append(region_info)
# 4. Alerts
alerts = data.get("alerts", [])
if alerts:
alert_info = "Active Market Alerts:\n"
for a in alerts:
alert_info += f"- [{a.get('severity', 'INFO')}] {a.get('title')}: {a.get('message')}\n"
chunks.append(alert_info)
return chunks
def init_rag():
try:
client = chromadb.PersistentClient(path=DB_PATH)
collection = client.get_or_create_collection(name="market_intelligence")
chunks = get_market_context()
if not chunks:
return collection, False
collection.add(
documents=chunks,
ids=[f"chunk_{i}" for i in range(len(chunks))]
)
return collection, True
except Exception as e:
print(f"⚠️ [RAG] Vector DB initialization failed (likely download timeout). Falling back to keyword search.")
return None, False
def manual_retrieval(question, chunks):
"""Simple keyword-based retrieval as fallback for when embeddings fail."""
# Score chunks based on word overlap
q_words = set(question.lower().split())
scored = []
for chunk in chunks:
c_words = set(chunk.lower().split())
score = len(q_words.intersection(c_words))
scored.append((score, chunk))
# Sort by score descending
scored.sort(key=lambda x: x[0], reverse=True)
return [s[1] for s in scored[:3]]
def query_market(question):
if not GROQ_API_KEY:
return "Error: GROQ_API_KEY not found in environment."
chunks = get_market_context()
collection, is_vector_active = init_rag()
context_chunks = []
if is_vector_active and collection:
try:
# Search for top 3 relevant chunks via vector
results = collection.query(
query_texts=[question],
n_results=3
)
context_chunks = results['documents'][0]
except Exception:
context_chunks = manual_retrieval(question, chunks)
else:
context_chunks = manual_retrieval(question, chunks)
context = "\n---\n".join(context_chunks)
client = Groq(api_key=GROQ_API_KEY)
# Use the requested model with a fallback if it doesn't exist
model_name = "meta-llama/llama-4-scout-17b-16e-instruct"
try:
completion = client.chat.completions.create(
model=model_name,
messages=[
{
"role": "system",
"content": f"You are a Textile Market Expert. Provide a direct, data-rich answer based on the context below. \n\nFORMATTING RULES:\n- DO NOT USE ASTERISKS (**) for bolding. Use ALL CAPS for headers instead.\n- Use a clean vertical list (one item per line).\n- Use double newlines between main points for scannability.\n- Omit all introductory fluff and concluding notes.\n- If comparing prices, use a vertical list format.\n\nCONTEXT:\n{context}"
},
{
"role": "user",
"content": question
}
],
temperature=0.5,
max_tokens=400,
top_p=1,
stream=False # Non-streaming for CLI output
)
return completion.choices[0].message.content
except Exception as e:
# Fallback to a guaranteed working model if the requested one is unavailable
if "404" in str(e) or "not found" in str(e).lower():
completion = client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[
{
"role": "system",
"content": f"You are a Textile Market Expert. Provide a direct, data-rich answer based on the context below. \n\nFORMATTING RULES:\n- DO NOT USE ASTERISKS (**) for bolding. Use ALL CAPS for headers instead.\n- Use a clean vertical list (one item per line).\n- Use double newlines between main points for scannability.\n- Omit all introductory fluff and concluding notes.\n\nCONTEXT:\n{context}"
},
{
"role": "user",
"content": question
}
],
temperature=0.5,
max_tokens=400
)
return completion.choices[0].message.content
return f"Error: {str(e)}"
if __name__ == "__main__":
query = ""
# 1. Check stdin (preferred for runPythonCli)
if not sys.stdin.isatty():
try:
raw_input = sys.stdin.read().strip()
if raw_input:
payload = json.loads(raw_input)
# payload could be [question] or {"question": "..."}
if isinstance(payload, list) and len(payload) > 0:
query = payload[0]
elif isinstance(payload, dict):
query = payload.get("question") or payload.get("feedback") or ""
except:
pass
# 2. Fallback to sys.argv
if not query and len(sys.argv) > 1:
query = " ".join(sys.argv[1:])
if query:
ans = query_market(query)
print(json.dumps({"response": ans}))
else:
print(json.dumps({"error": "No question provided"}))