radison / app.py
Hiren122's picture
Update app.py
5b541fd verified
import os
import json
import time
import http.client
from urllib.parse import urlparse
from flask import Flask, request, Response
app = Flask(__name__)
# ================= RADISON / PUTER CONFIG =================
RADISON_BASE_URL = "https://radison.yourdomain.com" # <-- change
RADISON_PATH = "/drivers/call"
RADISON_INTERFACE = "puter-chat-completion"
# 🔑 HARD-CODED API KEYS (ROTATION ENABLED)
RADISON_API_KEYS = [
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0IjoicyIsInYiOiIwLjAuMCIsInUiOiJXQXdENGpyalQ5ZWVBK2hyVEI3NjRBPT0iLCJ1dSI6ImZnNHlwTGtuVGlTS0VteGpmd01ucXc9PSIsImlhdCI6MTc2OTY5NTM4Nn0.r6YwsvMAImdWa8jL6YqplQ4RpyC5x-6N5jQefSaG5Ck",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0IjoicyIsInYiOiIwLjAuMCIsInUiOiJQMUozSWdSbFFrcUFHcUJnR0hNcm9nPT0iLCJ1dSI6IlZ1Zi9PYzY0VEo2Smtic3JSZmMzTlE9PSIsImlhdCI6MTc2OTY5NTUxM30.FqLIlwHnsSY-DcySQrPuZwfTzHCWiz-6MR-n21nLk58",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0IjoicyIsInYiOiIwLjAuMCIsInUiOiJ6Ykk0Y0NZQ1RvS0IyUzY4VGdGSnR3PT0iLCJ1dSI6Im5Uc0tMajVLUUxlTTlxOHMwVGxmT0E9PSIsImlhdCI6MTc3MDMwMDIyMH0.0xKV3xO7jHwcBEW_7gSwIByiDIVLI7VUlEdodANKzpI",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0IjoicyIsInYiOiIwLjAuMCIsInUiOiJFUnlnYjNoWFFpZTh3cUhZNjhOaElBPT0iLCJ1dSI6Ind3bFZmbHRCVENXUWRpeXFlTjE4YlE9PSIsImlhdCI6MTc3MDMwMDQwMH0.yDbtKux3BhzPlCuFpVD5Cqmoqo3z5k-ED4zFcposTng"
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0IjoicyIsInYiOiIwLjAuMCIsInUiOiJDd3pZRFAvUVNOSzZEQ3FQcUNacGFnPT0iLCJ1dSI6IjZzZEJaN0JFVFRTaWdlL1ZXeWZDV2c9PSIsImlhdCI6MTc3MDMwMDk4MH0.7Xl_Pj1OoLiiJb-keRgAIMYQhKjbBdC7HdRgUcAsyNA"
]
if not RADISON_BASE_URL or not RADISON_API_KEYS:
raise RuntimeError("RADISON config missing")
# ================= KEY ROTATOR =================
class KeyRotator:
def __init__(self, keys):
self.keys = keys
self.index = 0
def next(self):
key = self.keys[self.index]
self.index = (self.index + 1) % len(self.keys)
return key
key_rotator = KeyRotator(RADISON_API_KEYS)
# ---------------- normalize messages (OpenAI → Puter) ----------------
def normalize_messages(messages):
out = []
for m in messages:
role = m.get("role", "user")
content = m.get("content", "")
if isinstance(content, list):
out.append({"role": role, "content": content})
else:
out.append({
"role": role,
"content": [{"type": "text", "text": str(content)}]
})
return out
# ---------------- flatten structured content (Puter → OpenAI UI) ----------------
def flatten_content(content):
if isinstance(content, list):
texts = []
for c in content:
if isinstance(c, dict) and c.get("type") == "text":
texts.append(c.get("text", ""))
return "".join(texts)
return content or ""
# ---------------- call Radison with key rotation ----------------
def call_radison_raw(model, messages, extra_args, retries=3):
parsed = urlparse(RADISON_BASE_URL)
last_error = None
for _ in range(retries):
api_key = key_rotator.next()
try:
conn = http.client.HTTPSConnection(parsed.netloc, timeout=120)
payload = {
"interface": RADISON_INTERFACE,
"driver": "ai-chat",
"method": "complete",
"args": {
"model": model,
"messages": messages,
**extra_args
},
"auth_token": api_key
}
headers = {
"Content-Type": "application/json",
"Origin": "https://puter.com",
"Referer": "https://puter.com",
"User-Agent": "Mozilla/5.0"
}
conn.request("POST", RADISON_PATH, json.dumps(payload), headers)
res = conn.getresponse()
raw = res.read().decode("utf-8", errors="ignore")
conn.close()
# rotate key on auth / rate limit
if res.status in (401, 403, 429):
last_error = raw
continue
return json.loads(raw)
except Exception as e:
last_error = str(e)
continue
raise RuntimeError(f"Radison failed after key rotation: {last_error}")
# ================= CHAT COMPLETIONS =================
@app.route("/v1/chat/completions", methods=["POST"])
def chat_completions():
body = request.json or {}
messages = body.get("messages")
model = body.get("model")
stream = body.get("stream", False)
if not messages or not model:
return Response(
json.dumps({"error": "model and messages are required"}),
status=400,
mimetype="application/json"
)
passthrough = {
k: v for k, v in body.items()
if k not in {"messages", "model", "stream"}
}
radison_msgs = normalize_messages(messages)
radison_resp = call_radison_raw(model, radison_msgs, passthrough)
result = radison_resp.get("result", {})
msg = result.get("message", {})
finish_reason = result.get("finish_reason", "stop")
usage = result.get("usage", {})
# ================= NON-STREAM =================
if not stream:
openai_message = {
"role": "assistant",
"content": flatten_content(msg.get("content"))
}
if msg.get("tool_calls"):
openai_message["tool_calls"] = msg["tool_calls"]
finish_reason = "tool_calls"
response = {
"id": "chatcmpl-radison",
"object": "chat.completion",
"created": int(time.time()),
"model": model,
"choices": [
{
"index": 0,
"message": openai_message,
"finish_reason": finish_reason
}
],
"usage": {
"prompt_tokens": usage.get("prompt_tokens", 0),
"completion_tokens": usage.get("completion_tokens", 0),
"total_tokens": usage.get("prompt_tokens", 0)
+ usage.get("completion_tokens", 0)
}
}
return Response(
json.dumps(response),
status=200,
mimetype="application/json"
)
# ================= STREAM =================
def generate():
content = flatten_content(msg.get("content"))
for token in content.split(" "):
chunk = {
"id": "chatcmpl-radison",
"object": "chat.completion.chunk",
"choices": [
{
"index": 0,
"delta": {"content": token + " "}
}
]
}
yield f"data: {json.dumps(chunk)}\n\n"
time.sleep(0.015)
yield "data: [DONE]\n\n"
return Response(generate(), mimetype="text/event-stream")
# ================= RUN =================
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)