qa1145 commited on
Commit
6784fde
·
verified ·
1 Parent(s): 748f7ac

Upload 9 files

Browse files
Files changed (1) hide show
  1. app.py +71 -66
app.py CHANGED
@@ -2,37 +2,23 @@ import gradio as gr
2
  from fastapi import FastAPI, Request, HTTPException
3
  from fastapi.responses import JSONResponse, StreamingResponse
4
  from pydantic import BaseModel
5
- from typing import List, Optional
6
  import asyncio
7
  import random
8
  from datetime import datetime
9
  import threading
10
  import uvicorn
11
  import json
 
12
 
13
  from src.config import get_api_keys
14
  from src.model_tester import ModelTester
15
  from src.scheduler import Scheduler
16
- from src.utils import format_timestamp
17
 
18
 
19
  model_tester = ModelTester()
20
  scheduler = Scheduler(task_callback=lambda: model_tester.scan_all_models())
21
 
22
-
23
- class Message(BaseModel):
24
- role: str
25
- content: str
26
-
27
-
28
- class ChatCompletionRequest(BaseModel):
29
- model: Optional[str] = None
30
- messages: List[Message]
31
- temperature: Optional[float] = 1.0
32
- max_tokens: Optional[int] = None
33
- stream: Optional[bool] = False
34
-
35
-
36
  fastapi_app = FastAPI(title="OpenRouter Free API")
37
 
38
 
@@ -54,48 +40,85 @@ async def list_models():
54
  "object": "model",
55
  "created": 1677610602,
56
  "owned_by": "openrouter",
57
- "permission": [],
58
- "root": model_id,
59
- "parent": None,
60
  "free": is_free
61
  })
62
 
63
- return {
64
- "object": "list",
65
- "data": models
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
 
 
 
 
 
 
 
 
 
67
 
68
 
69
  @fastapi_app.post("/v1/chat/completions")
70
- async def chat_completions(request: ChatCompletionRequest):
71
- prompt = request.messages[-1].content if request.messages else ""
72
- model_hint = request.model
 
73
 
74
- if request.stream:
75
  return StreamingResponse(
76
- stream_chat(request.model, [{"role": m.role, "content": m.content} for m in request.messages]),
77
  media_type="text/event-stream"
78
  )
79
 
80
- result = await model_tester.chat_completion(prompt, model_hint)
 
 
 
81
 
82
- if not result.get("success"):
83
- raise HTTPException(status_code=400, detail=result.get("error", "Request failed"))
84
 
85
- # 直接返回原始响应
86
- return result.get("response", {})
87
-
88
-
89
- async def stream_chat(model_hint: Optional[str], messages: list):
90
- try:
91
- async for chunk in model_tester.chat_completion_stream(model_hint, messages):
92
- if isinstance(chunk, bytes):
93
- yield chunk.decode('utf-8')
94
- else:
95
- yield chunk
96
- except Exception as e:
97
- yield f"data: {{\"error\": \"{str(e)}\"}}\n\n"
98
- yield "data: [DONE]\n\n"
99
 
100
 
101
  @fastapi_app.get("/health")
@@ -115,8 +138,7 @@ def get_scan_status():
115
  scan_result = model_tester.scan_result
116
  total = scan_result.get("total_available", 0)
117
  free = scan_result.get("free_available", 0)
118
- timestamp = scan_result.get("timestamp", "Never")
119
- return f"Free: {free} | Total: {total} | Last: {timestamp}"
120
 
121
 
122
  def format_model_list(models):
@@ -127,28 +149,11 @@ with gr.Blocks(title="OpenRouter Free API") as demo:
127
  gr.Markdown("# OpenRouter Free API")
128
  gr.Markdown("Standard OpenAI-compatible API with free model support")
129
 
130
- gr.Markdown("## API Endpoints")
131
- gr.Markdown("""
132
- - `GET /v1/models` - List available models
133
- - `POST /v1/chat/completions` - Chat completion
134
- """)
135
-
136
- gr.Markdown("---")
137
- gr.Markdown(f"## Status: {get_scan_status()}")
138
 
139
  gr.Markdown("## Available Free Models")
140
- free_display = gr.Textbox(
141
- value=format_model_list(get_available_free_models()),
142
- lines=15,
143
- interactive=False
144
- )
145
-
146
- gr.Markdown("## All Available Models")
147
- all_display = gr.Textbox(
148
- value=format_model_list(get_available_models()),
149
- lines=15,
150
- interactive=False
151
- )
152
 
153
 
154
  app = gr.mount_gradio_app(fastapi_app, demo, path="/")
 
2
  from fastapi import FastAPI, Request, HTTPException
3
  from fastapi.responses import JSONResponse, StreamingResponse
4
  from pydantic import BaseModel
5
+ from typing import List, Optional, Union
6
  import asyncio
7
  import random
8
  from datetime import datetime
9
  import threading
10
  import uvicorn
11
  import json
12
+ import aiohttp
13
 
14
  from src.config import get_api_keys
15
  from src.model_tester import ModelTester
16
  from src.scheduler import Scheduler
 
17
 
18
 
19
  model_tester = ModelTester()
20
  scheduler = Scheduler(task_callback=lambda: model_tester.scan_all_models())
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  fastapi_app = FastAPI(title="OpenRouter Free API")
23
 
24
 
 
40
  "object": "model",
41
  "created": 1677610602,
42
  "owned_by": "openrouter",
 
 
 
43
  "free": is_free
44
  })
