@llumiverse/drivers 0.20.0 → 0.21.0

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.
Files changed (180) hide show
  1. package/lib/cjs/azure/azure_foundry.js +379 -0
  2. package/lib/cjs/azure/azure_foundry.js.map +1 -0
  3. package/lib/cjs/bedrock/index.js +8 -5
  4. package/lib/cjs/bedrock/index.js.map +1 -1
  5. package/lib/cjs/groq/index.js +91 -10
  6. package/lib/cjs/groq/index.js.map +1 -1
  7. package/lib/cjs/index.js +2 -1
  8. package/lib/cjs/index.js.map +1 -1
  9. package/lib/cjs/mistral/index.js +2 -1
  10. package/lib/cjs/mistral/index.js.map +1 -1
  11. package/lib/cjs/openai/azure_openai.js +72 -0
  12. package/lib/cjs/openai/azure_openai.js.map +1 -0
  13. package/lib/cjs/openai/index.js +6 -9
  14. package/lib/cjs/openai/index.js.map +1 -1
  15. package/lib/cjs/openai/openai.js +2 -2
  16. package/lib/cjs/openai/openai.js.map +1 -1
  17. package/lib/cjs/openai/openai_format.js +138 -0
  18. package/lib/cjs/openai/openai_format.js.map +1 -0
  19. package/lib/cjs/vertexai/models/claude.js +5 -3
  20. package/lib/cjs/vertexai/models/claude.js.map +1 -1
  21. package/lib/cjs/watsonx/index.js +1 -1
  22. package/lib/cjs/watsonx/index.js.map +1 -1
  23. package/lib/cjs/xai/index.js +3 -3
  24. package/lib/cjs/xai/index.js.map +1 -1
  25. package/lib/esm/azure/azure_foundry.js +373 -0
  26. package/lib/esm/azure/azure_foundry.js.map +1 -0
  27. package/lib/esm/bedrock/index.js +8 -5
  28. package/lib/esm/bedrock/index.js.map +1 -1
  29. package/lib/esm/groq/index.js +91 -10
  30. package/lib/esm/groq/index.js.map +1 -1
  31. package/lib/esm/index.js +2 -1
  32. package/lib/esm/index.js.map +1 -1
  33. package/lib/esm/mistral/index.js +2 -1
  34. package/lib/esm/mistral/index.js.map +1 -1
  35. package/lib/esm/openai/azure_openai.js +68 -0
  36. package/lib/esm/openai/azure_openai.js.map +1 -0
  37. package/lib/esm/openai/index.js +5 -8
  38. package/lib/esm/openai/index.js.map +1 -1
  39. package/lib/esm/openai/openai.js +2 -2
  40. package/lib/esm/openai/openai.js.map +1 -1
  41. package/lib/esm/openai/openai_format.js +134 -0
  42. package/lib/esm/openai/openai_format.js.map +1 -0
  43. package/lib/esm/src/adobe/firefly.js +115 -0
  44. package/lib/esm/src/adobe/firefly.js.map +1 -0
  45. package/lib/esm/src/bedrock/converse.js +278 -0
  46. package/lib/esm/src/bedrock/converse.js.map +1 -0
  47. package/lib/esm/src/bedrock/index.js +797 -0
  48. package/lib/esm/src/bedrock/index.js.map +1 -0
  49. package/lib/esm/src/bedrock/nova-image-payload.js +203 -0
  50. package/lib/esm/src/bedrock/nova-image-payload.js.map +1 -0
  51. package/lib/esm/src/bedrock/payloads.js +2 -0
  52. package/lib/esm/src/bedrock/payloads.js.map +1 -0
  53. package/lib/esm/src/bedrock/s3.js +99 -0
  54. package/lib/esm/src/bedrock/s3.js.map +1 -0
  55. package/lib/esm/src/groq/index.js +130 -0
  56. package/lib/esm/src/groq/index.js.map +1 -0
  57. package/lib/esm/src/huggingface_ie.js +196 -0
  58. package/lib/esm/src/huggingface_ie.js.map +1 -0
  59. package/lib/esm/src/index.js +13 -0
  60. package/lib/esm/src/index.js.map +1 -0
  61. package/lib/esm/src/mistral/index.js +167 -0
  62. package/lib/esm/src/mistral/index.js.map +1 -0
  63. package/lib/esm/src/mistral/types.js +80 -0
  64. package/lib/esm/src/mistral/types.js.map +1 -0
  65. package/{src/openai/azure.ts → lib/esm/src/openai/azure.js} +7 -34
  66. package/lib/esm/src/openai/azure.js.map +1 -0
  67. package/lib/esm/src/openai/index.js +463 -0
  68. package/lib/esm/src/openai/index.js.map +1 -0
  69. package/lib/esm/src/openai/openai.js +14 -0
  70. package/lib/esm/src/openai/openai.js.map +1 -0
  71. package/lib/esm/src/replicate.js +268 -0
  72. package/lib/esm/src/replicate.js.map +1 -0
  73. package/lib/esm/src/test/TestErrorCompletionStream.js +16 -0
  74. package/lib/esm/src/test/TestErrorCompletionStream.js.map +1 -0
  75. package/lib/esm/src/test/TestValidationErrorCompletionStream.js +20 -0
  76. package/lib/esm/src/test/TestValidationErrorCompletionStream.js.map +1 -0
  77. package/lib/esm/src/test/index.js +91 -0
  78. package/lib/esm/src/test/index.js.map +1 -0
  79. package/lib/esm/src/test/utils.js +25 -0
  80. package/lib/esm/src/test/utils.js.map +1 -0
  81. package/lib/esm/src/togetherai/index.js +122 -0
  82. package/lib/esm/src/togetherai/index.js.map +1 -0
  83. package/lib/esm/src/togetherai/interfaces.js +2 -0
  84. package/lib/esm/src/togetherai/interfaces.js.map +1 -0
  85. package/lib/esm/src/vertexai/debug.js +6 -0
  86. package/lib/esm/src/vertexai/debug.js.map +1 -0
  87. package/lib/esm/src/vertexai/embeddings/embeddings-image.js +24 -0
  88. package/lib/esm/src/vertexai/embeddings/embeddings-image.js.map +1 -0
  89. package/lib/esm/src/vertexai/embeddings/embeddings-text.js +20 -0
  90. package/lib/esm/src/vertexai/embeddings/embeddings-text.js.map +1 -0
  91. package/lib/esm/src/vertexai/index.js +270 -0
  92. package/lib/esm/src/vertexai/index.js.map +1 -0
  93. package/lib/esm/src/vertexai/models/claude.js +370 -0
  94. package/lib/esm/src/vertexai/models/claude.js.map +1 -0
  95. package/lib/esm/src/vertexai/models/gemini.js +700 -0
  96. package/lib/esm/src/vertexai/models/gemini.js.map +1 -0
  97. package/lib/esm/src/vertexai/models/imagen.js +310 -0
  98. package/lib/esm/src/vertexai/models/imagen.js.map +1 -0
  99. package/lib/esm/src/vertexai/models/llama.js +178 -0
  100. package/lib/esm/src/vertexai/models/llama.js.map +1 -0
  101. package/lib/esm/src/vertexai/models.js +21 -0
  102. package/lib/esm/src/vertexai/models.js.map +1 -0
  103. package/lib/esm/src/watsonx/index.js +157 -0
  104. package/lib/esm/src/watsonx/index.js.map +1 -0
  105. package/lib/esm/src/watsonx/interfaces.js +2 -0
  106. package/lib/esm/src/watsonx/interfaces.js.map +1 -0
  107. package/lib/esm/src/xai/index.js +64 -0
  108. package/lib/esm/src/xai/index.js.map +1 -0
  109. package/lib/esm/tsconfig.tsbuildinfo +1 -0
  110. package/lib/esm/vertexai/models/claude.js +5 -3
  111. package/lib/esm/vertexai/models/claude.js.map +1 -1
  112. package/lib/esm/watsonx/index.js +1 -1
  113. package/lib/esm/watsonx/index.js.map +1 -1
  114. package/lib/esm/xai/index.js +2 -2
  115. package/lib/esm/xai/index.js.map +1 -1
  116. package/lib/types/azure/azure_foundry.d.ts +50 -0
  117. package/lib/types/azure/azure_foundry.d.ts.map +1 -0
  118. package/lib/types/bedrock/index.d.ts.map +1 -1
  119. package/lib/types/groq/index.d.ts +5 -5
  120. package/lib/types/groq/index.d.ts.map +1 -1
  121. package/lib/types/index.d.ts +2 -1
  122. package/lib/types/index.d.ts.map +1 -1
  123. package/lib/types/mistral/index.d.ts +2 -2
  124. package/lib/types/mistral/index.d.ts.map +1 -1
  125. package/lib/types/openai/azure_openai.d.ts +25 -0
  126. package/lib/types/openai/azure_openai.d.ts.map +1 -0
  127. package/lib/types/openai/index.d.ts +6 -7
  128. package/lib/types/openai/index.d.ts.map +1 -1
  129. package/lib/types/openai/openai.d.ts +2 -2
  130. package/lib/types/openai/openai.d.ts.map +1 -1
  131. package/lib/types/openai/openai_format.d.ts +19 -0
  132. package/lib/types/openai/openai_format.d.ts.map +1 -0
  133. package/lib/types/src/adobe/firefly.d.ts +29 -0
  134. package/lib/types/src/bedrock/converse.d.ts +8 -0
  135. package/lib/types/src/bedrock/index.d.ts +57 -0
  136. package/lib/types/src/bedrock/nova-image-payload.d.ts +73 -0
  137. package/lib/types/src/bedrock/payloads.d.ts +11 -0
  138. package/lib/types/src/bedrock/s3.d.ts +22 -0
  139. package/lib/types/src/groq/index.d.ts +23 -0
  140. package/lib/types/src/huggingface_ie.d.ts +31 -0
  141. package/lib/types/src/index.d.ts +12 -0
  142. package/lib/types/src/mistral/index.d.ts +24 -0
  143. package/lib/types/src/mistral/types.d.ts +131 -0
  144. package/lib/types/src/openai/azure.d.ts +19 -0
  145. package/lib/types/src/openai/index.d.ts +25 -0
  146. package/lib/types/src/openai/openai.d.ts +14 -0
  147. package/lib/types/src/replicate.d.ts +44 -0
  148. package/lib/types/src/test/TestErrorCompletionStream.d.ts +8 -0
  149. package/lib/types/src/test/TestValidationErrorCompletionStream.d.ts +8 -0
  150. package/lib/types/src/test/index.d.ts +23 -0
  151. package/lib/types/src/test/utils.d.ts +4 -0
  152. package/lib/types/src/togetherai/index.d.ts +22 -0
  153. package/lib/types/src/togetherai/interfaces.d.ts +95 -0
  154. package/lib/types/src/vertexai/debug.d.ts +1 -0
  155. package/lib/types/src/vertexai/embeddings/embeddings-image.d.ts +10 -0
  156. package/lib/types/src/vertexai/embeddings/embeddings-text.d.ts +9 -0
  157. package/lib/types/src/vertexai/index.d.ts +49 -0
  158. package/lib/types/src/vertexai/models/claude.d.ts +17 -0
  159. package/lib/types/src/vertexai/models/gemini.d.ts +16 -0
  160. package/lib/types/src/vertexai/models/imagen.d.ts +74 -0
  161. package/lib/types/src/vertexai/models/llama.d.ts +19 -0
  162. package/lib/types/src/vertexai/models.d.ts +14 -0
  163. package/lib/types/src/watsonx/index.d.ts +26 -0
  164. package/lib/types/src/watsonx/interfaces.d.ts +64 -0
  165. package/lib/types/src/xai/index.d.ts +18 -0
  166. package/lib/types/vertexai/models/claude.d.ts.map +1 -1
  167. package/lib/types/xai/index.d.ts.map +1 -1
  168. package/package.json +20 -16
  169. package/src/azure/azure_foundry.ts +450 -0
  170. package/src/bedrock/index.ts +8 -5
  171. package/src/groq/index.ts +107 -16
  172. package/src/index.ts +2 -1
  173. package/src/mistral/index.ts +3 -2
  174. package/src/openai/azure_openai.ts +92 -0
  175. package/src/openai/index.ts +19 -22
  176. package/src/openai/openai.ts +2 -5
  177. package/src/openai/openai_format.ts +165 -0
  178. package/src/vertexai/models/claude.ts +5 -3
  179. package/src/watsonx/index.ts +5 -5
  180. package/src/xai/index.ts +2 -3
