stratifyai 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
api/static/models.html ADDED
@@ -0,0 +1,567 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>StratifyAI - Model Catalog</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 1400px;
23
+ margin: 0 auto;
24
+ }
25
+
26
+ .header {
27
+ background: white;
28
+ padding: 20px 30px;
29
+ border-radius: 10px;
30
+ margin-bottom: 20px;
31
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
32
+ display: flex;
33
+ justify-content: space-between;
34
+ align-items: center;
35
+ }
36
+
37
+ .header h1 {
38
+ color: #667eea;
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 15px;
42
+ }
43
+
44
+ .header h1 img {
45
+ height: 40px;
46
+ width: auto;
47
+ }
48
+
49
+ .back-btn {
50
+ background: #667eea;
51
+ color: white;
52
+ border: none;
53
+ padding: 10px 20px;
54
+ border-radius: 5px;
55
+ cursor: pointer;
56
+ font-weight: 500;
57
+ text-decoration: none;
58
+ transition: background 0.3s;
59
+ }
60
+
61
+ .back-btn:hover {
62
+ background: #5568d3;
63
+ }
64
+
65
+ .summary-bar {
66
+ background: white;
67
+ padding: 15px 25px;
68
+ border-radius: 10px;
69
+ margin-bottom: 20px;
70
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
71
+ display: flex;
72
+ gap: 30px;
73
+ flex-wrap: wrap;
74
+ }
75
+
76
+ .summary-item {
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 8px;
80
+ }
81
+
82
+ .summary-item .label {
83
+ color: #666;
84
+ font-size: 14px;
85
+ }
86
+
87
+ .summary-item .value {
88
+ font-weight: 600;
89
+ color: #333;
90
+ font-size: 18px;
91
+ }
92
+
93
+ .filters {
94
+ background: white;
95
+ padding: 15px 25px;
96
+ border-radius: 10px;
97
+ margin-bottom: 20px;
98
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
99
+ display: flex;
100
+ gap: 20px;
101
+ align-items: center;
102
+ flex-wrap: wrap;
103
+ }
104
+
105
+ .filter-group {
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 8px;
109
+ }
110
+
111
+ .filter-group label {
112
+ color: #666;
113
+ font-size: 14px;
114
+ }
115
+
116
+ .filter-group select,
117
+ .filter-group input {
118
+ padding: 8px 12px;
119
+ border: 1px solid #ddd;
120
+ border-radius: 5px;
121
+ font-size: 14px;
122
+ }
123
+
124
+ .filter-group input[type="checkbox"] {
125
+ width: 18px;
126
+ height: 18px;
127
+ cursor: pointer;
128
+ }
129
+
130
+ .main-content {
131
+ background: white;
132
+ padding: 20px;
133
+ border-radius: 10px;
134
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
135
+ overflow-x: auto;
136
+ }
137
+
138
+ .provider-section {
139
+ margin-bottom: 30px;
140
+ }
141
+
142
+ .provider-header {
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 15px;
146
+ margin-bottom: 15px;
147
+ padding-bottom: 10px;
148
+ border-bottom: 2px solid #eee;
149
+ }
150
+
151
+ .provider-name {
152
+ font-size: 20px;
153
+ font-weight: 600;
154
+ color: #333;
155
+ }
156
+
157
+ .provider-status {
158
+ padding: 4px 12px;
159
+ border-radius: 20px;
160
+ font-size: 12px;
161
+ font-weight: 500;
162
+ }
163
+
164
+ .provider-status.active {
165
+ background: #dcfce7;
166
+ color: #166534;
167
+ }
168
+
169
+ .provider-status.inactive {
170
+ background: #fef3c7;
171
+ color: #92400e;
172
+ }
173
+
174
+ .provider-meta {
175
+ font-size: 13px;
176
+ color: #666;
177
+ }
178
+
179
+ table {
180
+ width: 100%;
181
+ border-collapse: collapse;
182
+ font-size: 14px;
183
+ }
184
+
185
+ th, td {
186
+ padding: 12px 15px;
187
+ text-align: left;
188
+ border-bottom: 1px solid #eee;
189
+ }
190
+
191
+ th {
192
+ background: #f8f9fa;
193
+ font-weight: 600;
194
+ color: #333;
195
+ position: sticky;
196
+ top: 0;
197
+ }
198
+
199
+ tr:hover {
200
+ background: #f8f9fa;
201
+ }
202
+
203
+ .model-name {
204
+ font-weight: 500;
205
+ color: #333;
206
+ }
207
+
208
+ .cost {
209
+ font-family: 'SFMono-Regular', Consolas, monospace;
210
+ color: #666;
211
+ }
212
+
213
+ .context {
214
+ font-family: 'SFMono-Regular', Consolas, monospace;
215
+ }
216
+
217
+ .capability {
218
+ display: inline-block;
219
+ padding: 3px 8px;
220
+ border-radius: 4px;
221
+ font-size: 11px;
222
+ font-weight: 500;
223
+ margin: 2px;
224
+ }
225
+
226
+ .capability.vision {
227
+ background: #dbeafe;
228
+ color: #1e40af;
229
+ }
230
+
231
+ .capability.tools {
232
+ background: #fce7f3;
233
+ color: #be185d;
234
+ }
235
+
236
+ .capability.caching {
237
+ background: #d1fae5;
238
+ color: #047857;
239
+ }
240
+
241
+ .capability.reasoning {
242
+ background: #fef3c7;
243
+ color: #92400e;
244
+ }
245
+
246
+ .validated-badge {
247
+ display: inline-block;
248
+ padding: 3px 8px;
249
+ border-radius: 4px;
250
+ font-size: 11px;
251
+ font-weight: 500;
252
+ }
253
+
254
+ .validated-badge.yes {
255
+ background: #dcfce7;
256
+ color: #166534;
257
+ }
258
+
259
+ .validated-badge.no {
260
+ background: #fee2e2;
261
+ color: #991b1b;
262
+ }
263
+
264
+ .loading {
265
+ text-align: center;
266
+ padding: 50px;
267
+ color: #666;
268
+ }
269
+
270
+ .loading-spinner {
271
+ display: inline-block;
272
+ width: 40px;
273
+ height: 40px;
274
+ border: 4px solid #f3f3f3;
275
+ border-top: 4px solid #667eea;
276
+ border-radius: 50%;
277
+ animation: spin 1s linear infinite;
278
+ margin-bottom: 15px;
279
+ }
280
+
281
+ @keyframes spin {
282
+ 0% { transform: rotate(0deg); }
283
+ 100% { transform: rotate(360deg); }
284
+ }
285
+
286
+ .error-message {
287
+ background: #fee2e2;
288
+ color: #991b1b;
289
+ padding: 15px 20px;
290
+ border-radius: 8px;
291
+ margin-bottom: 20px;
292
+ }
293
+
294
+ .no-models {
295
+ color: #666;
296
+ font-style: italic;
297
+ padding: 20px;
298
+ text-align: center;
299
+ }
300
+ </style>
301
+ </head>
302
+ <body>
303
+ <div class="container">
304
+ <div class="header">
305
+ <h1>
306
+ <img src="/static/stratifyai_wide_logo.png" alt="StratifyAI Logo">
307
+ Model Catalog
308
+ </h1>
309
+ <a href="/" class="back-btn">← Back to Chat</a>
310
+ </div>
311
+
312
+ <div class="summary-bar" id="summary-bar">
313
+ <div class="summary-item">
314
+ <span class="label">Total Models:</span>
315
+ <span class="value" id="total-models">-</span>
316
+ </div>
317
+ <div class="summary-item">
318
+ <span class="label">Providers:</span>
319
+ <span class="value" id="total-providers">-</span>
320
+ </div>
321
+ <div class="summary-item">
322
+ <span class="label">Active Providers:</span>
323
+ <span class="value" id="active-providers">-</span>
324
+ </div>
325
+ </div>
326
+
327
+ <div class="filters">
328
+ <div class="filter-group">
329
+ <label for="provider-filter">Provider:</label>
330
+ <select id="provider-filter">
331
+ <option value="">All Providers</option>
332
+ </select>
333
+ </div>
334
+ <div class="filter-group">
335
+ <label for="capability-filter">Capability:</label>
336
+ <select id="capability-filter">
337
+ <option value="">All Capabilities</option>
338
+ <option value="vision">Vision</option>
339
+ <option value="tools">Tools</option>
340
+ <option value="caching">Caching</option>
341
+ <option value="reasoning">Reasoning</option>
342
+ </select>
343
+ </div>
344
+ <div class="filter-group">
345
+ <input type="checkbox" id="active-only">
346
+ <label for="active-only">Active providers only</label>
347
+ </div>
348
+ <div class="filter-group">
349
+ <input type="checkbox" id="validated-only">
350
+ <label for="validated-only">Validated models only</label>
351
+ </div>
352
+ <div class="filter-group">
353
+ <button id="reset-filters" style="background: #6b7280; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; font-weight: 500;">🔄 Reset Filters</button>
354
+ </div>
355
+ </div>
356
+
357
+ <div class="main-content" id="main-content">
358
+ <div class="loading">
359
+ <div class="loading-spinner"></div>
360
+ <p>Loading model catalog...</p>
361
+ </div>
362
+ </div>
363
+ </div>
364
+
365
+ <script>
366
+ const API_BASE = 'http://localhost:8080';
367
+ let allData = null;
368
+
369
+ // Format cost for display
370
+ function formatCost(cost) {
371
+ if (cost === 0 || cost === null || cost === undefined) return '-';
372
+ if (cost < 0.01) return `$${cost.toFixed(4)}`;
373
+ return `$${cost.toFixed(2)}`;
374
+ }
375
+
376
+ // Format context window for display
377
+ function formatContext(context) {
378
+ if (!context) return '-';
379
+ if (context >= 1000000) return `${(context / 1000000).toFixed(1)}M`;
380
+ if (context >= 1000) return `${(context / 1000).toFixed(0)}K`;
381
+ return context.toString();
382
+ }
383
+
384
+ // Capitalize provider name
385
+ function capitalizeProvider(provider) {
386
+ const special = {
387
+ 'openai': 'OpenAI',
388
+ 'anthropic': 'Anthropic',
389
+ 'google': 'Google',
390
+ 'deepseek': 'DeepSeek',
391
+ 'groq': 'Groq',
392
+ 'grok': 'Grok (X.AI)',
393
+ 'ollama': 'Ollama',
394
+ 'openrouter': 'OpenRouter',
395
+ 'bedrock': 'AWS Bedrock'
396
+ };
397
+ return special[provider] || provider.charAt(0).toUpperCase() + provider.slice(1);
398
+ }
399
+
400
+ // Build capabilities badges
401
+ function buildCapabilities(model) {
402
+ let badges = [];
403
+ if (model.supports_vision) badges.push('<span class="capability vision">👁️ Vision</span>');
404
+ if (model.supports_tools) badges.push('<span class="capability tools">🔧 Tools</span>');
405
+ if (model.supports_caching) badges.push('<span class="capability caching">💾 Caching</span>');
406
+ if (model.reasoning_model) badges.push('<span class="capability reasoning">🧠 Reasoning</span>');
407
+ return badges.length > 0 ? badges.join('') : '<span style="color: #999;">-</span>';
408
+ }
409
+
410
+ // Render models table
411
+ function renderModels() {
412
+ if (!allData) return;
413
+
414
+ const providerFilter = document.getElementById('provider-filter').value;
415
+ const capabilityFilter = document.getElementById('capability-filter').value;
416
+ const activeOnly = document.getElementById('active-only').checked;
417
+ const validatedOnly = document.getElementById('validated-only').checked;
418
+
419
+ const container = document.getElementById('main-content');
420
+ let html = '';
421
+
422
+ // Get providers in order
423
+ const providerOrder = ['openai', 'anthropic', 'google', 'deepseek', 'groq', 'grok', 'ollama', 'openrouter', 'bedrock'];
424
+ const providers = providerFilter ? [providerFilter] : providerOrder;
425
+
426
+ for (const provider of providers) {
427
+ const providerData = allData.providers[provider];
428
+ if (!providerData) continue;
429
+
430
+ // Filter by active status
431
+ if (activeOnly && !providerData.active) continue;
432
+
433
+ // Filter models
434
+ let models = providerData.models || [];
435
+
436
+ if (capabilityFilter) {
437
+ models = models.filter(m => {
438
+ switch (capabilityFilter) {
439
+ case 'vision': return m.supports_vision;
440
+ case 'tools': return m.supports_tools;
441
+ case 'caching': return m.supports_caching;
442
+ case 'reasoning': return m.reasoning_model;
443
+ default: return true;
444
+ }
445
+ });
446
+ }
447
+
448
+ if (validatedOnly) {
449
+ models = models.filter(m => m.validated);
450
+ }
451
+
452
+ if (models.length === 0) continue;
453
+
454
+ // Build provider section
455
+ html += `
456
+ <div class="provider-section">
457
+ <div class="provider-header">
458
+ <span class="provider-name">${capitalizeProvider(provider)}</span>
459
+ <span class="provider-status ${providerData.active ? 'active' : 'inactive'}">
460
+ ${providerData.active ? '✓ Active' : '⚠ No API Key'}
461
+ </span>
462
+ <span class="provider-meta">${models.length} model${models.length !== 1 ? 's' : ''}</span>
463
+ ${providerData.validation_error ? `<span class="provider-meta" style="color: #dc2626;">⚠ ${providerData.validation_error}</span>` : ''}
464
+ </div>
465
+ <table>
466
+ <thead>
467
+ <tr>
468
+ <th>Model</th>
469
+ <th>Context</th>
470
+ <th>Cost (Input)</th>
471
+ <th>Cost (Output)</th>
472
+ <th>Capabilities</th>
473
+ <th>Validated</th>
474
+ </tr>
475
+ </thead>
476
+ <tbody>
477
+ `;
478
+
479
+ for (const model of models) {
480
+ html += `
481
+ <tr>
482
+ <td class="model-name">${model.id}</td>
483
+ <td class="context">${formatContext(model.context_window)}</td>
484
+ <td class="cost">${formatCost(model.cost_input)}/1M</td>
485
+ <td class="cost">${formatCost(model.cost_output)}/1M</td>
486
+ <td>${buildCapabilities(model)}</td>
487
+ <td>
488
+ <span class="validated-badge ${model.validated ? 'yes' : 'no'}">
489
+ ${model.validated ? '✓ Yes' : '✗ No'}
490
+ </span>
491
+ </td>
492
+ </tr>
493
+ `;
494
+ }
495
+
496
+ html += `
497
+ </tbody>
498
+ </table>
499
+ </div>
500
+ `;
501
+ }
502
+
503
+ if (!html) {
504
+ html = '<div class="no-models">No models match the current filters.</div>';
505
+ }
506
+
507
+ container.innerHTML = html;
508
+ }
509
+
510
+ // Load data
511
+ async function loadData() {
512
+ try {
513
+ const response = await fetch(`${API_BASE}/api/all-models`);
514
+ if (!response.ok) throw new Error('Failed to load models');
515
+
516
+ allData = await response.json();
517
+
518
+ // Update summary
519
+ document.getElementById('total-models').textContent = allData.summary.total_models;
520
+ document.getElementById('total-providers').textContent = allData.summary.total_providers;
521
+ document.getElementById('active-providers').textContent = allData.summary.active_providers;
522
+
523
+ // Populate provider filter
524
+ const providerFilter = document.getElementById('provider-filter');
525
+ const providerOrder = ['openai', 'anthropic', 'google', 'deepseek', 'groq', 'grok', 'ollama', 'openrouter', 'bedrock'];
526
+ for (const provider of providerOrder) {
527
+ if (allData.providers[provider]) {
528
+ const option = document.createElement('option');
529
+ option.value = provider;
530
+ option.textContent = capitalizeProvider(provider);
531
+ providerFilter.appendChild(option);
532
+ }
533
+ }
534
+
535
+ // Render models
536
+ renderModels();
537
+
538
+ } catch (error) {
539
+ document.getElementById('main-content').innerHTML = `
540
+ <div class="error-message">
541
+ ❌ Error loading model catalog: ${error.message}
542
+ </div>
543
+ `;
544
+ }
545
+ }
546
+
547
+ // Reset filters
548
+ function resetFilters() {
549
+ document.getElementById('provider-filter').value = '';
550
+ document.getElementById('capability-filter').value = '';
551
+ document.getElementById('active-only').checked = false;
552
+ document.getElementById('validated-only').checked = false;
553
+ renderModels();
554
+ }
555
+
556
+ // Event listeners for filters
557
+ document.getElementById('provider-filter').addEventListener('change', renderModels);
558
+ document.getElementById('capability-filter').addEventListener('change', renderModels);
559
+ document.getElementById('active-only').addEventListener('change', renderModels);
560
+ document.getElementById('validated-only').addEventListener('change', renderModels);
561
+ document.getElementById('reset-filters').addEventListener('click', resetFilters);
562
+
563
+ // Initialize
564
+ loadData();
565
+ </script>
566
+ </body>
567
+ </html>
Binary file
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stratifyai
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Unified multi-provider LLM abstraction module with intelligent routing, cost tracking, and caching
5
5
  Author-email: Steven Cotton <cototnbytes@gmail.com>
