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: options.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
- // Create promises for first token from each racer
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
- for await (const event of racer.iterator) {
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
- for await (const event of stream) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-ai-cli",
3
- "version": "10.6.0",
3
+ "version": "10.6.1",
4
4
  "description": "Fully Autonomous AI System - Self-funding, Self-deploying, Production Memory, A2A Protocol & Governance",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",