@@ -0,0 +1,92 @@
1
+ import { DefaultAzureCredential, getBearerTokenProvider } from "@azure/identity";
2
+ import { AIModel, DriverOptions, getModelCapabilities, modelModalitiesToArray, Providers } from "@llumiverse/core";
3
+ import OpenAI, { AzureOpenAI } from "openai";
4
+ import { BaseOpenAIDriver } from "./index.js";
5
+
6
+ export interface AzureOpenAIDriverOptions extends DriverOptions {
7
+
8
+ /**
9
+ * The credentials to use to access Azure OpenAI
10
+ */
11
+ azureADTokenProvider?: any; //type with azure credentials
12
+
13
+ apiKey?: string;
14
+
15
+ endpoint?: string;
16
+
17
+ apiVersion?: string
18
+
19
+ deployment?: string;
20
+
21
+ }
22
+
23
+ export class AzureOpenAIDriver extends BaseOpenAIDriver {
24
+
25
+
26
+ service: AzureOpenAI;
27
+ readonly provider = Providers.azure_openai;
28
+
29
+ //Overload to allow independent instantiation with AzureOpenAI service
30
+ constructor(serviceOrOpts: AzureOpenAI | AzureOpenAIDriverOptions) {
31
+ if (serviceOrOpts instanceof AzureOpenAI) {
32
+ super({});
33
+ this.service = serviceOrOpts;
34
+ return;
35
+ }
36
+ const opts = serviceOrOpts ?? {};
37
+ super(opts);
38
+ if (!opts.azureADTokenProvider && !opts.apiKey) {
39
+ opts.azureADTokenProvider = this.getDefaultCognitiveServicesAuth();
40
+ }
41
+
42
+ this.service = new AzureOpenAI({
43
+ apiKey: opts.apiKey,
44
+ azureADTokenProvider: opts.azureADTokenProvider,
45
+ endpoint: opts.endpoint,
46
+ apiVersion: opts.apiVersion ?? "2024-10-21",
47
+ deployment: opts.deployment
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Get default authentication for Azure Cognitive Services API
53
+ */
54
+ getDefaultCognitiveServicesAuth() {
55
+ const scope = "https://cognitiveservices.azure.com/.default";
56
+ const azureADTokenProvider = getBearerTokenProvider(new DefaultAzureCredential(), scope);
57
+ return azureADTokenProvider;
58
+ }
59
+
60
+ async listModels(): Promise<AIModel[]> {
61
+ return this._listModels();
62
+ }
63
+
64
+ async _listModels(_filter?: (m: OpenAI.Models.Model) => boolean): Promise<AIModel[]> {
65
+ if (!this.service.deploymentName) {
66
+ throw new Error("A specific deployment is not set. Azure OpenAI cannot list deployments. Update your endpoint URL to include the deployment name, e.g., https://your-resource.openai.azure.com/openai/deployments/your-deployment/chat/completions");
67
+ }
68
+
69
+ //Do a test execution to check if the model works and to get the model ID.
70
+ let modelID = this.service.deploymentName;
71
+ try {
72
+ const testResponse = await this.service.chat.completions.create({
73
+ model: this.service.deploymentName,
74
+ messages: [{ role: "user", content: "Hi" }],
75
+ max_tokens: 1,
76
+ });
77
+ modelID = testResponse.model;
78
+ } catch (error) {
79
+ this.logger.error("Failed to test model for Azure OpenAI listing :", error);
80
+ }
81
+ const modelCapability = getModelCapabilities(modelID, "openai");
82
+ return [{
83
+ id: modelID,
84
+ name: this.service.deploymentName,
85
+ provider: this.provider,
86
+ owner: "openai",
87
+ input_modalities: modelModalitiesToArray(modelCapability.input),
88
+ output_modalities: modelModalitiesToArray(modelCapability.output),
89
+ tool_support: modelCapability.tool_support,
90
+ } satisfies AIModel<string>];
91
+ }
92
+ }
@@ -11,6 +11,7 @@ import {
11
11
  ExecutionTokenUsage,
12
12
  JSONSchema,
13
13
  ModelType,
14
+ Providers,
14
15
  ToolDefinition,
15
16
  ToolUse,
16
17
  TrainingJob,
@@ -22,13 +23,11 @@ import {
22
23
  supportsToolUse,
23
24
  } from "@llumiverse/core";
24
25
  import { asyncMap } from "@llumiverse/core/async";
25
- import { formatOpenAILikeMultimodalPrompt } from "@llumiverse/core/formatters";
26
+ import { formatOpenAILikeMultimodalPrompt } from "./openai_format.js";
26
27
  import OpenAI, { AzureOpenAI } from "openai";
28
+ import { ChatCompletionMessageParam } from "openai/resources/chat/completions";
27
29
  import { Stream } from "openai/streaming";
28
30
 
29
- //For code readability
30
- type OpenAIMessageBlock = OpenAI.Chat.Completions.ChatCompletionMessageParam;
31
-
32
31
  //TODO: Do we need a list?, replace with if statements and modernise?
33
32
  const supportFineTunning = new Set([
34
33
  "gpt-3.5-turbo-1106",
@@ -43,14 +42,15 @@ export interface BaseOpenAIDriverOptions extends DriverOptions {
43
42
 
44
43
  export abstract class BaseOpenAIDriver extends AbstractDriver<
45
44
  BaseOpenAIDriverOptions,
46
- OpenAIMessageBlock[]
45
+ ChatCompletionMessageParam[]
47
46
  > {
48
- abstract provider: "azure_openai" | "openai" | "xai";
47
+ //abstract provider: "azure_openai" | "openai" | "xai" | "azure_foundry";
48
+ abstract provider: Providers.openai | Providers.azure_openai | "xai" | Providers.azure_foundry;
49
49
  abstract service: OpenAI | AzureOpenAI;
50
50
 
51
51
  constructor(opts: BaseOpenAIDriverOptions) {
52
52
  super(opts);
53
- this.formatPrompt = formatOpenAILikeMultimodalPrompt as any
53
+ this.formatPrompt = formatOpenAILikeMultimodalPrompt
54
54
  //TODO: better type, we send back OpenAI.Chat.Completions.ChatCompletionMessageParam[] but just not compatible with Function call that we don't use here
55
55
  }
56
56
 
@@ -82,7 +82,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
82
82
  };
83
83
  }
84
84
 
85
- async requestTextCompletionStream(prompt: OpenAIMessageBlock[], options: ExecutionOptions): Promise<any> {
85
+ async requestTextCompletionStream(prompt: ChatCompletionMessageParam[], options: ExecutionOptions): Promise<AsyncIterable<Completion>> {
86
86
  if (options.model_options?._option_id !== "openai-text" && options.model_options?._option_id !== "openai-thinking") {
87
87
  this.logger.warn("Invalid model options", { options: options.model_options });
88
88
  }
@@ -157,7 +157,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
157
157
  return asyncMap(stream, mapFn);
158
158
  }
159
159
 
160
- async requestTextCompletion(prompt: OpenAIMessageBlock[], options: ExecutionOptions): Promise<any> {
160
+ async requestTextCompletion(prompt: ChatCompletionMessageParam[], options: ExecutionOptions): Promise<Completion> {
161
161
  if (options.model_options?._option_id !== "openai-text" && options.model_options?._option_id !== "openai-thinking") {
162
162
  this.logger.warn("Invalid model options", { options: options.model_options });
163
163
  }
@@ -170,7 +170,7 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
170
170
  const toolDefs = getToolDefinitions(options.tools);
171
171
  const useTools: boolean = toolDefs ? supportsToolUse(options.model, "openai") : false;
172
172
 
173
- let conversation = updateConversation(options.conversation as OpenAIMessageBlock[], prompt);
173
+ let conversation = updateConversation(options.conversation as ChatCompletionMessageParam[], prompt);
174
174
 
175
175
  let parsedSchema: JSONSchema | undefined = undefined;
176
176
  let strictMode = false;
@@ -293,12 +293,6 @@ export abstract class BaseOpenAIDriver extends AbstractDriver<
293
293
  const wordBlacklist = ["embed", "whisper", "transcribe", "audio", "moderation", "tts",
294
294
  "realtime", "dall-e", "babbage", "davinci", "codex", "o1-pro"];
295
295
 
296
- if (this.provider === "azure_openai") {
297
- //Azure OpenAI has additional information about the models
298
- result = result.filter((m) => {
299
- return !(m as any)?.capabilities?.embeddings;
300
- });
301
- }
302
296
 
303
297
  //OpenAI has very little information, filtering based on name.
304
298
  result = result.filter((m) => {
@@ -377,7 +371,7 @@ function jobInfo(job: OpenAI.FineTuning.Jobs.FineTuningJob): TrainingJob {
377
371
  }
378
372
  }
379
373
 
380
- function insert_image_detail(messages: OpenAIMessageBlock[], detail_level: string): OpenAIMessageBlock[] {
374
+ function insert_image_detail(messages: ChatCompletionMessageParam[], detail_level: string): ChatCompletionMessageParam[] {
381
375
  if (detail_level == "auto" || detail_level == "low" || detail_level == "high") {
382
376
  for (const message of messages) {
383
377
  if (message.role !== 'assistant' && message.content) {
@@ -395,7 +389,7 @@ function insert_image_detail(messages: OpenAIMessageBlock[], detail_level: strin
395
389
  return messages;
396
390
  }
397
391
 
398
- function convertRoles(messages: OpenAIMessageBlock[], model: string): OpenAIMessageBlock[] {
392
+ function convertRoles(messages: ChatCompletionMessageParam[], model: string): ChatCompletionMessageParam[] {
399
393
  //New openai models use developer role instead of system
400
394
  if (model.includes("o1") || model.includes("o3")) {
401
395
  if (model.includes("o1-mini") || model.includes("o1-preview")) {
@@ -462,7 +456,7 @@ function openAiFinishReason(finish_reason?: string): string | undefined {
462
456
  return finish_reason;
463
457
  }
464
458
 
465
- function updateConversation(conversation: OpenAIMessageBlock[], message: OpenAIMessageBlock[]): OpenAIMessageBlock[] {
459
+ function updateConversation(conversation: ChatCompletionMessageParam[], message: ChatCompletionMessageParam[]): ChatCompletionMessageParam[] {
466
460
  if (!message) {
467
461
  return conversation;
468
462
  }
@@ -489,12 +483,15 @@ export function collectTools(toolCalls?: OpenAI.Chat.Completions.ChatCompletionM
489
483
  return tools.length > 0 ? tools : undefined;
490
484
  }
491
485
 
492
- function createPromptFromResponse(response: OpenAI.Chat.Completions.ChatCompletionMessage) : OpenAIMessageBlock[] {
493
- const messages: OpenAIMessageBlock[] = [];
486
+ function createPromptFromResponse(response: OpenAI.Chat.Completions.ChatCompletionMessage): ChatCompletionMessageParam[] {
487
+ const messages: ChatCompletionMessageParam[] = [];
494
488
  if (response) {
495
489
  messages.push({
496
490
  role: response.role,
497
- content: response.content,
491
+ content: [{
492
+ type: "text",
493
+ text: response.content ?? ""
494
+ }],
498
495
  tool_calls: response.tool_calls,
499
496
  });
500
497
  }
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- import { DriverOptions } from "@llumiverse/core";
3
+ import { DriverOptions, Providers } from "@llumiverse/core";
4
4
  import OpenAI from "openai";
5
5
  import { BaseOpenAIDriver } from "./index.js";
6
6
 
@@ -19,15 +19,12 @@ export interface OpenAIDriverOptions extends DriverOptions {
19
19
  export class OpenAIDriver extends BaseOpenAIDriver {
20
20
 
21
21
  service: OpenAI;
22
- provider: "openai";
22
+ readonly provider = Providers.openai;
23
23
 
24
24
  constructor(opts: OpenAIDriverOptions) {
25
25
  super(opts);
26
26
  this.service = new OpenAI({
27
27
  apiKey: opts.apiKey
28
28
  });
29
- this.provider = "openai";
30
29
  }
31
-
32
-
33
30
  }
@@ -0,0 +1,165 @@
1
+ // This file is used by multiple drivers
2
+ // to format prompts in a way that is compatible with OpenAI's API.
3
+
4
+ import { PromptRole, PromptOptions, PromptSegment } from "@llumiverse/common";
5
+ import { readStreamAsBase64 } from "@llumiverse/core";
6
+
7
+ import type {
8
+ ChatCompletionMessageParam,
9
+ ChatCompletionContentPartText,
10
+ ChatCompletionContentPartImage,
11
+ ChatCompletionUserMessageParam,
12
+ ChatCompletionSystemMessageParam,
13
+ ChatCompletionAssistantMessageParam,
14
+ ChatCompletionToolMessageParam
15
+ } from 'openai/resources/chat/completions';
16
+
17
+ export interface OpenAITextMessage {
18
+ content: string;
19
+ role: 'system' | 'user' | 'assistant' | 'developer';
20
+ }
21
+
22
+ /**
23
+ * OpenAI text only prompts
24
+ * @param segments
25
+ * @returns
26
+ */
27
+ export function formatOpenAILikeTextPrompt(segments: PromptSegment[]): OpenAITextMessage[] {
28
+ const system: OpenAITextMessage[] = [];
29
+ const safety: OpenAITextMessage[] = [];
30
+ const user: OpenAITextMessage[] = [];
31
+
32
+ for (const msg of segments) {
33
+ if (msg.role === PromptRole.system) {
34
+ system.push({ content: msg.content, role: "system" });
35
+ } else if (msg.role === PromptRole.safety) {
36
+ safety.push({ content: "IMPORTANT: " + msg.content, role: "system" });
37
+ } else if (msg.role !== PromptRole.negative && msg.role !== PromptRole.mask && msg.role !== PromptRole.tool) {
38
+ user.push({
39
+ content: msg.content,
40
+ role: msg.role || 'user',
41
+ })
42
+ }
43
+ }
44
+
45
+ // put system messages first and safety last
46
+ return system.concat(user).concat(safety);
47
+ }
48
+
49
+
50
+ export async function formatOpenAILikeMultimodalPrompt(segments: PromptSegment[], opts: PromptOptions & OpenAIPromptFormatterOptions): Promise<ChatCompletionMessageParam[]> {
51
+ const system: ChatCompletionMessageParam[] = [];
52
+ const safety: ChatCompletionMessageParam[] = [];
53
+ const others: ChatCompletionMessageParam[] = [];
54
+
55
+ for (const msg of segments) {
56
+
57
+ const parts: (ChatCompletionContentPartImage | ChatCompletionContentPartText)[] = [];
58
+
59
+ //generate the parts based on PromptSegment
60
+ if (msg.files) {
61
+ for (const file of msg.files) {
62
+ const stream = await file.getStream();
63
+ const data = await readStreamAsBase64(stream);
64
+ parts.push({
65
+ type: "image_url",
66
+ image_url: {
67
+ url: `data:${file.mime_type || "image/jpeg"};base64,${data}`,
68
+ //detail: "auto" //This is modified just before execution to "low" | "high" | "auto"
69
+ },
70
+ })
71
+ }
72
+ }
73
+
74
+ if (msg.content) {
75
+ parts.push({
76
+ text: msg.content,
77
+ type: "text"
78
+ })
79
+ }
80
+
81
+
82
+ if (msg.role === PromptRole.system) {
83
+ // For system messages, filter to only text parts
84
+ const textParts = parts.filter((part): part is ChatCompletionContentPartText => part.type === 'text');
85
+ const systemMsg: ChatCompletionSystemMessageParam = {
86
+ role: "system",
87
+ content: textParts.length === 1 && !msg.files ? textParts[0].text : textParts
88
+ };
89
+ system.push(systemMsg);
90
+
91
+ if (opts.useToolForFormatting && opts.schema) {
92
+ system.forEach(s => {
93
+ if (typeof s.content === 'string') {
94
+ s.content = "TOOL: " + s.content;
95
+ } else if (Array.isArray(s.content)) {
96
+ s.content.forEach((c: any) => {
97
+ if (c.type === "text") c.text = "TOOL: " + c.text;
98
+ });
99
+ }
100
+ });
101
+ }
102
+
103
+ } else if (msg.role === PromptRole.safety) {
104
+ const textParts = parts.filter((part): part is ChatCompletionContentPartText => part.type === 'text');
105
+ const safetyMsg: ChatCompletionSystemMessageParam = {
106
+ role: "system",
107
+ content: textParts
108
+ };
109
+
110
+ if (Array.isArray(safetyMsg.content)) {
111
+ safetyMsg.content.forEach((c: any) => {
112
+ if (c.type === "text") c.text = "DO NOT IGNORE - IMPORTANT: " + c.text;
113
+ });
114
+ }
115
+
116
+ system.push(safetyMsg);
117
+ } else if (msg.role === PromptRole.tool) {
118
+ if (!msg.tool_use_id) {
119
+ throw new Error("Tool use id is required for tool messages")
120
+ }
121
+ const toolMsg: ChatCompletionToolMessageParam = {
122
+ role: "tool",
123
+ tool_call_id: msg.tool_use_id,
124
+ content: msg.content || ""
125
+ };
126
+ others.push(toolMsg);
127
+ } else if (msg.role !== PromptRole.negative && msg.role !== PromptRole.mask) {
128
+ if (msg.role === 'assistant') {
129
+ const assistantMsg: ChatCompletionAssistantMessageParam = {
130
+ role: 'assistant',
131
+ content: parts as (ChatCompletionContentPartText)[]
132
+ };
133
+ others.push(assistantMsg);
134
+ } else {
135
+ const userMsg: ChatCompletionUserMessageParam = {
136
+ role: 'user',
137
+ content: parts
138
+ };
139
+ others.push(userMsg);
140
+ }
141
+ }
142
+
143
+ }
144
+
145
+ if (opts.result_schema && !opts.useToolForFormatting) {
146
+ const schemaMsg: ChatCompletionSystemMessageParam = {
147
+ role: "system",
148
+ content: [{
149
+ type: "text",
150
+ text: "IMPORTANT: only answer using JSON, and respecting the schema included below, between the <response_schema> tags. " + `<response_schema>${JSON.stringify(opts.result_schema)}</response_schema>`
151
+ }]
152
+ };
153
+ system.push(schemaMsg);
154
+ }
155
+
156
+ // put system messages first and safety last
157
+ return ([] as ChatCompletionMessageParam[]).concat(system).concat(others).concat(safety);
158
+
159
+ }
160
+
161
+ export interface OpenAIPromptFormatterOptions {
162
+ multimodal?: boolean
163
+ useToolForFormatting?: boolean
164
+ schema?: Object
165
+ }
@@ -72,11 +72,13 @@ function maxToken(option: StatelessExecutionOptions): number {
72
72
  if (modelOptions && typeof modelOptions.max_tokens === "number") {
73
73
  return modelOptions.max_tokens;
74
74
  } else {
75
+ const thinking_budget = modelOptions?.thinking_budget_tokens ?? 0;
76
+ let maxSupportedTokens = getMaxTokensLimitVertexAi(option.model);
75
77
  // Fallback to the default max tokens limit for the model
76
- if (option.model.includes('claude-3-7-sonnet') && (modelOptions?.thinking_budget_tokens ?? 0) < 64000) {
77
- return 64000; // Claude 3.7 can go up to 128k with a beta header, but when no max tokens is specified, we default to 64k.
78
+ if (option.model.includes('claude-3-7-sonnet') && (modelOptions?.thinking_budget_tokens ?? 0) < 48000) {
79
+ maxSupportedTokens = 64000; // Claude 3.7 can go up to 128k with a beta header, but when no max tokens is specified, we default to 64k.
78
80
  }
79
- return getMaxTokensLimitVertexAi(option.model);
81
+ return Math.min(16000 + thinking_budget, maxSupportedTokens); // Cap to 16k, to avoid taking up too much context window and quota.
80
82
  }
81
83
  }
82
84
 
@@ -31,10 +31,10 @@ export class WatsonxDriver extends AbstractDriver<WatsonxDriverOptions, string>
31
31
 
32
32
  async requestTextCompletion(prompt: string, options: ExecutionOptions): Promise<Completion<any>> {
33
33
  if (options.model_options?._option_id !== "text-fallback") {
34
- this.logger.warn("Invalid model options", {options: options.model_options });
34
+ this.logger.warn("Invalid model options", { options: options.model_options });
35
35
  }
36
36
  options.model_options = options.model_options as TextFallbackOptions | undefined;
37
-
37
+
38
38
  const payload: WatsonxTextGenerationPayload = {
39
39
  model_id: options.model,
40
40
  input: prompt + "\n",
@@ -66,7 +66,7 @@ export class WatsonxDriver extends AbstractDriver<WatsonxDriverOptions, string>
66
66
 
67
67
  async requestTextCompletionStream(prompt: string, options: ExecutionOptions): Promise<AsyncIterable<CompletionChunk>> {
68
68
  if (options.model_options?._option_id !== "text-fallback") {
69
- this.logger.warn("Invalid model options", {options: options.model_options });
69
+ this.logger.warn("Invalid model options", { options: options.model_options });
70
70
  }
71
71
  options.model_options = options.model_options as TextFallbackOptions | undefined;
72
72
  const payload: WatsonxTextGenerationPayload = {
@@ -132,7 +132,7 @@ export class WatsonxDriver extends AbstractDriver<WatsonxDriverOptions, string>
132
132
  if (now < this.authToken.expiration) {
133
133
  return this.authToken.access_token;
134
134
  } else {
135
- this.logger.debug("Token expired, refetching", this.authToken, now)
135
+ this.logger.debug("Token expired, refetching")
136
136
  }
137
137
  }
138
138
  const authToken = await fetch('https://iam.cloud.ibm.com/identity/token', {
@@ -163,7 +163,7 @@ export class WatsonxDriver extends AbstractDriver<WatsonxDriverOptions, string>
163
163
  }
164
164
 
165
165
  if (!options.text) {
166
- throw new Error ("No text provided");
166
+ throw new Error("No text provided");
167
167
  }
168
168
 
169
169
  const payload: GenerateEmbeddingPayload = {
package/src/xai/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AIModel, Completion, DriverOptions, ExecutionOptions, PromptOptions, PromptSegment } from "@llumiverse/core";
2
- import { formatOpenAILikeMultimodalPrompt, OpenAIPromptFormatterOptions } from "@llumiverse/core/formatters";
2
+ import { formatOpenAILikeMultimodalPrompt, OpenAIPromptFormatterOptions } from "../openai/openai_format.js";
3
3
  import { FetchClient } from "@vertesia/api-fetch-client";
4
4
  import OpenAI from "openai";
5
5
  import { BaseOpenAIDriver } from "../openai/index.js";
@@ -14,7 +14,6 @@ export interface xAiDriverOptions extends DriverOptions {
14
14
 
15
15
  export class xAIDriver extends BaseOpenAIDriver {
16
16
 
17
-
18
17
  service: OpenAI;
19
18
  provider: "xai";
20
19
  xai_service: FetchClient;
@@ -33,7 +32,7 @@ export class xAIDriver extends BaseOpenAIDriver {
33
32
  });
34
33
  this.xai_service = new FetchClient(opts.endpoint ?? this.DEFAULT_ENDPOINT ).withAuthCallback(async () => `Bearer ${opts.apiKey}`);
35
34
  this.provider = "xai";
36
- this.formatPrompt = this._formatPrompt;
35
+ //this.formatPrompt = this._formatPrompt; //TODO: fix xai prompt formatting
37
36
  }
38
37
 
39
38
  async _formatPrompt(segments: PromptSegment[], opts: PromptOptions): Promise<OpenAI.Chat.Completions.ChatCompletionMessageParam[]> {