Spaces:
Running
Running
| import os | |
| import pandas as pd | |
| from datetime import datetime | |
| from fastapi import FastAPI, HTTPException, BackgroundTasks | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from openai import AsyncOpenAI | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| app = FastAPI(title="PurePolyglot Hybrid Backend", version="1.0.0") | |
| # Enable CORS for the Vite SPA | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Attempt Qwen first, fallback to Groq | |
| QWEN_API_KEY = os.getenv("QWEN_API_KEY") | |
| QWEN_BASE_URL = os.getenv("QWEN_BASE_URL", "https://dashscope-intl.aliyuncs.com/compatible-mode/v1") | |
| QWEN_MODEL_NAME = os.getenv("QWEN_MODEL_NAME", "qwen3-coder-80b-instruct") | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY") | |
| if QWEN_API_KEY and QWEN_API_KEY != "your-api-key-here": | |
| client = AsyncOpenAI(api_key=QWEN_API_KEY, base_url=QWEN_BASE_URL) | |
| MODEL_NAME = QWEN_MODEL_NAME | |
| NODE_TYPE = "Qwen Hybrid Node" | |
| elif GROQ_API_KEY: | |
| client = AsyncOpenAI(api_key=GROQ_API_KEY, base_url="https://api.groq.com/openai/v1") | |
| MODEL_NAME = "llama-3.3-70b-versatile" | |
| NODE_TYPE = "Groq Hybrid Node" | |
| else: | |
| client = None | |
| MODEL_NAME = None | |
| NODE_TYPE = "Offline" | |
| class TranslationRequest(BaseModel): | |
| text: str | |
| source_language: str = "Unknown" | |
| source_dialect: str = "Standard" | |
| target_language: str | |
| target_dialect: str | |
| user_key: str = "Polyglot Player" | |
| class TranslationResponse(BaseModel): | |
| original_text: str | |
| translated_text: str | |
| target_dialect: str | |
| node: str | |
| def log_to_pending_queue(request: TranslationRequest, translation: str): | |
| """Background task to log translations to pending_approvals.csv""" | |
| pending_file = "/app/pending_approvals.csv" if os.path.exists("/app") else "pending_approvals.csv" | |
| new_entry = { | |
| "User": request.user_key, | |
| "Data_Origin": "Game: Polyglot Chat", | |
| "Utterance": request.text, | |
| "Dialect": request.target_dialect, | |
| "Clarification": translation, | |
| "Clarification_Source": NODE_TYPE, | |
| "Tone": "Neutral / Conversational", | |
| "Context": f"Translated from {request.source_language} ({request.source_dialect})", | |
| "Pragmatic_Analysis": "", | |
| "Audio": "", | |
| "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "Chain_ID": "", | |
| "Approvers": "", | |
| "Language": request.target_language | |
| } | |
| try: | |
| if os.path.exists(pending_file): | |
| df = pd.read_csv(pending_file) | |
| else: | |
| df = pd.DataFrame(columns=new_entry.keys()) | |
| df = pd.concat([df, pd.DataFrame([new_entry])], ignore_index=True) | |
| df.to_csv(pending_file, index=False) | |
| print(f"✅ Logged to {pending_file} for peer review.") | |
| # Sync to Hugging Face PureChain_Dataset if HF_TOKEN is present | |
| from huggingface_hub import HfApi | |
| hf_token = os.environ.get("HF_TOKEN") | |
| if hf_token: | |
| api = HfApi(token=hf_token) | |
| api.upload_file( | |
| path_or_fileobj=pending_file, | |
| path_in_repo="pending_approvals.csv", | |
| repo_id="toecm/PureChain_Dataset", | |
| repo_type="dataset", | |
| commit_message="🔄 Auto-sync: Polyglot Chat text translation added to pending approvals queue" | |
| ) | |
| print("☁️ Synced Polyglot Chat text translation entry to HF PureChain_Dataset.") | |
| except Exception as e: | |
| print(f"Failed to save translation to pending queue: {e}") | |
| async def translate_text(request: TranslationRequest, background_tasks: BackgroundTasks): | |
| if not client: | |
| raise HTTPException(status_code=500, detail="No LLM API key configured (neither Qwen nor Groq).") | |
| system_prompt = ( | |
| f"You are an expert polyglot interpreter specializing in deep cultural and linguistic dialects.\n" | |
| f"Translate the following text from {request.source_language} ({request.source_dialect}) " | |
| f"into {request.target_language} ({request.target_dialect}).\n" | |
| f"Output ONLY the raw translated string. Do not include quotes, explanations, or thinking traces." | |
| ) | |
| try: | |
| response = await client.chat.completions.create( | |
| model=MODEL_NAME, | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": request.text} | |
| ], | |
| temperature=0.3, | |
| max_tokens=256 | |
| ) | |
| translated_text = response.choices[0].message.content.strip() | |
| # Log to CSV in the background | |
| background_tasks.add_task(log_to_pending_queue, request, translated_text) | |
| return TranslationResponse( | |
| original_text=request.text, | |
| translated_text=translated_text, | |
| target_dialect=f"{request.target_language} ({request.target_dialect})", | |
| node=NODE_TYPE | |
| ) | |
| except Exception as e: | |
| print(f"Error calling {NODE_TYPE} API: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def root(): | |
| return {"message": f"PurePolyglot Hybrid Backend Online ({NODE_TYPE})"} | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True) | |