qa1145 commited on
Commit
eff2c28
·
verified ·
1 Parent(s): 2a464ca

Upload 9 files

Browse files
Files changed (3) hide show
  1. app.py +121 -127
  2. requirements.txt +3 -0
  3. src/model_tester.py +12 -3
app.py CHANGED
@@ -1,170 +1,164 @@
1
  import gradio as gr
2
- import threading
 
 
 
 
 
3
  from datetime import datetime
 
 
4
 
5
- from src.config import get_api_keys, get_scan_interval_hours
6
  from src.model_tester import ModelTester
7
  from src.scheduler import Scheduler
8
  from src.utils import format_timestamp
9
 
10
 
11
- class AppState:
12
- def __init__(self):
13
- self.model_tester = ModelTester()
14
- self.scheduler = Scheduler(task_callback=self.run_scan)
15
- self.scan_result = {
16
- "available_models": [],
17
- "available_free_models": [],
18
- "total_available": 0,
19
- "free_available": 0,
20
- "timestamp": None
21
- }
22
- self.last_update_time = None
23
- self._initial_scan_done = False
24
-
25
- def run_scan(self):
26
- result = self.model_tester.scan_all_models()
27
- self.scan_result = result
28
- self.last_update_time = format_timestamp()
29
- self._initial_scan_done = True
30
 
31
 
32
- app_state = AppState()
 
 
33
 
34
 
35
- def get_available_models():
36
- models = app_state.scan_result.get("available_models", [])
37
- return "\n".join(models) if models else "No models available yet"
 
 
 
38
 
39
 
40
- def get_available_free_models():
41
- models = app_state.scan_result.get("available_free_models", [])
42
- return "\n".join(models) if models else "No free models available yet"
43
 
44
 
45
- def get_models_api(free_only: bool = False):
46
- """API endpoint to get available models as JSON"""
47
- if free_only:
48
- return app_state.scan_result.get("available_free_models", [])
49
- return app_state.scan_result.get("available_models", [])
50
 
51
 
