@contractspec/lib.ai-providers 2.9.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/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  Website: https://contractspec.io/
4
4
 
5
-
6
5
  **Unified AI provider abstraction** for ContractSpec applications.
7
6
 
8
7
  ## Overview
@@ -15,13 +14,25 @@ This library provides a consistent interface for working with multiple LLM provi
15
14
 
16
15
  ## Supported Providers
17
16
 
18
- | Provider | Local | BYOK | Managed |
19
- |----------|-------|------|---------|
20
- | Ollama | ✅ | - | - |
21
- | OpenAI | - | ✅ | ✅ |
22
- | Anthropic | - | ✅ | ✅ |
23
- | Mistral | - | ✅ | ✅ |
24
- | Google Gemini | - | ✅ | ✅ |
17
+ | Provider | Local | BYOK | Managed |
18
+ | ------------- | ----- | ---- | ------- |
19
+ | Ollama | ✅ | - | - |
20
+ | OpenAI | - | ✅ | ✅ |
21
+ | Anthropic | - | ✅ | ✅ |
22
+ | Mistral | - | ✅ | ✅ |
23
+ | Google Gemini | - | ✅ | ✅ |
24
+
25
+ ### Mistral model presets
26
+
27
+ The bundled Mistral model catalog includes current families used in ContractSpec flows:
28
+
29
+ - `mistral-large-latest`
30
+ - `mistral-medium-latest`
31
+ - `mistral-small-latest`
32
+ - `codestral-latest`
33
+ - `devstral-small-latest`
34
+ - `magistral-medium-latest`
35
+ - `pixtral-large-latest`
25
36
 
26
37
  ## Usage
27
38
 
@@ -90,4 +101,3 @@ const model = getAIProvider(config);
90
101
  - `validateProvider(config)` - Check if provider is properly configured
91
102
  - `getRecommendedModels(provider)` - Get recommended models for a provider
92
103
  - `getAvailableProviders()` - List available providers with status
93
-
@@ -159,11 +159,23 @@ var MODELS = [
159
159
  },
160
160
  costPerMillion: { input: 2, output: 6 }
161
161
  },
