vectorplasticity commited on
Commit
7c24035
·
verified ·
1 Parent(s): 38dc899

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +29 -132
app.py CHANGED
@@ -1,155 +1,52 @@
1
- from flask import Flask, jsonify, Response, render_template_string
2
  import requests
3
- from cachetools import TTLCache
4
  import json
 
5
 
6
  app = Flask(__name__)
 
7
 
8
- # Cache for 3 hours (10800 seconds)
9
- cache = TTLCache(maxsize=1, ttl=10800)
10
-
11
- def fetch_models():
12
- """Fetch models from OpenRouter API."""
13
  if 'models' in cache:
14
  return cache['models']
15
-
16
- resp = requests.get('https://openrouter.ai/api/v1/models', timeout=30)
17
- resp.raise_for_status()
18
- data = resp.json()
19
- models = data.get('data', [])
20
- cache['models'] = models
21
- return models
22
-
23
- HTML_TEMPLATE = '''
24
- <!DOCTYPE html>
25
- <html>
26
- <head>
27
- <title>OpenRouter Models</title>
28
- <meta charset="utf-8">
29
- <style>
30
- body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; margin: 20px; background: #f5f5f5; }
31
- .header { background: #1a1a2e; color: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
32
- .header h1 { margin: 0; }
33
- .header p { margin: 10px 0 0; opacity: 0.8; }
34
- .actions { margin: 20px 0; }
35
- .btn { display: inline-block; padding: 10px 20px; margin-right: 10px; background: #4a9eff; color: white; text-decoration: none; border-radius: 5px; }
36
- .btn:hover { background: #3a8eef; }
37
- .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 15px; }
38
- .card { background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
39
- .card h3 { margin: 0 0 10px; font-size: 14px; word-break: break-all; color: #333; }
40
- .card .pricing { color: #28a745; font-weight: bold; margin: 5px 0; }
41
- .card .context { color: #666; font-size: 12px; }
42
- .badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-right: 5px; }
43
- .badge-free { background: #28a745; color: white; }
44
- .badge-paid { background: #dc3545; color: white; }
45
- .status { margin-top: 10px; padding-top: 10px; border-top: 1px solid #eee; }
46
- .status-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; }
47
- .status-up { background: #28a745; }
48
- .status-down { background: #dc3545; }
49
- .filters { margin-bottom: 15px; }
50
- .filters input { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; width: 300px; }
51
- table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
52
- th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; }
53
- th { background: #1a1a2e; color: white; }
54
- tr:hover { background: #f8f9fa; }
55
- .mode-tag { font-size: 11px; padding: 2px 6px; border-radius: 3px; background: #e9ecef; }
56
- </style>
57
- </head>
58
- <body>
59
- <div class="header">
60
- <h1>🤖 OpenRouter Models Directory</h1>
61
- <p>Auto-updates every 3 hours • {{ models|length }} models available</p>
62
- </div>
63
-
64
- <div class="actions">
65
- <a href="/api/raw" class="btn">📄 View Raw JSON</a>
66
- <a href="/api/download" class="btn">⬇️ Download JSON</a>
67
- </div>
68
-
69
- <div class="filters">
70
- <input type="text" id="search" placeholder="Search models..." onkeyup="filterTable()">
71
- </div>
72
-
73
- <table id="modelsTable">
74
- <thead>
75
- <tr>
76
- <th>Model</th>
77
- <th>Provider</th>
78
- <th>Pricing (Prompt/Completion)</th>
79
- <th>Context</th>
80
- <th>Status</th>
81
- </tr>
82
- </thead>
83
- <tbody>
84
- {% for m in models %}
85
- <tr>
86
- <td><strong>{{ m.name or m.id }}</strong><br><small style="color:#666">{{ m.id }}</small></td>
87
- <td>{{ m.id.split('/')[0] if '/' in m.id else 'Unknown' }}</td>
88
- <td class="pricing">
89
- ${{ m.pricing.prompt }}/1K → ${{ m.pricing.completion }}/1K
90
- </td>
91
- <td>{{ "{:,}".format(m.context_length) }} tokens</td>
92
- <td>
93
- {% if m.id.startswith('openai/gpt-4') or m.id.startswith('anthropic/claude-3') %}
94
- <span class="badge badge-paid">Paid</span>
95
- {% else %}
96
- <span class="badge badge-free">Available</span>
97
- {% endif %}
98
- <div style="margin-top:5px;font-size:11px;color:#666">
99
- Supports: {{ ", ".join(m.architecture.modality or ['text']) }}
100
- </div>
101
- </td>
102
- </tr>
103
- {% endfor %}
104
- </tbody>
105
- </table>
106
-
107
- <script>
108
- function filterTable() {
109
- var input = document.getElementById("search");
110
- var filter = input.value.toLowerCase();
111
- var table = document.getElementById("modelsTable");
112
- var tr = table.getElementsByTagName("tr");
113
- for (var i = 1; i < tr.length; i++) {
114
- var td = tr[i].getElementsByTagName("td");
115
- var found = false;
116
- for (var j = 0; j < td.length; j++) {
117
- if (td[j] && td[j].textContent.toLowerCase().indexOf(filter) > -1) {
118
- found = true;
119
- break;
120
- }
121
- }
122
- tr[i].style.display = found ? "" : "none";
123
- }
124
- }
125
- </script>
126
- </body>
127
- </html>'''
128
 
