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