6
6
  Maintainer-email: Steven Cotton <cototnbytes@gmail.com>
@@ -33,6 +33,9 @@ Requires-Dist: pydantic>=2.0.0
33
33
  Requires-Dist: typing-extensions>=4.0.0
34
34
  Requires-Dist: typer>=0.9.0
35
35
  Requires-Dist: tiktoken>=0.5.0
36
+ Requires-Dist: fastapi>=0.115.0
37
+ Requires-Dist: uvicorn[standard]>=0.34.0
38
+ Requires-Dist: websockets>=14.0
36
39
  Provides-Extra: dev
37
40
  Requires-Dist: pytest>=9.0.0; extra == "dev"
38
41
  Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
@@ -1,3 +1,10 @@
1
+ api/__init__.py,sha256=g3jSdRogNbCJK7ksKp4tuDTmktckaqXKCmadgRYvfkQ,52
2
+ api/main.py,sha256=4DTOEwpxx3LWTKS82T3WPN3JTX4QCmkbVVGWSg6Tu-c,30349
3
+ api/static/index.html,sha256=1olXOhlrFQZPdSKHxB0ozli5g1o0i6Ci_rnzYBkgTxw,48277
4
+ api/static/models.html,sha256=jjEYoWfNAJd7PrryVaVqHh19C_t21_uJAtVSCsBUoU0,19099
5
+ api/static/stratifyai_trans_logo.png,sha256=icER6lIzQ7uVhSwt7E_plEkpE57B7-6W4GuTgXXMxko,1393542
6
+ api/static/stratifyai_wide_logo.png,sha256=caZsnIxxnSs1_9s72MmUrWK3pbBsKIDZ1cahDXf34X0,88548
7
+ api/static/stratum_logo.png,sha256=XBrXiTE-g9Ku615qMWyD0qCXljfLZq56E_7WLUJUhGg,31093
1
8
  cli/__init__.py,sha256=Bwi6ibJLRbqK0oIP_U0pvypH0nKXUBMbufZPnXMkI2E,96
