qa1145 commited on
Commit
3b8a72f
·
verified ·
1 Parent(s): 4c40bac

Upload 9 files

Browse files
Files changed (3) hide show
  1. app.py +35 -0
  2. requirements.txt +1 -0
  3. src/model_tester.py +142 -3
app.py CHANGED
@@ -33,6 +33,38 @@ def get_available_models():
33
  return "\n".join(models) if models else "No models available yet"
34
 
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  def get_scan_status():
37
  count = app_state.scan_result.get("available_count", 0)
38
  tested = app_state.scan_result.get("total_tested", 0)
@@ -109,6 +141,9 @@ with gr.Blocks(title="OpenRouter Free Models Scanner") as demo:
109
 
110
  app_state.scheduler.start()
111
 
 
 
 
112
 
113
  if __name__ == "__main__":
114
  demo.launch()
 
33
  return "\n".join(models) if models else "No models available yet"
34
 
35
 
36
+ def get_models_api():
37
+ """API endpoint to get available models as JSON"""
38
+ return app_state.scan_result.get("available_models", [])
39
+
40
+
41
+ def scan_and_get_models():
42
+ """Trigger scan and return models"""
43
+ app_state.scheduler.run_now()
44
+ import time
45
+ time.sleep(2)
46
+ return app_state.scan_result.get("available_models", [])
47
+
48
+
49
+ def chat(prompt: str, model: str = None):
50
+ """Chat endpoint - tries direct model first, then falls back to list match"""
51
+ result = app_state.model_tester.chat_completion_sync(prompt, model)
52
+
53
+ if result.get("success"):
54
+ return {
55
+ "success": True,
56
+ "response": result.get("response"),
57
+ "method": result.get("method"),
58
+ "model": result.get("model")
59
+ }
60
+ else:
61
+ return {
62
+ "success": False,
63
+ "error": result.get("error", "Unknown error"),
64
+ "method": result.get("method")
65
+ }
66
+
67
+
68
  def get_scan_status():
69
  count = app_state.scan_result.get("available_count", 0)
70
  tested = app_state.scan_result.get("total_tested", 0)
 
141
 
142
  app_state.scheduler.start()
143
 
144
+ gr.API(documentation=None)(get_models_api)
145
+ gr.API(documentation=None)(chat)
146
+
147
 
148
  if __name__ == "__main__":
149
  demo.launch()
requirements.txt CHANGED
@@ -2,3 +2,4 @@ gradio
2
  requests
3
  python-dotenv
4
  schedule
 
 
2
  requests
3
  python-dotenv
4
  schedule
5
+ aiohttp
src/model_tester.py CHANGED
@@ -1,6 +1,8 @@
1
- import concurrent.futures
2
- from typing import List, Dict, Any, Set
 
3
  from datetime import datetime
 
4
 
5
  from .openrouter_client import OpenRouterClient
6
  from . import config
@@ -12,6 +14,8 @@ class ModelTester:
12
  self.client = OpenRouterClient()
13
  self.max_concurrency = config.get_max_concurrency()
14
  self.test_prompt = config.get_test_prompt()
 
 
15
 
16
  def get_all_free_models(self) -> List[str]:
17
  models = self.client.get_models()
@@ -24,6 +28,140 @@ class ModelTester:
24
 
25
  return free_models
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  def test_single_model(self, model_id: str) -> tuple[str, bool]:
28
  is_available = self.client.test_model(model_id, self.test_prompt)
29
  cleaned_name = clean_model_name(model_id)
@@ -32,13 +170,14 @@ class ModelTester:
32
  def test_all_models(self) -> Dict[str, Any]:
33
  print(f"[{datetime.now()}] Starting model scan...")
34
 
35
- free_models = self.get_all_free_models()
36
  print(f"Found {len(free_models)} free models to test")
37
 
38
  available_models: Set[str] = set()
39
  tested_count = 0
40
  success_count = 0
41
 
 
42
  with concurrent.futures.ThreadPoolExecutor(
43
  max_workers=self.max_concurrency
44
  ) as executor:
 
1
+ import asyncio
2
+ import aiohttp
3
+ from typing import List, Dict, Any, Optional, Set
4
  from datetime import datetime
5
+ import random
6
 
7
  from .openrouter_client import OpenRouterClient
8
  from . import config
 
14
  self.client = OpenRouterClient()
15
  self.max_concurrency = config.get_max_concurrency()
16
  self.test_prompt = config.get_test_prompt()
17
+ self._cached_models: Optional[List[str]] = None
18
+ self._cache_time: Optional[datetime] = None
19
 
20
  def get_all_free_models(self) -> List[str]:
21
  models = self.client.get_models()
 
28
 
29
  return free_models
30
 
