genesis-ai-cli 10.6.0 → 10.6.1
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.
|
@@ -155,7 +155,9 @@ class ModelRacer {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
catch { /* stream ended */ }
|
|
158
|
-
// Record result for learning
|
|
158
|
+
// Record result for learning + track savings
|
|
159
|
+
const saved = Math.max(0, candidates[0].expectedTTFT - ttft);
|
|
160
|
+
this.totalSaved += saved;
|
|
159
161
|
if (this.config.enableLearning) {
|
|
160
162
|
this.recordRaceResult(winner.racer.provider, winner.racer.model, {
|
|
161
163
|
ttft,
|
|
@@ -221,6 +223,9 @@ class ModelRacer {
|
|
|
221
223
|
}
|
|
222
224
|
}
|
|
223
225
|
catch { /* done */ }
|
|
226
|
+
// Primary won: savings = expected backup TTFT - primary's actual TTFT
|
|
227
|
+
const saved = Math.max(0, backup.expectedTTFT - ttft);
|
|
228
|
+
this.totalSaved += saved;
|
|
224
229
|
if (this.config.enableLearning) {
|
|
225
230
|
this.recordRaceResult(primary.provider, primary.model, {
|
|
226
231
|
ttft, tokensPerSec: tokenCount / ((Date.now() - startTime - ttft) / 1000 || 1),
|
|
@@ -356,10 +361,24 @@ class ModelRacer {
|
|
|
356
361
|
// Helpers
|
|
357
362
|
// ==========================================================================
|
|
358
363
|
selectCandidates(options) {
|
|
364
|
+
// Auto-exclude providers without API keys or unreachable services
|
|
365
|
+
const autoExclude = [];
|
|
366
|
+
if (!process.env.ANTHROPIC_API_KEY)
|
|
367
|
+
autoExclude.push('anthropic');
|
|
368
|
+
if (!process.env.OPENAI_API_KEY)
|
|
369
|
+
autoExclude.push('openai');
|
|
370
|
+
if (!process.env.GROQ_API_KEY)
|
|
371
|
+
autoExclude.push('groq');
|
|
372
|
+
if (!process.env.OLLAMA_HOST)
|
|
373
|
+
autoExclude.push('ollama');
|
|
374
|
+
const excludeProviders = [
|
|
375
|
+
...autoExclude,
|
|
376
|
+
...(options.excludeProviders || []),
|
|
377
|
+
];
|
|
359
378
|
return this.tracker.getRacingCandidates({
|
|
360
379
|
maxCandidates: this.config.maxRacers,
|
|
361
380
|
preferSpeed: true,
|
|
362
|
-
excludeProviders
|
|
381
|
+
excludeProviders,
|
|
363
382
|
});
|
|
364
383
|
}
|
|
365
384
|
buildStreamOptions(candidate, options, signal) {
|
|
@@ -386,18 +405,22 @@ class ModelRacer {
|
|
|
386
405
|
}
|
|
387
406
|
}
|
|
388
407
|
async findFirstToken(racers, startTime) {
|
|
389
|
-
//
|
|
408
|
+
// Use manual .next() calls to avoid closing the iterator when we find a winner.
|
|
409
|
+
// for-await would call .return() on early exit, terminating the stream.
|
|
390
410
|
const promises = racers.map(async (racer) => {
|
|
391
411
|
try {
|
|
392
|
-
|
|
412
|
+
while (true) {
|
|
413
|
+
const { value: event, done } = await racer.iterator.next();
|
|
414
|
+
if (done || !event)
|
|
415
|
+
return null;
|
|
393
416
|
if (event.type === 'token') {
|
|
394
417
|
return { racer, firstEvent: event };
|
|
395
418
|
}
|
|
396
419
|
if (event.type === 'error') {
|
|
397
420
|
return null;
|
|
398
421
|
}
|
|
422
|
+
// Skip non-token events (metadata, etc) and keep pulling
|
|
399
423
|
}
|
|
400
|
-
return null;
|
|
401
424
|
}
|
|
402
425
|
catch {
|
|
403
426
|
return null;
|
|
@@ -412,13 +435,17 @@ class ModelRacer {
|
|
|
412
435
|
return results;
|
|
413
436
|
}
|
|
414
437
|
async waitForFirstToken(stream) {
|
|
438
|
+
// Use manual .next() to avoid closing the iterator on early return.
|
|
415
439
|
try {
|
|
416
|
-
|
|
440
|
+
while (true) {
|
|
441
|
+
const { value: event, done } = await stream.next();
|
|
442
|
+
if (done || !event)
|
|
443
|
+
return null;
|
|
417
444
|
if (event.type === 'token' || event.type === 'error') {
|
|
418
445
|
return event;
|
|
419
446
|
}
|
|
447
|
+
// Skip non-token/error events and keep pulling
|
|
420
448
|
}
|
|
421
|
-
return null;
|
|
422
449
|
}
|
|
423
450
|
catch {
|
|
424
451
|
return null;
|
|
@@ -485,6 +512,12 @@ class ModelRacer {
|
|
|
485
512
|
provider,
|
|
486
513
|
model,
|
|
487
514
|
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
515
|
+
// Racing info for orchestrator to extract
|
|
516
|
+
racingWinner: provider,
|
|
517
|
+
racingModel: model,
|
|
518
|
+
racingStrategy: this.config.strategy,
|
|
519
|
+
racingCandidates: this.config.maxRacers,
|
|
520
|
+
racingSaved: Math.round(this.totalSaved / Math.max(1, this.raceCount)),
|
|
488
521
|
};
|
|
489
522
|
}
|
|
490
523
|
}
|
package/package.json
CHANGED