@contractspec/lib.ai-providers 3.0.0 → 3.1.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.
package/dist/index.js CHANGED
@@ -289,22 +289,30 @@ function getDefaultModel(provider) {
289
289
  }
290
290
 
291
291
  // src/factory.ts
292
- import { anthropic } from "@ai-sdk/anthropic";
293
- import { google } from "@ai-sdk/google";
294
- import { mistral } from "@ai-sdk/mistral";
295
- import { openai } from "@ai-sdk/openai";
296
- import { ollama } from "ollama-ai-provider";
292
+ import { createAnthropic } from "@ai-sdk/anthropic";
293
+ import { createGoogleGenerativeAI } from "@ai-sdk/google";
294
+ import { createMistral } from "@ai-sdk/mistral";
295
+ import { createOpenAI } from "@ai-sdk/openai";
296
+ import { createOllama } from "ollama-ai-provider";
297
297
  class BaseProvider {
298
298
  name;
299
299
  model;
300
300
  mode;
301
301
  config;
302
+ transport;
303
+ authMethod;
304
+ apiVersion;
305
+ customHeaders;
302
306
  cachedModel = null;
303
307
  constructor(config) {
304
308
  this.name = config.provider;
305
309
  this.model = config.model ?? DEFAULT_MODELS[config.provider];
306
310
  this.mode = this.determineMode(config);
307
311
  this.config = config;
312
+ this.transport = config.transport;
313
+ this.authMethod = config.authMethod;
314
+ this.apiVersion = config.apiVersion;
315
+ this.customHeaders = config.customHeaders;
308
316
  }
309
317
  getModel() {
310
318
  if (!this.cachedModel) {
@@ -344,81 +352,33 @@ class BaseProvider {
344
352
  return "managed";
345
353
  }
346
354
  createModel() {
347
- const { baseUrl, proxyUrl } = this.config;
355
+ const { baseUrl, proxyUrl, apiKey } = this.config;
356
+ const headers = this.customHeaders;
357
+ if (this.name === "ollama") {
358
+ const provider = createOllama({ baseURL: baseUrl, headers });
359
+ return provider(this.model);
360
+ }
361
+ if (this.mode === "managed" && proxyUrl) {
362
+ const provider = createOpenAI({ baseURL: proxyUrl, apiKey, headers });
363
+ return provider(this.model);
364
+ }
348
365
  switch (this.name) {
349
- case "ollama": {
350
- const originalBaseUrl = process.env.OLLAMA_BASE_URL;
351
- if (baseUrl && baseUrl !== "http://localhost:11434") {
352
- process.env.OLLAMA_BASE_URL = baseUrl;
353
- }
354
- const ollamaModel = ollama(this.model);
355
- if (originalBaseUrl !== undefined) {
356
- process.env.OLLAMA_BASE_URL = originalBaseUrl;
357
- } else if (baseUrl && baseUrl !== "http://localhost:11434") {
358
- delete process.env.OLLAMA_BASE_URL;
359
- }
360
- return ollamaModel;
366
+ case "openai": {
367
+ const provider = createOpenAI({ apiKey, headers });
368
+ return provider(this.model);
369
+ }
370
+ case "anthropic": {
371
+ const provider = createAnthropic({ apiKey, headers });
372
+ return provider(this.model);
373
+ }
374
+ case "mistral": {
375
+ const provider = createMistral({ apiKey, headers });
376
+ return provider(this.model);
377
+ }
378
+ case "gemini": {
379
+ const provider = createGoogleGenerativeAI({ apiKey, headers });
380
+ return provider(this.model);
361
381
  }
362
- case "openai":
363
- if (this.mode === "managed") {
364
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
365
- if (proxyUrl) {
366
- process.env.OPENAI_BASE_URL = proxyUrl;
367
- }
368
- const model = openai(this.model);
369
- if (originalBaseUrl !== undefined) {
370
- process.env.OPENAI_BASE_URL = originalBaseUrl;
371
- } else if (proxyUrl) {
372
- delete process.env.OPENAI_BASE_URL;
373
- }
374
- return model;
375
- }
376
- return openai(this.model);
377
- case "anthropic":
378
- if (this.mode === "managed") {
379
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
380
- if (proxyUrl) {
381
- process.env.OPENAI_BASE_URL = proxyUrl;
382
- }
383
- const model = openai(this.model);
384
- if (originalBaseUrl !== undefined) {
385
- process.env.OPENAI_BASE_URL = originalBaseUrl;
386
- } else if (proxyUrl) {
387
- delete process.env.OPENAI_BASE_URL;
388
- }
389
- return model;
390
- }
391
- return anthropic(this.model);
392
- case "mistral":
393
- if (this.mode === "managed") {
394
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
395
- if (proxyUrl) {
396
- process.env.OPENAI_BASE_URL = proxyUrl;
397
- }
398
- const model = openai(this.model);
399
- if (originalBaseUrl !== undefined) {
400
- process.env.OPENAI_BASE_URL = originalBaseUrl;
401
- } else if (proxyUrl) {
402
- delete process.env.OPENAI_BASE_URL;
403
- }
404
- return model;
405
- }
406
- return mistral(this.model);
407
- case "gemini":
408
- if (this.mode === "managed") {
409
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
410
- if (proxyUrl) {
411
- process.env.OPENAI_BASE_URL = proxyUrl;
412
- }
413
- const model = openai(this.model);
414
- if (originalBaseUrl !== undefined) {
415
- process.env.OPENAI_BASE_URL = originalBaseUrl;
416
- } else if (proxyUrl) {
417
- delete process.env.OPENAI_BASE_URL;
418
- }
419
- return model;
420
- }
421
- return google(this.model);
422
382
  default:
423
383
  throw new Error(`Unknown provider: ${this.name}`);
424
384
  }
@@ -500,13 +460,17 @@ function createProviderFromEnv() {
500
460
  case "ollama":
501
461
  break;
502
462
  }
463
+ const transport = process.env.CONTRACTSPEC_AI_TRANSPORT;
464
+ const apiVersion = process.env.CONTRACTSPEC_AI_API_VERSION;
503
465
  return createProvider({
504
466
  provider,
505
467
  model,
506
468
  apiKey,
507
469
  baseUrl: process.env.OLLAMA_BASE_URL,
508
470
  proxyUrl: process.env.CONTRACTSPEC_AI_PROXY_URL,
509
- organizationId: process.env.CONTRACTSPEC_ORG_ID
471
+ organizationId: process.env.CONTRACTSPEC_ORG_ID,
472
+ transport,
473
+ apiVersion
510
474
  });
511
475
  }
512
476
  function getAvailableProviders() {
@@ -514,35 +478,45 @@ function getAvailableProviders() {
514
478
  providers.push({
515
479
  provider: "ollama",
516
480
  available: true,
517
- mode: "local"
481
+ mode: "local",
482
+ transports: ["rest", "sdk"],
483
+ authMethods: []
518
484
  });
519
485
  const openaiKey = process.env.OPENAI_API_KEY;
520
486
  providers.push({
521
487
  provider: "openai",
522
488
  available: Boolean(openaiKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
523
489
  mode: openaiKey ? "byok" : "managed",
524
- reason: !openaiKey ? "Set OPENAI_API_KEY for BYOK mode" : undefined
490
+ reason: !openaiKey ? "Set OPENAI_API_KEY for BYOK mode" : undefined,
491
+ transports: ["rest", "sdk"],
492
+ authMethods: ["api-key"]
525
493
  });
526
494
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
527
495
  providers.push({
528
496
  provider: "anthropic",
529
497
  available: Boolean(anthropicKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
530
498
  mode: anthropicKey ? "byok" : "managed",
531
- reason: !anthropicKey ? "Set ANTHROPIC_API_KEY for BYOK mode" : undefined
499
+ reason: !anthropicKey ? "Set ANTHROPIC_API_KEY for BYOK mode" : undefined,
500
+ transports: ["rest", "sdk"],
501
+ authMethods: ["api-key"]
532
502
  });
533
503
  const mistralKey = process.env.MISTRAL_API_KEY;
534
504
  providers.push({
535
505
  provider: "mistral",
536
506
  available: Boolean(mistralKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
537
507
  mode: mistralKey ? "byok" : "managed",
538
- reason: !mistralKey ? "Set MISTRAL_API_KEY for BYOK mode" : undefined
508
+ reason: !mistralKey ? "Set MISTRAL_API_KEY for BYOK mode" : undefined,
509
+ transports: ["rest", "sdk"],
510
+ authMethods: ["api-key"]
539
511
  });
540
512
  const geminiKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
541
513
  providers.push({
542
514
  provider: "gemini",
543
515
  available: Boolean(geminiKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
544
516
  mode: geminiKey ? "byok" : "managed",
545
- reason: !geminiKey ? "Set GOOGLE_API_KEY for BYOK mode" : undefined
517
+ reason: !geminiKey ? "Set GOOGLE_API_KEY for BYOK mode" : undefined,
518
+ transports: ["rest", "sdk"],
519
+ authMethods: ["api-key"]
546
520
  });
547
521
  return providers;
548
522
  }
@@ -602,6 +576,174 @@ async function listOllamaModels(baseUrl = "http://localhost:11434") {
602
576
  return [];
603
577
  }
604
578
  }
579
+ // src/selector.ts
580
+ function createModelSelector(options) {
581
+ const { store, fallbackModels, defaultConstraints } = options;
582
+ const catalog = fallbackModels ?? MODELS;
583
+ return {
584
+ async select(context) {
585
+ const merged = mergeConstraints(defaultConstraints, context.constraints);
586
+ if (context.priorities?.length) {
587
+ return selectMultiObjective(store, catalog, context.priorities, merged);
588
+ }
589
+ const dimension = context.taskDimension ?? "reasoning";
590
+ return selectByDimension(store, catalog, dimension, merged);
591
+ },
592
+ async selectAndCreate(context) {
593
+ const selection = await this.select(context);
594
+ const model = createProvider({
595
+ provider: selection.providerKey,
596
+ model: selection.modelId
597
+ }).getModel();
598
+ return { model, selection };
599
+ }
600
+ };
601
+ }
602
+ async function selectByDimension(store, catalog, dimension, constraints) {
603
+ const { rankings } = await store.listModelRankings({ dimension, limit: 50 });
604
+ const eligible = filterRankings(rankings, catalog, constraints);
605
+ const topCandidate = eligible[0];
606
+ if (topCandidate) {
607
+ const dimScore = topCandidate.dimensionScores[dimension]?.score ?? topCandidate.compositeScore;
608
+ return {
609
+ modelId: topCandidate.modelId,
610
+ providerKey: topCandidate.providerKey,
611
+ score: dimScore,
612
+ reason: `Top-ranked for "${dimension}" (score ${Math.round(dimScore)})`,
613
+ alternatives: eligible.slice(1, 4).map((r) => ({
614
+ modelId: r.modelId,
615
+ providerKey: r.providerKey,
616
+ score: r.dimensionScores[dimension]?.score ?? r.compositeScore
617
+ }))
618
+ };
619
+ }
620
+ return fallbackFromCatalog(catalog, constraints, dimension);
621
+ }
622
+ async function selectMultiObjective(store, catalog, priorities, constraints) {
623
+ const { rankings } = await store.listModelRankings({ limit: 100 });
624
+ const eligible = filterRankings(rankings, catalog, constraints);
625
+ if (eligible.length === 0) {
626
+ const primaryDim = priorities.reduce((a, b) => b.weight > a.weight ? b : a).dimension;
627
+ return fallbackFromCatalog(catalog, constraints, primaryDim);
628
+ }
629
+ const totalWeight = priorities.reduce((sum, p) => sum + p.weight, 0) || 1;
630
+ const scored = eligible.map((r) => {
631
+ let weightedScore = 0;
632
+ for (const p of priorities) {
633
+ const dimScore = r.dimensionScores[p.dimension]?.score ?? 0;
634
+ weightedScore += dimScore * (p.weight / totalWeight);
635
+ }
636
+ return { ranking: r, weightedScore };
637
+ });
638
+ scored.sort((a, b) => b.weightedScore - a.weightedScore);
639
+ const best = scored[0];
640
+ if (!best) {
641
+ const primaryDim = priorities.reduce((a, b) => b.weight > a.weight ? b : a).dimension;
642
+ return fallbackFromCatalog(catalog, constraints, primaryDim);
643
+ }
644
+ const dims = priorities.map((p) => p.dimension).join(", ");
645
+ return {
646
+ modelId: best.ranking.modelId,
647
+ providerKey: best.ranking.providerKey,
648
+ score: Math.round(best.weightedScore * 100) / 100,
649
+ reason: `Multi-objective optimum across [${dims}]`,
650
+ alternatives: scored.slice(1, 4).map((s) => ({
651
+ modelId: s.ranking.modelId,
652
+ providerKey: s.ranking.providerKey,
653
+ score: Math.round(s.weightedScore * 100) / 100
654
+ }))
655
+ };
656
+ }
657
+ function filterRankings(rankings, catalog, constraints) {
658
+ return rankings.filter((r) => {
659
+ if (constraints.allowedProviders?.length) {
660
+ if (!constraints.allowedProviders.includes(r.providerKey))
661
+ return false;
662
+ }
663
+ if (constraints.excludeModels?.length) {
664
+ if (constraints.excludeModels.includes(r.modelId))
665
+ return false;
666
+ }
667
+ const info = getModelInfo(r.modelId) ?? catalog.find((m) => m.id === r.modelId);
668
+ if (!info)
669
+ return true;
670
+ if (constraints.minContextWindow && info.contextWindow < constraints.minContextWindow) {
671
+ return false;
672
+ }
673
+ if (constraints.maxCostPerMillionInput && info.costPerMillion) {
674
+ if (info.costPerMillion.input > constraints.maxCostPerMillionInput)
675
+ return false;
676
+ }
677
+ if (constraints.maxCostPerMillionOutput && info.costPerMillion) {
678
+ if (info.costPerMillion.output > constraints.maxCostPerMillionOutput)
679
+ return false;
680
+ }
681
+ if (constraints.requiredCapabilities?.length) {
682
+ for (const cap of constraints.requiredCapabilities) {
683
+ if (!info.capabilities[cap])
684
+ return false;
685
+ }
686
+ }
687
+ return true;
688
+ });
689
+ }
690
+ function fallbackFromCatalog(catalog, constraints, dimension) {
691
+ let eligible = catalog.filter((m) => m.costPerMillion != null);
692
+ const {
693
+ allowedProviders,
694
+ excludeModels,
695
+ minContextWindow,
696
+ requiredCapabilities
697
+ } = constraints;
698
+ if (allowedProviders?.length) {
699
+ eligible = eligible.filter((m) => allowedProviders.includes(m.provider));
700
+ }
701
+ if (excludeModels?.length) {
702
+ eligible = eligible.filter((m) => !excludeModels.includes(m.id));
703
+ }
704
+ if (minContextWindow) {
705
+ eligible = eligible.filter((m) => m.contextWindow >= minContextWindow);
706
+ }
707
+ if (requiredCapabilities?.length) {
708
+ eligible = eligible.filter((m) => requiredCapabilities.every((cap) => m.capabilities[cap]));
709
+ }
710
+ if (eligible.length === 0) {
711
+ eligible = catalog.slice(0, 5);
712
+ }
713
+ eligible.sort((a, b) => {
714
+ const costA = a.costPerMillion ? (a.costPerMillion.input + a.costPerMillion.output) / 2 : 999;
715
+ const costB = b.costPerMillion ? (b.costPerMillion.input + b.costPerMillion.output) / 2 : 999;
716
+ return b.contextWindow / 1e5 - costB - (a.contextWindow / 1e5 - costA);
717
+ });
718
+ const best = eligible[0];
719
+ if (!best) {
720
+ return {
721
+ modelId: "unknown",
722
+ providerKey: "openai",
723
+ score: 0,
724
+ reason: `No eligible models found for "${dimension}"`,
725
+ alternatives: []
726
+ };
727
+ }
728
+ return {
729
+ modelId: best.id,
730
+ providerKey: best.provider,
731
+ score: 0,
732
+ reason: `Fallback from catalog (no ranking data for "${dimension}")`,
733
+ alternatives: eligible.slice(1, 4).map((m) => ({
734
+ modelId: m.id,
735
+ providerKey: m.provider,
736
+ score: 0
737
+ }))
738
+ };
739
+ }
740
+ function mergeConstraints(defaults, overrides) {
741
+ if (!defaults)
742
+ return overrides ?? {};
743
+ if (!overrides)
744
+ return defaults;
745
+ return { ...defaults, ...overrides };
746
+ }
605
747
 
606
748
  // src/legacy.ts
607
749
  function mapLegacyProvider(legacy) {
@@ -685,6 +827,7 @@ export {
685
827
  getAIProvider,
686
828
  createProviderFromEnv,
687
829
  createProvider,
830
+ createModelSelector,
688
831
  MODELS,
689
832
  DEFAULT_MODELS
690
833
  };
package/dist/legacy.js CHANGED
@@ -289,22 +289,30 @@ function getDefaultModel(provider) {
289
289
  }
290
290
 
291
291
  // src/factory.ts
292
- import { anthropic } from "@ai-sdk/anthropic";
293
- import { google } from "@ai-sdk/google";
294
- import { mistral } from "@ai-sdk/mistral";
295
- import { openai } from "@ai-sdk/openai";
296
- import { ollama } from "ollama-ai-provider";
292
+ import { createAnthropic } from "@ai-sdk/anthropic";
293
+ import { createGoogleGenerativeAI } from "@ai-sdk/google";
294
+ import { createMistral } from "@ai-sdk/mistral";
295
+ import { createOpenAI } from "@ai-sdk/openai";
296
+ import { createOllama } from "ollama-ai-provider";
297
297
  class BaseProvider {
298
298
  name;
299
299
  model;
300
300
  mode;
301
301
  config;
302
+ transport;
303
+ authMethod;
304
+ apiVersion;
305
+ customHeaders;
302
306
  cachedModel = null;
303
307
  constructor(config) {
304
308
  this.name = config.provider;
305
309
  this.model = config.model ?? DEFAULT_MODELS[config.provider];
306
310
  this.mode = this.determineMode(config);
307
311
  this.config = config;
312
+ this.transport = config.transport;
313
+ this.authMethod = config.authMethod;
314
+ this.apiVersion = config.apiVersion;
315
+ this.customHeaders = config.customHeaders;
308
316
  }
309
317
  getModel() {
310
318
  if (!this.cachedModel) {
@@ -344,81 +352,33 @@ class BaseProvider {
344
352
  return "managed";
345
353
  }
346
354
  createModel() {
347
- const { baseUrl, proxyUrl } = this.config;
355
+ const { baseUrl, proxyUrl, apiKey } = this.config;
356
+ const headers = this.customHeaders;
357
+ if (this.name === "ollama") {
358
+ const provider = createOllama({ baseURL: baseUrl, headers });
359
+ return provider(this.model);
360
+ }
361
+ if (this.mode === "managed" && proxyUrl) {
362
+ const provider = createOpenAI({ baseURL: proxyUrl, apiKey, headers });
363
+ return provider(this.model);
364
+ }
348
365
  switch (this.name) {
349
- case "ollama": {
350
- const originalBaseUrl = process.env.OLLAMA_BASE_URL;
351
- if (baseUrl && baseUrl !== "http://localhost:11434") {
352
- process.env.OLLAMA_BASE_URL = baseUrl;
353
- }
354
- const ollamaModel = ollama(this.model);
355
- if (originalBaseUrl !== undefined) {
356
- process.env.OLLAMA_BASE_URL = originalBaseUrl;
357
- } else if (baseUrl && baseUrl !== "http://localhost:11434") {
358
- delete process.env.OLLAMA_BASE_URL;
359
- }
360
- return ollamaModel;
366
+ case "openai": {
367
+ const provider = createOpenAI({ apiKey, headers });
368
+ return provider(this.model);
369
+ }
370
+ case "anthropic": {
371
+ const provider = createAnthropic({ apiKey, headers });
372
+ return provider(this.model);
373
+ }
374
+ case "mistral": {
375
+ const provider = createMistral({ apiKey, headers });
376
+ return provider(this.model);
377
+ }
378
+ case "gemini": {
379
+ const provider = createGoogleGenerativeAI({ apiKey, headers });
380
+ return provider(this.model);
361
381
  }
362
- case "openai":
363
- if (this.mode === "managed") {
364
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
365
- if (proxyUrl) {
366
- process.env.OPENAI_BASE_URL = proxyUrl;
367
- }
368
- const model = openai(this.model);
369
- if (originalBaseUrl !== undefined) {
370
- process.env.OPENAI_BASE_URL = originalBaseUrl;
371
- } else if (proxyUrl) {
372
- delete process.env.OPENAI_BASE_URL;
373
- }
374
- return model;
375
- }
376
- return openai(this.model);
377
- case "anthropic":
378
- if (this.mode === "managed") {
379
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
380
- if (proxyUrl) {
381
- process.env.OPENAI_BASE_URL = proxyUrl;
382
- }
383
- const model = openai(this.model);
384
- if (originalBaseUrl !== undefined) {
385
- process.env.OPENAI_BASE_URL = originalBaseUrl;
386
- } else if (proxyUrl) {
387
- delete process.env.OPENAI_BASE_URL;
388
- }
389
- return model;
390
- }
391
- return anthropic(this.model);
392
- case "mistral":
393
- if (this.mode === "managed") {
394
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
395
- if (proxyUrl) {
396
- process.env.OPENAI_BASE_URL = proxyUrl;
397
- }
398
- const model = openai(this.model);
399
- if (originalBaseUrl !== undefined) {
400
- process.env.OPENAI_BASE_URL = originalBaseUrl;
401
- } else if (proxyUrl) {
402
- delete process.env.OPENAI_BASE_URL;
403
- }
404
- return model;
405
- }
406
- return mistral(this.model);
407
- case "gemini":
408
- if (this.mode === "managed") {
409
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
410
- if (proxyUrl) {
411
- process.env.OPENAI_BASE_URL = proxyUrl;
412
- }
413
- const model = openai(this.model);
414
- if (originalBaseUrl !== undefined) {
415
- process.env.OPENAI_BASE_URL = originalBaseUrl;
416
- } else if (proxyUrl) {
417
- delete process.env.OPENAI_BASE_URL;
418
- }
419
- return model;
420
- }
421
- return google(this.model);
422
382
  default:
423
383
  throw new Error(`Unknown provider: ${this.name}`);
424
384
  }
@@ -500,13 +460,17 @@ function createProviderFromEnv() {
500
460
  case "ollama":
501
461
  break;
502
462
  }
463
+ const transport = process.env.CONTRACTSPEC_AI_TRANSPORT;
464
+ const apiVersion = process.env.CONTRACTSPEC_AI_API_VERSION;
503
465
  return createProvider({
504
466
  provider,
505
467
  model,
506
468
  apiKey,
507
469
  baseUrl: process.env.OLLAMA_BASE_URL,
508
470
  proxyUrl: process.env.CONTRACTSPEC_AI_PROXY_URL,
509
- organizationId: process.env.CONTRACTSPEC_ORG_ID
471
+ organizationId: process.env.CONTRACTSPEC_ORG_ID,
472
+ transport,
473
+ apiVersion
510
474
  });
511
475
  }
512
476
  function getAvailableProviders() {
@@ -514,35 +478,45 @@ function getAvailableProviders() {
514
478
  providers.push({
515
479
  provider: "ollama",
516
480
  available: true,
517
- mode: "local"
481
+ mode: "local",
482
+ transports: ["rest", "sdk"],
483
+ authMethods: []
518
484
  });
519
485
  const openaiKey = process.env.OPENAI_API_KEY;
520
486
  providers.push({
521
487
  provider: "openai",
522
488
  available: Boolean(openaiKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
523
489
  mode: openaiKey ? "byok" : "managed",
524
- reason: !openaiKey ? "Set OPENAI_API_KEY for BYOK mode" : undefined
490
+ reason: !openaiKey ? "Set OPENAI_API_KEY for BYOK mode" : undefined,
491
+ transports: ["rest", "sdk"],
492
+ authMethods: ["api-key"]
525
493
  });
526
494
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
527
495
  providers.push({
528
496
  provider: "anthropic",
529
497
  available: Boolean(anthropicKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
530
498
  mode: anthropicKey ? "byok" : "managed",
531
- reason: !anthropicKey ? "Set ANTHROPIC_API_KEY for BYOK mode" : undefined
499
+ reason: !anthropicKey ? "Set ANTHROPIC_API_KEY for BYOK mode" : undefined,
500
+ transports: ["rest", "sdk"],
501
+ authMethods: ["api-key"]
532
502
  });
533
503
  const mistralKey = process.env.MISTRAL_API_KEY;
534
504
  providers.push({
535
505
  provider: "mistral",
536
506
  available: Boolean(mistralKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
537
507
  mode: mistralKey ? "byok" : "managed",
538
- reason: !mistralKey ? "Set MISTRAL_API_KEY for BYOK mode" : undefined
508
+ reason: !mistralKey ? "Set MISTRAL_API_KEY for BYOK mode" : undefined,
509
+ transports: ["rest", "sdk"],
510
+ authMethods: ["api-key"]
539
511
  });
540
512
  const geminiKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
541
513
  providers.push({
542
514
  provider: "gemini",
543
515
  available: Boolean(geminiKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
544
516
  mode: geminiKey ? "byok" : "managed",
545
- reason: !geminiKey ? "Set GOOGLE_API_KEY for BYOK mode" : undefined
517
+ reason: !geminiKey ? "Set GOOGLE_API_KEY for BYOK mode" : undefined,
518
+ transports: ["rest", "sdk"],
519
+ authMethods: ["api-key"]
546
520
  });
547
521
  return providers;
548
522
  }