45
 
46
+ return {"object": "list", "data": models}
47
+
48
+
49
+ async def proxy_request(model_hint: Optional[str], messages: list, stream: bool):
50
+ """透传请求到OpenRouter,只修改model"""
51
+ api_keys = get_api_keys()
52
+ api_key = random.choice(api_keys)
53
+
54
+ # 找到可用的模型ID
55
+ model_tester.refresh_model_list()
56
+ available_free = model_tester.get_all_free_models()
57
+
58
+ target_model = None
59
+
60
+ if model_hint:
61
+ # 尝试匹配用户指定的模型
62
+ for m in available_free:
63
+ model_name = m.replace(":free", "").split("/")[-1]
64
+ if model_hint.lower() in model_name.lower():
65
+ target_model = m
66
+ break
67
+ if not target_model:
68
+ target_model = f"{model_hint}:free" if ":free" not in model_hint else model_hint
69
+
70
+ if not target_model and available_free:
71
+ target_model = random.choice(available_free[:5])
72
+
73
+ if not target_model:
74
+ raise HTTPException(status_code=400, detail="No available model")
75
+
76
+ # 构建请求
77
+ url = "https://openrouter.ai/api/v1/chat/completions"
78
+ headers = {
79
+ "Authorization": f"Bearer {api_key}",
80
+ "Content-Type": "application/json"
81
+ }
82
+ payload = {
83
+ "model": target_model,
84
+ "messages": messages,
85
+ "stream": stream
86
  }
87
+
88
+ async with aiohttp.ClientSession() as session:
89
+ async with session.post(url, json=payload, headers=headers, timeout=aiohttp.ClientTimeout(total=120)) as response:
90
+ if stream:
91
+ async for chunk in response.content:
92
+ yield chunk
93
+ else:
94
+ data = await response.json()
95
+ yield data
96
 
97
 
98
  @fastapi_app.post("/v1/chat/completions")
99
+ async def chat_completions(request: Request):
100
+ body = await request.json()
101
+ stream = body.get("stream", False)
102
+ messages = body.get("messages", [])
103
 
104
+ if stream:
105
  return StreamingResponse(
106
+ proxy_request(body.get("model"), messages, stream),
107
  media_type="text/event-stream"
108
  )
109
 
110
+ result = None
111
+ async for data in proxy_request(body.get("model"), messages, stream):
112
+ result = data
113
+ break
114
 
115
+ if not result:
116
+ raise HTTPException(status_code=400, detail="Request failed")
117
 
118
+ if "error" in result:
119
+ raise HTTPException(status_code=400, detail=result["error"])
120
+
121
+ return result
 
 
 
 
 
 
 
 
 
 
122
 
123
 
124
  @fastapi_app.get("/health")
 
138
  scan_result = model_tester.scan_result
139
  total = scan_result.get("total_available", 0)
140
  free = scan_result.get("free_available", 0)
141
+ return f"Free: {free} | Total: {total}"
 
142
 
143
 
144
  def format_model_list(models):
 
149
  gr.Markdown("# OpenRouter Free API")
150
  gr.Markdown("Standard OpenAI-compatible API with free model support")
151
 
152
+ gr.Markdown("## Status")
153
+ gr.Markdown(f"**{get_scan_status()}**")
 
 
 
 
 
 
154
 
155
  gr.Markdown("## Available Free Models")
156
+ gr.Textbox(value=format_model_list(get_available_free_models()), lines=15, interactive=False)
 
 
 
 
 
 
 
 
 
 
 
157
 
158
 
159
  app = gr.mount_gradio_app(fastapi_app, demo, path="/")