162
+ {
163
+ id: "mistral-medium-latest",
164
+ name: "Mistral Medium",
165
+ provider: "mistral",
166
+ contextWindow: 128000,
167
+ capabilities: {
168
+ vision: false,
169
+ tools: true,
170
+ reasoning: false,
171
+ streaming: true
172
+ }
173
+ },
162
174
  {
163
175
  id: "codestral-latest",
164
176
  name: "Codestral",
165
177
  provider: "mistral",
166
- contextWindow: 32000,
178
+ contextWindow: 256000,
167
179
  capabilities: {
168
180
  vision: false,
169
181
  tools: true,
@@ -172,6 +184,42 @@ var MODELS = [
172
184
  },
173
185
  costPerMillion: { input: 0.2, output: 0.6 }
174
186
  },
187
+ {
188
+ id: "devstral-small-latest",
189
+ name: "Devstral Small",
190
+ provider: "mistral",
191
+ contextWindow: 128000,
192
+ capabilities: {
193
+ vision: false,
194
+ tools: true,
195
+ reasoning: true,
196
+ streaming: true
197
+ }
198
+ },
199
+ {
200
+ id: "magistral-medium-latest",
201
+ name: "Magistral Medium",
202
+ provider: "mistral",
203
+ contextWindow: 128000,
204
+ capabilities: {
205
+ vision: false,
206
+ tools: true,
207
+ reasoning: true,
208
+ streaming: true
209
+ }
210
+ },
211
+ {
212
+ id: "pixtral-large-latest",
213
+ name: "Pixtral Large",
214
+ provider: "mistral",
215
+ contextWindow: 128000,
216
+ capabilities: {
217
+ vision: true,
218
+ tools: true,
219
+ reasoning: false,
220
+ streaming: true
221
+ }
222
+ },
175
223
  {
176
224
  id: "mistral-small-latest",
177
225
  name: "Mistral Small",
@@ -240,22 +288,30 @@ function getDefaultModel(provider) {
240
288
  }
241
289
 
242
290
  // src/factory.ts
243
- import { anthropic } from "@ai-sdk/anthropic";
244
- import { google } from "@ai-sdk/google";
245
- import { mistral } from "@ai-sdk/mistral";
246
- import { openai } from "@ai-sdk/openai";
247
- import { ollama } from "ollama-ai-provider";
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";
248
296
  class BaseProvider {
249
297
  name;
250
298
  model;
251
299
  mode;
252
300
  config;
301
+ transport;
302
+ authMethod;
303
+ apiVersion;
304
+ customHeaders;
253
305
  cachedModel = null;
254
306
  constructor(config) {
255
307
  this.name = config.provider;
256
308
  this.model = config.model ?? DEFAULT_MODELS[config.provider];
257
309
  this.mode = this.determineMode(config);
258
310
  this.config = config;
311
+ this.transport = config.transport;
312
+ this.authMethod = config.authMethod;
313
+ this.apiVersion = config.apiVersion;
314
+ this.customHeaders = config.customHeaders;
259
315
  }
260
316
  getModel() {
261
317
  if (!this.cachedModel) {
@@ -295,81 +351,33 @@ class BaseProvider {
295
351
  return "managed";
296
352
  }
297
353
  createModel() {
298
- 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
+ }
299
364
  switch (this.name) {
300
- case "ollama": {
301
- const originalBaseUrl = process.env.OLLAMA_BASE_URL;
302
- if (baseUrl && baseUrl !== "http://localhost:11434") {
303
- process.env.OLLAMA_BASE_URL = baseUrl;
304
- }
305
- const ollamaModel = ollama(this.model);
306
- if (originalBaseUrl !== undefined) {
307
- process.env.OLLAMA_BASE_URL = originalBaseUrl;
308
- } else if (baseUrl && baseUrl !== "http://localhost:11434") {
309
- delete process.env.OLLAMA_BASE_URL;
310
- }
311
- return ollamaModel;
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);
312
380
  }
313
- case "openai":
314
- if (this.mode === "managed") {
315
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
316
- if (proxyUrl) {
317
- process.env.OPENAI_BASE_URL = proxyUrl;
318
- }
319
- const model = openai(this.model);
320
- if (originalBaseUrl !== undefined) {
321
- process.env.OPENAI_BASE_URL = originalBaseUrl;
322
- } else if (proxyUrl) {
323
- delete process.env.OPENAI_BASE_URL;
324
- }
325
- return model;
326
- }
327
- return openai(this.model);
328
- case "anthropic":
329
- if (this.mode === "managed") {
330
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
331
- if (proxyUrl) {
332
- process.env.OPENAI_BASE_URL = proxyUrl;
333
- }
334
- const model = openai(this.model);
335
- if (originalBaseUrl !== undefined) {
336
- process.env.OPENAI_BASE_URL = originalBaseUrl;
337
- } else if (proxyUrl) {
338
- delete process.env.OPENAI_BASE_URL;
339
- }
340
- return model;
341
- }
342
- return anthropic(this.model);
343
- case "mistral":
344
- if (this.mode === "managed") {
345
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
346
- if (proxyUrl) {
347
- process.env.OPENAI_BASE_URL = proxyUrl;
348
- }
349
- const model = openai(this.model);
350
- if (originalBaseUrl !== undefined) {
351
- process.env.OPENAI_BASE_URL = originalBaseUrl;
352
- } else if (proxyUrl) {
353
- delete process.env.OPENAI_BASE_URL;
354
- }
355
- return model;
356
- }
357
- return mistral(this.model);
358
- case "gemini":
359
- if (this.mode === "managed") {
360
- const originalBaseUrl = process.env.OPENAI_BASE_URL;
361
- if (proxyUrl) {
362
- process.env.OPENAI_BASE_URL = proxyUrl;
363
- }
364
- const model = openai(this.model);
365
- if (originalBaseUrl !== undefined) {
366
- process.env.OPENAI_BASE_URL = originalBaseUrl;
367
- } else if (proxyUrl) {
368
- delete process.env.OPENAI_BASE_URL;
369
- }
370
- return model;
371
- }
372
- return google(this.model);
373
381
  default:
374
382
  throw new Error(`Unknown provider: ${this.name}`);
375
383
  }
@@ -451,13 +459,17 @@ function createProviderFromEnv() {
451
459
  case "ollama":
452
460
  break;
453
461
  }
462
+ const transport = process.env.CONTRACTSPEC_AI_TRANSPORT;
463
+ const apiVersion = process.env.CONTRACTSPEC_AI_API_VERSION;
454
464
  return createProvider({
455
465
  provider,
456
466
  model,
457
467
  apiKey,
458
468
  baseUrl: process.env.OLLAMA_BASE_URL,
459
469
  proxyUrl: process.env.CONTRACTSPEC_AI_PROXY_URL,
460
- organizationId: process.env.CONTRACTSPEC_ORG_ID
470
+ organizationId: process.env.CONTRACTSPEC_ORG_ID,
471
+ transport,
472
+ apiVersion
461
473
  });
462
474
  }
463
475
  function getAvailableProviders() {
@@ -465,35 +477,45 @@ function getAvailableProviders() {
465
477
  providers.push({
466
478
  provider: "ollama",
467
479
  available: true,
468
- mode: "local"
480
+ mode: "local",
481
+ transports: ["rest", "sdk"],
482
+ authMethods: []
469
483
  });
470
484
  const openaiKey = process.env.OPENAI_API_KEY;
471
485
  providers.push({
472
486
  provider: "openai",
473
487
  available: Boolean(openaiKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
474
488
  mode: openaiKey ? "byok" : "managed",
475
- 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"]
476
492
  });
477
493
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
478
494
  providers.push({
479
495
  provider: "anthropic",
480
496
  available: Boolean(anthropicKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
481
497
  mode: anthropicKey ? "byok" : "managed",
482
- 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"]
483
501
  });
484
502
  const mistralKey = process.env.MISTRAL_API_KEY;
485
503
  providers.push({
486
504
  provider: "mistral",
487
505
  available: Boolean(mistralKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
488
506
  mode: mistralKey ? "byok" : "managed",
489
- 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"]
490
510
  });
491
511
  const geminiKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
492
512
  providers.push({
493
513
  provider: "gemini",
494
514
  available: Boolean(geminiKey) || Boolean(process.env.CONTRACTSPEC_AI_PROXY_URL),
495
515
  mode: geminiKey ? "byok" : "managed",
496
- 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"]
497
519
  });
498
520
  return providers;
499
521
  }