52
- def scan_and_get_models():
53
- """Trigger scan and return models"""
54
- app_state.scheduler.run_now()
55
- import time
56
- time.sleep(2)
57
- return app_state.scan_result.get("available_models", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
 
60
- def chat(prompt: str, model: str = None):
61
- """Chat endpoint - tries direct model first, then falls back to list match"""
62
- result = app_state.model_tester.chat_completion_sync(prompt, model)
 
63
 
64
- if result.get("success"):
65
- return {
66
- "success": True,
67
- "response": result.get("response"),
68
- "method": result.get("method"),
69
- "model": result.get("model")
70
- }
71
- else:
72
- return {
73
- "success": False,
74
- "error": result.get("error", "Unknown error"),
75
- "method": result.get("method")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
 
77
 
78
 
79
- def get_scan_status():
80
- total = app_state.scan_result.get("total_available", 0)
81
- free = app_state.scan_result.get("free_available", 0)
82
- timestamp = app_state.last_update_time or "Never"
83
- return f"Free: {free} | Total: {total} | Last scan: {timestamp}"
84
-
85
-
86
- def trigger_scan():
87
- app_state.scheduler.run_now()
88
- return "Scan triggered! Results will update shortly..."
89
 
90
 
91
- def get_api_keys_status():
92
- try:
93
- keys = get_api_keys()
94
- return f"{len(keys)} keys configured"
95
- except ValueError as e:
96
- return f"Error: {str(e)}"
97
 
98
 
99
- def get_interval_info():
100
- return f"Scan interval: every {get_scan_interval_hours()} hour(s)"
101
 
102
 
103
- with gr.Blocks(title="OpenRouter Free Models Scanner") as demo:
104
- gr.Markdown("# OpenRouter Free Models Scanner")
105
- gr.Markdown("Auto-scan all available models from OpenRouter (free priority)")
 
 
 
106
 
107
- with gr.Row():
108
- gr.Markdown(f"### API Keys: {get_api_keys_status()}")
109
- gr.Markdown(f"### {get_interval_info()}")
110
 
111
- gr.Markdown("---")
 
112
 
113
- with gr.Row():
114
- gr.Markdown("## Status")
115
- status_display = gr.Textbox(
116
- value=get_scan_status(),
117
- label="Scan Status",
118
- interactive=False
119
- )
120
-
121
- with gr.Row():
122
- scan_btn = gr.Button("Trigger Scan Now", variant="primary")
123
- scan_result = gr.Textbox(
124
- value="Click button to trigger scan",
125
- label="Scan Result",
126
- interactive=False
127
- )
128
-
129
- scan_btn.click(
130
- fn=trigger_scan,
131
- inputs=[],
132
- outputs=[scan_result]
133
- )
134
 
 
 
 
 
 
 
 
 
 
 
135
  gr.Markdown("---")
136
- gr.Markdown("## Available Free Models (Priority)")
137
-
138
- free_models_display = gr.Textbox(
139
- value=get_available_free_models(),
140
- label=f"Free Models ({len(app_state.scan_result.get('available_free_models', []))})",
141
- interactive=False,
142
- lines=15
143
  )
144
-
145
  gr.Markdown("## All Available Models")
146
-
147
- models_display = gr.Textbox(
148
- value=get_available_models(),
149
- label=f"All Models ({len(app_state.scan_result.get('available_models', []))})",
150
- interactive=False,
151
- lines=15
152
- )
153
-
154
- demo.load(
155
- fn=lambda: (
156
- get_scan_status(),
157
- get_available_free_models(),
158
- get_available_models()
159
- ),
160
- outputs=[status_display, free_models_display, models_display]
161
  )
162
 
163
- app_state.scheduler.start()
164
 
165
- gr.API(documentation=None)(lambda free_only=False: get_models_api(free_only))
166
- gr.API(documentation=None)(chat)
167
 
168
 
169
  if __name__ == "__main__":
170
- demo.launch()
 
1
  import gradio as gr
2
+ from fastapi import FastAPI, Request, HTTPException
3
+ from fastapi.responses import JSONResponse
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
 
12
+ from src.config import get_api_keys
13
  from src.model_tester import ModelTester
14
  from src.scheduler import Scheduler
15
  from src.utils import format_timestamp
16
 
17
 
18
+ model_tester = ModelTester()
19
+ scheduler = Scheduler(task_callback=lambda: model_tester.scan_all_models())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
 
22
+ class Message(BaseModel):
23
+ role: str
24
+ content: str
25
 
26
 
27
+ class ChatCompletionRequest(BaseModel):
28
+ model: Optional[str] = None
29
+ messages: List[Message]
30
+ temperature: Optional[float] = 1.0
31
+ max_tokens: Optional[int] = None
32
+ stream: Optional[bool] = False
33
 
34
 
35
+ fastapi_app = FastAPI(title="OpenRouter Free API")
 
 
36
 
37
 
38
+ @fastapi_app.on_event("startup")
39
+ async def startup_event():
40
+ threading.Thread(target=lambda: scheduler.start(), daemon=True).start()
 
 
41
 
42
 
43
+ @fastapi_app.get("/v1/models")
44
+ async def list_models():
45
+ available = model_tester.get_available_models(free_only=False)
46
+ available_free = model_tester.get_available_models(free_only=True)
47
+
48
+ models = []
49
+ for model_id in available:
50
+ is_free = model_id in available_free
51
+ models.append({
52
+ "id": model_id,
53
+ "object": "model",
54
+ "created": 1677610602,
55
+ "owned_by": "openrouter",
56
+ "permission": [],
57
+ "root": model_id,
58
+ "parent": None,
59
+ "free": is_free
60
+ })
61
+
62
+ return {
63
+ "object": "list",
64
+ "data": models
65
+ }
66
 
67
 
68
+ @fastapi_app.post("/v1/chat/completions")
69
+ async def chat_completions(request: ChatCompletionRequest):
70
+ prompt = request.messages[-1].content if request.messages else ""
71
+ model_hint = request.model
72
 
73
+ result = await model_tester.chat_completion(prompt, model_hint)
74
+
75
+ if not result.get("success"):
76
+ raise HTTPException(status_code=400, detail=result.get("error", "Request failed"))
77
+
78
+ response_data = result.get("response", {})
79
+
80
+ content = ""
81
+ if "choices" in response_data and response_data["choices"]:
82
+ content = response_data["choices"][0].get("message", {}).get("content", "")
83
+
84
+ return {
85
+ "id": response_data.get("id", f"chatcmpl-{random.randint(100000, 999999)}"),
86
+ "object": "chat.completion",
87
+ "created": int(datetime.now().timestamp()),
88
+ "model": result.get("model", request.model or "unknown"),
89
+ "choices": [
90
+ {
91
+ "index": 0,
92
+ "message": {
93
+ "role": "assistant",
94
+ "content": content
95
+ },
96
+ "finish_reason": "stop"
97
+ }
98
+ ],
99
+ "usage": {
100
+ "prompt_tokens": len(prompt),
101
+ "completion_tokens": 10,
102
+ "total_tokens": len(prompt) + 10
103
  }
104
+ }
105
 
106
 
107
+ @fastapi_app.get("/health")
108
+ async def health():
109
+ return {"status": "ok"}
 
 
 
 
 
 
 
110
 
111
 
112
+ def get_available_models():
113
+ return model_tester.get_available_models(free_only=False)
 
 
 
 
114
 
115
 
116
+ def get_available_free_models():
117
+ return model_tester.get_available_models(free_only=True)
118
 
119
 
120
+ def get_scan_status():
121
+ scan_result = model_tester.scan_result
122
+ total = scan_result.get("total_available", 0)
123
+ free = scan_result.get("free_available", 0)
124
+ timestamp = scan_result.get("timestamp", "Never")
125
+ return f"Free: {free} | Total: {total} | Last: {timestamp}"
126
 
 
 
 
127
 
128
+ def format_model_list(models):
129
+ return "\n".join(models) if models else "No models available"
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
+ with gr.Blocks(title="OpenRouter Free API") as demo:
133
+ gr.Markdown("# OpenRouter Free API")
134
+ gr.Markdown("Standard OpenAI-compatible API with free model support")
135
+
136
+ gr.Markdown("## API Endpoints")
137
+ gr.Markdown("""
138
+ - `GET /v1/models` - List available models
139
+ - `POST /v1/chat/completions` - Chat completion
140
+ """)
141
+
142
  gr.Markdown("---")
143
+ gr.Markdown(f"## Status: {get_scan_status()}")
144
+
145
+ gr.Markdown("## Available Free Models")
146
+ free_display = gr.Textbox(
147
+ value=format_model_list(get_available_free_models()),
148
+ lines=15,
149
+ interactive=False
150
  )
151
+
152
  gr.Markdown("## All Available Models")
153
+ all_display = gr.Textbox(
154
+ value=format_model_list(get_available_models()),
155
+ lines=15,
156
+ interactive=False
 
 
 
 
 
 
 
 
 
 
 
157
  )
158
 
 
159
 
160
+ app = gr.mount_gradio_app(fastapi_app, demo, path="/")
 
161
 
162
 
163
  if __name__ == "__main__":
164
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt CHANGED
@@ -3,3 +3,6 @@ requests
3
  python-dotenv
4
  schedule
5
  aiohttp
 
 
 
 
3
  python-dotenv
4
  schedule
5
  aiohttp
6
+ fastapi
7
+ uvicorn
8
+ sse-starlette
src/model_tester.py CHANGED
@@ -21,6 +21,13 @@ class ModelTester:
21
  self._available_free_models: List[str] = []
22
  self._scan_in_progress = False
23
  self._last_scan_time: Optional[datetime] = None
 
 
 
 
 
 
 
24
 
25
  def refresh_model_list(self):
26
  """Get latest model list from API"""
@@ -106,15 +113,17 @@ class ModelTester:
106
  self._last_scan_time = datetime.now()
107
  self._scan_in_progress = False
108
 
109
- print(f"Scan complete: {len(self._available_free_models)} free, {len(self._available_models)} total available")
110
-
111
- return {
112
  "available_models": self._available_models,
113
  "available_free_models": self._available_free_models,
114
  "total_available": len(self._available_models),
115
  "free_available": len(self._available_free_models),
116
  "timestamp": self._last_scan_time.isoformat() if self._last_scan_time else None
117
  }
 
 
 
 
118
 
119
  def scan_all_models(self):
120
  """Sync wrapper for scan"""
 
21
  self._available_free_models: List[str] = []
22
  self._scan_in_progress = False
23
  self._last_scan_time: Optional[datetime] = None
24
+ self.scan_result: Dict[str, Any] = {
25
+ "available_models": [],
26
+ "available_free_models": [],
27
+ "total_available": 0,
28
+ "free_available": 0,
29
+ "timestamp": None
30
+ }
31
 
32
  def refresh_model_list(self):
33
  """Get latest model list from API"""
 
113
  self._last_scan_time = datetime.now()
114
  self._scan_in_progress = False
115
 
116
+ self.scan_result = {
 
 
117
  "available_models": self._available_models,
118
  "available_free_models": self._available_free_models,
119
  "total_available": len(self._available_models),
120
  "free_available": len(self._available_free_models),
121
  "timestamp": self._last_scan_time.isoformat() if self._last_scan_time else None
122
  }
123
+
124
+ print(f"Scan complete: {len(self._available_free_models)} free, {len(self._available_models)} total available")
125
+
126
+ return self.scan_result
127
 
128
  def scan_all_models(self):
129
  """Sync wrapper for scan"""