from flask import Flask, request, Response, stream_with_context import requests import json import uuid import time import random import os app = Flask(__name__) def get_token(): with open("token.txt", encoding="utf-8") as f: return random.choice(f.read().split("\n")) def create_merlin_request(openai_req): messages = openai_req['messages'] context_messages = [ f"{msg['role']}: {msg['content']}" for msg in messages[:-1] ] return { "attachments": [], "chatId": str(uuid.uuid1()), "language": "AUTO", "message": { "content": messages[-1]['content'], "context": "\n".join(context_messages), "childId": str(uuid.uuid4()), "id": str(uuid.uuid4()), "parentId": "root" }, "mode": "UNIFIED_CHAT", "model": openai_req.get('model', 'claude-3-haiku'), "metadata": { "largeContext": False, "merlinMagic": False, "proFinderMode": False, "webAccess": False } } def process_merlin_response(response_text): try: merlin_resp = json.loads(response_text[6:]) content = merlin_resp['data']['content'] if isinstance(content, str): content = content.encode('latin1').decode('utf-8') return content return content except Exception: return "" def process_non_stream_response(response): try: full_content = "" for line in response.text.split('\n'): if line.startswith('data: '): content = process_merlin_response(line) if content and content != " ": full_content += content if not full_content: return Response("Empty response from server", status=500) return { "id": str(uuid.uuid4()), "object": "chat.completion", "created": int(time.time()), "model": "claude-3-haiku", "choices": [{ "message": { "role": "assistant", "content": full_content }, "finish_reason": "stop", "index": 0 }] } except Exception as e: print(f"Error processing response: {str(e)}") return Response("Failed to process response", status=500) @app.route('/', methods=['GET']) def home(): return {"status": "GetMerlin2Api Service Running...", "message": "MoLoveSze..."} @app.route('/v1/chat/completions', methods=['POST']) def chat(): try: auth_token = os.getenv('AUTH_TOKEN') if auth_token: request_token = request.headers.get('Authorization', '') if request_token != f"Bearer {auth_token}": return Response("Unauthorized", status=401) token = get_token() if not token: return Response("Failed to get token", status=500) openai_req = request.json merlin_req = create_merlin_request(openai_req) headers = { "Content-Type": "application/json", "Accept": "text/event-stream", "Authorization": f"Bearer {token}", "x-merlin-version": "web-merlin", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "host": "arcane.getmerlin.in" } response = requests.post( "https://arcane.getmerlin.in/v1/thread/unified", json=merlin_req, headers=headers, stream=True ) if response.status_code != 200: return Response(f"Merlin API error: {response.text}", status=response.status_code) if not openai_req.get('stream', False): return process_non_stream_response(response) def generate(): try: buffer = "" for chunk in response.iter_content(chunk_size=1024, decode_unicode=True): if chunk: buffer += chunk while '\n' in buffer: line, buffer = buffer.split('\n', 1) if line.startswith('data: '): try: content = process_merlin_response(line) if content: openai_resp = { "id": str(uuid.uuid4()), "object": "chat.completion.chunk", "created": int(time.time()), "model": openai_req.get('model', 'claude-3-haiku'), "choices": [{ "delta": { "content": content }, "index": 0, "finish_reason": None }] } yield f"data: {json.dumps(openai_resp, ensure_ascii=False)}\n\n" except Exception as e: print(f"Error processing chunk: {str(e)}") continue final_resp = { "choices": [{ "delta": {"content": ""}, "index": 0, "finish_reason": "stop" }] } yield f"data: {json.dumps(final_resp)}\n\n" yield "data: [DONE]\n\n" except Exception as e: print(f"Error in generate: {str(e)}") return return Response( stream_with_context(generate()), content_type='text/event-stream' ) except Exception as e: print(f"Error in chat endpoint: {str(e)}") return Response(f"Internal server error: {str(e)}", status=500) if __name__ == '__main__': port = int(os.getenv('PORT', 7860)) # HF Spaces 默认端口 app.run(host='0.0.0.0', port=port)