2
9
  cli/stratifyai_cli.py,sha256=qdOcLDS3yjOSLJplQkc7PQK4kUDhaBpy8AV7XPh-8_c,111826
3
10
  stratifyai/__init__.py,sha256=yjmHcAcorD5txlNZwmamljOnN3nHCosBY2_0CE26TaE,2873
@@ -49,9 +56,9 @@ stratifyai/utils/log_extractor.py,sha256=Nz2x9xPvRVdp5yk6cTLQ5CHK4Fi5oNKQG2t2k9e
49
56
  stratifyai/utils/model_selector.py,sha256=xoaJ4rHhsqHTbEUnINeT267AWNziRPEVMdjfBuyykuI,11895
50
57
  stratifyai/utils/provider_validator.py,sha256=LUBZebd8MZFXPoUDFOTJ6JxMkJG9CbcrJNHZiK--WCU,14864
51
58
  stratifyai/utils/token_counter.py,sha256=58hkX3q4yObZVwo3lvn7OGPh7puiWNind7yX3Qem_1o,6432
52
- stratifyai-0.1.2.dist-info/licenses/LICENSE,sha256=UEMslgLRhi7sYtDIwMY0IjEppiVEgqgMIdsulgYdb0Q,1068
53
- stratifyai-0.1.2.dist-info/METADATA,sha256=TDiyf0lPvlatnehrAaKHvzOawaqGJ-nXutUalR0xxM4,7872
54
- stratifyai-0.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
55
- stratifyai-0.1.2.dist-info/entry_points.txt,sha256=aWXYCRBSld8hzAtCPAqsl2NHKJIJkIgc63uQOO2PcUQ,55
56
- stratifyai-0.1.2.dist-info/top_level.txt,sha256=0_JiuYll_AXJr5e4qXzbCs8LYykDxGaEjawwI69vEhM,15
57
- stratifyai-0.1.2.dist-info/RECORD,,
59
+ stratifyai-0.1.3.dist-info/licenses/LICENSE,sha256=UEMslgLRhi7sYtDIwMY0IjEppiVEgqgMIdsulgYdb0Q,1068
60
+ stratifyai-0.1.3.dist-info/METADATA,sha256=brTVzjEx5DXL1hDDdNhDzb4OuyixtKWwHf4EJ6P13tQ,7977
61
+ stratifyai-0.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
62
+ stratifyai-0.1.3.dist-info/entry_points.txt,sha256=aWXYCRBSld8hzAtCPAqsl2NHKJIJkIgc63uQOO2PcUQ,55
63
+ stratifyai-0.1.3.dist-info/top_level.txt,sha256=FknIJcC6EUuTJw66TfdXz-tF5VyT5t4HsXXwMDb6Qtw,19
64
+ stratifyai-0.1.3.dist-info/RECORD,,