Spaces:
Paused
Paused
| #!/usr/bin/env python3 | |
| """ | |
| SmilyAI Proxy β runs on a HF Space. | |
| Forwards authenticated requests to a private HF Inference Endpoint. | |
| Exists because PythonAnywhere's free tier can't reach arbitrary outbound | |
| hosts, but *.hf.space IS whitelisted. | |
| """ | |
| import os | |
| import json | |
| import requests | |
| from flask import Flask, request, Response, jsonify | |
| from flask_cors import CORS | |
| app = Flask(__name__) | |
| CORS(app) | |
| # ββ Config (set these as SECRETS in the Space settings, not hardcoded!) ββββββ | |
| TARGET_BASE_URL = os.environ.get("TARGET_BASE_URL", "").rstrip("/") | |
| HF_TOKEN = os.environ.get("HF_TOKEN", "") | |
| PROXY_SECRET = os.environ.get("PROXY_SECRET", "") # shared secret w/ your Flask app | |
| if not TARGET_BASE_URL or not HF_TOKEN or not PROXY_SECRET: | |
| print("β WARNING: TARGET_BASE_URL / HF_TOKEN / PROXY_SECRET not fully set!") | |
| def check_auth(req) -> bool: | |
| key = req.headers.get("X-Proxy-Key", "") | |
| return PROXY_SECRET and key == PROXY_SECRET | |
| def health(): | |
| return jsonify({"status": "ok", "service": "smilyai-proxy"}) | |
| def chat_completions(): | |
| if not check_auth(request): | |
| return jsonify({"error": "unauthorized"}), 401 | |
| try: | |
| payload = request.get_json(force=True) | |
| except Exception: | |
| return jsonify({"error": "invalid json body"}), 400 | |
| is_stream = bool(payload.get("stream", False)) | |
| headers = { | |
| "Authorization": f"Bearer {HF_TOKEN}", | |
| "Content-Type": "application/json; charset=utf-8", # β add charset here | |
| } | |
| url = f"{TARGET_BASE_URL}/chat/completions" | |
| if not is_stream: | |
| try: | |
| r = requests.post(url, headers=headers, json=payload, timeout=120) | |
| return Response( | |
| r.content, | |
| status=r.status_code, | |
| content_type="application/json; charset=utf-8" # β fix here too | |
| ) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 502 | |
| def generate(): | |
| try: | |
| with requests.post( | |
| url, headers=headers, json=payload, | |
| stream=True, timeout=120 | |
| ) as r: | |
| # β KEY FIX: iter_lines with encoding="utf-8" explicitly | |
| for raw_line in r.iter_lines(decode_unicode=False): | |
| if raw_line is None: | |
| continue | |
| # decode bytes as utf-8 explicitly | |
| if isinstance(raw_line, bytes): | |
| line = raw_line.decode("utf-8", errors="replace") | |
| else: | |
| line = raw_line | |
| if not line: | |
| continue | |
| yield (line + "\n\n").encode("utf-8") | |
| except Exception as e: | |
| err = json.dumps({"error": str(e)}, ensure_ascii=False) | |
| yield f"data: {err}\n\n".encode("utf-8") | |
| yield b"data: [DONE]\n\n" | |
| return Response( | |
| generate(), | |
| content_type="text/event-stream; charset=utf-8", # β charset here | |
| headers={ | |
| "Cache-Control": "no-cache", | |
| "X-Accel-Buffering": "no", | |
| "Transfer-Encoding": "chunked", | |
| } | |
| ) | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860))) |