129
  @app.route('/')
130
  def index():
131
- models = fetch_models()
132
- return render_template_string(HTML_TEMPLATE, models=models)
133
 
134
  @app.route('/api/raw')
135
  def raw_json():
136
- """Return raw JSON with indent=4."""
137
- models = fetch_models()
138
  return Response(
139
- json.dumps({'data': models}, indent=4),
140
- mimetype='application/json'
 
141
  )
142
 
143
  @app.route('/api/download')
144
  def download_json():
145
- """Download JSON file."""
146
- models = fetch_models()
147
- json_str = json.dumps({'data': models}, indent=4)
148
  return Response(
149
- json_str,
150
  mimetype='application/json',
151
- headers={'Content-Disposition': 'attachment; filename=openrouter_models.json'}
152
  )
153
 
154
  if __name__ == '__main__':
155
- app.run(host='0.0.0.0', port=7860)
 
1
+ from flask import Flask, render_template, jsonify, Response
2
  import requests
3
+ from datetime import datetime
4
  import json
5
+ from cachetools import TTLCache
6
 
7
  app = Flask(__name__)
8
+ cache = TTLCache(maxsize=10, ttl=10800)
9
 
10
+ def get_cached_models():
 
 
 
 
11
  if 'models' in cache:
12
  return cache['models']
13
+ try:
14
+ resp = requests.get('https://openrouter.ai/api/v1/models', timeout=30)
15
+ resp.raise_for_status()
16
+ data = resp.json().get('data', [])
17
+ for m in data:
18
+ try:
19
+ p = m.get('pricing', {})
20
+ m['prompt_cost'] = float(p.get('prompt', 0) or 0)
21
+ m['completion_cost'] = float(p.get('completion', 0) or 0)
22
+ m['total_cost'] = round(m['prompt_cost'] + m['completion_cost'], 6)
23
+ except:
24
+ m['prompt_cost'] = m['completion_cost'] = m['total_cost'] = 0
25
+ cache['models'] = data
26
+ return data
27
+ except Exception as e:
28
+ return cache.get('models', [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  @app.route('/')
31
  def index():
32
+ models = get_cached_models()
33
+ return render_template('index.html', models=models, count=len(models), updated=datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC'))
34
 
35
  @app.route('/api/raw')
36
  def raw_json():
 
 
37
  return Response(
38
+ json.dumps(get_cached_models(), indent=4),
39
+ mimetype='application/json',
40
+ headers={'Content-Disposition': 'inline'}
41
  )
42
 
43
  @app.route('/api/download')
44
  def download_json():
 
 
 
45
  return Response(
46
+ json.dumps(get_cached_models(), indent=4),
47
  mimetype='application/json',
48
+ headers={'Content-Disposition': 'attachment; filename=openrouter-models.json'}
49
  )
50
 
51
  if __name__ == '__main__':
52
+ app.run(debug=True)