31
+ def get_cached_free_models(self, force_refresh: bool = False) -> List[str]:
32
+ if self._cached_models is None or force_refresh:
33
+ self._cached_models = self.get_all_free_models()
34
+ self._cache_time = datetime.now()
35
+ return self._cached_models
36
+
37
+ async def try_model_direct(
38
+ self,
39
+ session: aiohttp.ClientSession,
40
+ model_id: str,
41
+ api_key: str
42
+ ) -> Optional[Dict[str, Any]]:
43
+ url = "https://openrouter.ai/api/v1/chat/completions"
44
+ payload = {
45
+ "model": model_id,
46
+ "messages": [{"role": "user", "content": self.test_prompt}],
47
+ "max_tokens": 10
48
+ }
49
+ headers = {
50
+ "Authorization": f"Bearer {api_key}",
51
+ "Content-Type": "application/json"
52
+ }
53
+
54
+ try:
55
+ timeout = aiohttp.ClientTimeout(total=config.get_request_timeout())
56
+ async with session.post(url, json=payload, headers=headers, timeout=timeout) as response:
57
+ if response.status == 200:
58
+ data = await response.json()
59
+ return {
60
+ "success": True,
61
+ "model": model_id,
62
+ "response": data,
63
+ "method": "direct"
64
+ }
65
+ else:
66
+ return {
67
+ "success": False,
68
+ "model": model_id,
69
+ "error": f"HTTP {response.status}",
70
+ "method": "direct"
71
+ }
72
+ except asyncio.TimeoutError:
73
+ return {"success": False, "model": model_id, "error": "timeout", "method": "direct"}
74
+ except Exception as e:
75
+ return {"success": False, "model": model_id, "error": str(e), "method": "direct"}
76
+
77
+ async def try_models_from_list(
78
+ self,
79
+ session: aiohttp.ClientSession,
80
+ keyword: str,
81
+ api_key: str
82
+ ) -> Optional[Dict[str, Any]]:
83
+ free_models = self.get_cached_free_models()
84
+
85
+ matched = [m for m in free_models if keyword.lower() in m.lower()]
86
+
87
+ if not matched:
88
+ matched = free_models[:10]
89
+
90
+ for model_id in matched:
91
+ result = await self.try_model_direct(session, model_id, api_key)
92
+ if result and result.get("success"):
93
+ result["method"] = "list_match"
94
+ return result
95
+
96
+ return {
97
+ "success": False,
98
+ "model": matched[0] if matched else None,
99
+ "error": "No available model found",
100
+ "method": "list_match"
101
+ }
102
+
103
+ async def chat_completion(self, prompt: str, model_hint: Optional[str] = None) -> Dict[str, Any]:
104
+ api_keys = config.get_api_keys()
105
+ api_key = random.choice(api_keys)
106
+
107
+ timeout = aiohttp.ClientTimeout(total=config.get_request_timeout())
108
+
109
+ async with aiohttp.ClientSession() as session:
110
+ tasks = []
111
+
112
+ if model_hint:
113
+ full_model = f"{model_hint}:free" if ":free" not in model_hint else model_hint
114
+ tasks.append(asyncio.create_task(
115
+ self.try_model_direct(session, full_model, api_key)
116
+ ))
117
+
118
+ tasks.append(asyncio.create_task(
119
+ self.try_models_from_list(session, model_hint or "", api_key)
120
+ ))
121
+
122
+ done, pending = await asyncio.wait(
123
+ tasks,
124
+ return_when=asyncio.FIRST_COMPLETED
125
+ )
126
+
127
+ for task in pending:
128
+ task.cancel()
129
+
130
+ if tasks[0] in done:
131
+ result = tasks[0].result()
132
+ if result and result.get("success"):
133
+ return {
134
+ "success": True,
135
+ "response": result.get("response"),
136
+ "method": "direct",
137
+ "model": result.get("model")
138
+ }
139
+
140
+ if tasks[1] in done:
141
+ result = tasks[1].result()
142
+ if result and result.get("success"):
143
+ return {
144
+ "success": True,
145
+ "response": result.get("response"),
146
+ "method": "list_match",
147
+ "model": result.get("model")
148
+ }
149
+ else:
150
+ return {
151
+ "success": False,
152
+ "error": result.get("error", "Unknown error"),
153
+ "method": "list_match"
154
+ }
155
+
156
+ return {
157
+ "success": False,
158
+ "error": "Both methods failed",
159
+ "method": "both_failed"
160
+ }
161
+
162
+ def chat_completion_sync(self, prompt: str, model_hint: Optional[str] = None) -> Dict[str, Any]:
163
+ return asyncio.run(self.chat_completion(prompt, model_hint))
164
+
165
  def test_single_model(self, model_id: str) -> tuple[str, bool]:
166
  is_available = self.client.test_model(model_id, self.test_prompt)
167
  cleaned_name = clean_model_name(model_id)
 
170
  def test_all_models(self) -> Dict[str, Any]:
171
  print(f"[{datetime.now()}] Starting model scan...")
172
 
173
+ free_models = self.get_cached_free_models(force_refresh=True)
174
  print(f"Found {len(free_models)} free models to test")
175
 
176
  available_models: Set[str] = set()
177
  tested_count = 0
178
  success_count = 0
179
 
180
+ import concurrent.futures
181
  with concurrent.futures.ThreadPoolExecutor(
182
  max_workers=self.max_concurrency
183
  ) as executor: