@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.
- package/lib/cjs/azure/azure_foundry.js +379 -0
- package/lib/cjs/azure/azure_foundry.js.map +1 -0
- package/lib/cjs/bedrock/index.js +8 -5
- package/lib/cjs/bedrock/index.js.map +1 -1
- package/lib/cjs/groq/index.js +91 -10
- package/lib/cjs/groq/index.js.map +1 -1
- package/lib/cjs/index.js +2 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/mistral/index.js +2 -1
- package/lib/cjs/mistral/index.js.map +1 -1
- package/lib/cjs/openai/azure_openai.js +72 -0
- package/lib/cjs/openai/azure_openai.js.map +1 -0
- package/lib/cjs/openai/index.js +6 -9
- package/lib/cjs/openai/index.js.map +1 -1
- package/lib/cjs/openai/openai.js +2 -2
- package/lib/cjs/openai/openai.js.map +1 -1
- package/lib/cjs/openai/openai_format.js +138 -0
- package/lib/cjs/openai/openai_format.js.map +1 -0
- package/lib/cjs/vertexai/models/claude.js +5 -3
- package/lib/cjs/vertexai/models/claude.js.map +1 -1
- package/lib/cjs/watsonx/index.js +1 -1
- package/lib/cjs/watsonx/index.js.map +1 -1
- package/lib/cjs/xai/index.js +3 -3
- package/lib/cjs/xai/index.js.map +1 -1
- package/lib/esm/azure/azure_foundry.js +373 -0
- package/lib/esm/azure/azure_foundry.js.map +1 -0
- package/lib/esm/bedrock/index.js +8 -5
- package/lib/esm/bedrock/index.js.map +1 -1
- package/lib/esm/groq/index.js +91 -10
- package/lib/esm/groq/index.js.map +1 -1
- package/lib/esm/index.js +2 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/mistral/index.js +2 -1
- package/lib/esm/mistral/index.js.map +1 -1
- package/lib/esm/openai/azure_openai.js +68 -0
- package/lib/esm/openai/azure_openai.js.map +1 -0
- package/lib/esm/openai/index.js +5 -8
- package/lib/esm/openai/index.js.map +1 -1
- package/lib/esm/openai/openai.js +2 -2
- package/lib/esm/openai/openai.js.map +1 -1
- package/lib/esm/openai/openai_format.js +134 -0
- package/lib/esm/openai/openai_format.js.map +1 -0
- package/lib/esm/src/adobe/firefly.js +115 -0
- package/lib/esm/src/adobe/firefly.js.map +1 -0
- package/lib/esm/src/bedrock/converse.js +278 -0
- package/lib/esm/src/bedrock/converse.js.map +1 -0
- package/lib/esm/src/bedrock/index.js +797 -0
- package/lib/esm/src/bedrock/index.js.map +1 -0
- package/lib/esm/src/bedrock/nova-image-payload.js +203 -0
- package/lib/esm/src/bedrock/nova-image-payload.js.map +1 -0
- package/lib/esm/src/bedrock/payloads.js +2 -0
- package/lib/esm/src/bedrock/payloads.js.map +1 -0
- package/lib/esm/src/bedrock/s3.js +99 -0
- package/lib/esm/src/bedrock/s3.js.map +1 -0
- package/lib/esm/src/groq/index.js +130 -0
- package/lib/esm/src/groq/index.js.map +1 -0
- package/lib/esm/src/huggingface_ie.js +196 -0
- package/lib/esm/src/huggingface_ie.js.map +1 -0
- package/lib/esm/src/index.js +13 -0
- package/lib/esm/src/index.js.map +1 -0
- package/lib/esm/src/mistral/index.js +167 -0
- package/lib/esm/src/mistral/index.js.map +1 -0
- package/lib/esm/src/mistral/types.js +80 -0
- package/lib/esm/src/mistral/types.js.map +1 -0
- package/{src/openai/azure.ts → lib/esm/src/openai/azure.js} +7 -34
- package/lib/esm/src/openai/azure.js.map +1 -0
- package/lib/esm/src/openai/index.js +463 -0
- package/lib/esm/src/openai/index.js.map +1 -0
- package/lib/esm/src/openai/openai.js +14 -0
- package/lib/esm/src/openai/openai.js.map +1 -0
- package/lib/esm/src/replicate.js +268 -0
- package/lib/esm/src/replicate.js.map +1 -0
- package/lib/esm/src/test/TestErrorCompletionStream.js +16 -0
- package/lib/esm/src/test/TestErrorCompletionStream.js.map +1 -0
- package/lib/esm/src/test/TestValidationErrorCompletionStream.js +20 -0
- package/lib/esm/src/test/TestValidationErrorCompletionStream.js.map +1 -0
- package/lib/esm/src/test/index.js +91 -0
- package/lib/esm/src/test/index.js.map +1 -0
- package/lib/esm/src/test/utils.js +25 -0
- package/lib/esm/src/test/utils.js.map +1 -0
- package/lib/esm/src/togetherai/index.js +122 -0
- package/lib/esm/src/togetherai/index.js.map +1 -0
- package/lib/esm/src/togetherai/interfaces.js +2 -0
- package/lib/esm/src/togetherai/interfaces.js.map +1 -0
- package/lib/esm/src/vertexai/debug.js +6 -0
- package/lib/esm/src/vertexai/debug.js.map +1 -0
- package/lib/esm/src/vertexai/embeddings/embeddings-image.js +24 -0
- package/lib/esm/src/vertexai/embeddings/embeddings-image.js.map +1 -0
- package/lib/esm/src/vertexai/embeddings/embeddings-text.js +20 -0
- package/lib/esm/src/vertexai/embeddings/embeddings-text.js.map +1 -0
- package/lib/esm/src/vertexai/index.js +270 -0
- package/lib/esm/src/vertexai/index.js.map +1 -0
- package/lib/esm/src/vertexai/models/claude.js +370 -0
- package/lib/esm/src/vertexai/models/claude.js.map +1 -0
- package/lib/esm/src/vertexai/models/gemini.js +700 -0
- package/lib/esm/src/vertexai/models/gemini.js.map +1 -0
- package/lib/esm/src/vertexai/models/imagen.js +310 -0
- package/lib/esm/src/vertexai/models/imagen.js.map +1 -0
- package/lib/esm/src/vertexai/models/llama.js +178 -0
- package/lib/esm/src/vertexai/models/llama.js.map +1 -0
- package/lib/esm/src/vertexai/models.js +21 -0
- package/lib/esm/src/vertexai/models.js.map +1 -0
- package/lib/esm/src/watsonx/index.js +157 -0
- package/lib/esm/src/watsonx/index.js.map +1 -0
- package/lib/esm/src/watsonx/interfaces.js +2 -0
- package/lib/esm/src/watsonx/interfaces.js.map +1 -0
- package/lib/esm/src/xai/index.js +64 -0
- package/lib/esm/src/xai/index.js.map +1 -0
- package/lib/esm/tsconfig.tsbuildinfo +1 -0
- package/lib/esm/vertexai/models/claude.js +5 -3
- package/lib/esm/vertexai/models/claude.js.map +1 -1
- package/lib/esm/watsonx/index.js +1 -1
- package/lib/esm/watsonx/index.js.map +1 -1
- package/lib/esm/xai/index.js +2 -2
- package/lib/esm/xai/index.js.map +1 -1
- package/lib/types/azure/azure_foundry.d.ts +50 -0
- package/lib/types/azure/azure_foundry.d.ts.map +1 -0
- package/lib/types/bedrock/index.d.ts.map +1 -1
- package/lib/types/groq/index.d.ts +5 -5
- package/lib/types/groq/index.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/mistral/index.d.ts +2 -2
- package/lib/types/mistral/index.d.ts.map +1 -1
- package/lib/types/openai/azure_openai.d.ts +25 -0
- package/lib/types/openai/azure_openai.d.ts.map +1 -0
- package/lib/types/openai/index.d.ts +6 -7
- package/lib/types/openai/index.d.ts.map +1 -1
- package/lib/types/openai/openai.d.ts +2 -2
- package/lib/types/openai/openai.d.ts.map +1 -1
- package/lib/types/openai/openai_format.d.ts +19 -0
- package/lib/types/openai/openai_format.d.ts.map +1 -0
- package/lib/types/src/adobe/firefly.d.ts +29 -0
- package/lib/types/src/bedrock/converse.d.ts +8 -0
- package/lib/types/src/bedrock/index.d.ts +57 -0
- package/lib/types/src/bedrock/nova-image-payload.d.ts +73 -0
- package/lib/types/src/bedrock/payloads.d.ts +11 -0
- package/lib/types/src/bedrock/s3.d.ts +22 -0
- package/lib/types/src/groq/index.d.ts +23 -0
- package/lib/types/src/huggingface_ie.d.ts +31 -0
- package/lib/types/src/index.d.ts +12 -0
- package/lib/types/src/mistral/index.d.ts +24 -0
- package/lib/types/src/mistral/types.d.ts +131 -0
- package/lib/types/src/openai/azure.d.ts +19 -0
- package/lib/types/src/openai/index.d.ts +25 -0
- package/lib/types/src/openai/openai.d.ts +14 -0
- package/lib/types/src/replicate.d.ts +44 -0
- package/lib/types/src/test/TestErrorCompletionStream.d.ts +8 -0
- package/lib/types/src/test/TestValidationErrorCompletionStream.d.ts +8 -0
- package/lib/types/src/test/index.d.ts +23 -0
- package/lib/types/src/test/utils.d.ts +4 -0
- package/lib/types/src/togetherai/index.d.ts +22 -0
- package/lib/types/src/togetherai/interfaces.d.ts +95 -0
- package/lib/types/src/vertexai/debug.d.ts +1 -0
- package/lib/types/src/vertexai/embeddings/embeddings-image.d.ts +10 -0
- package/lib/types/src/vertexai/embeddings/embeddings-text.d.ts +9 -0
- package/lib/types/src/vertexai/index.d.ts +49 -0
- package/lib/types/src/vertexai/models/claude.d.ts +17 -0
- package/lib/types/src/vertexai/models/gemini.d.ts +16 -0
- package/lib/types/src/vertexai/models/imagen.d.ts +74 -0
- package/lib/types/src/vertexai/models/llama.d.ts +19 -0
- package/lib/types/src/vertexai/models.d.ts +14 -0
- package/lib/types/src/watsonx/index.d.ts +26 -0
- package/lib/types/src/watsonx/interfaces.d.ts +64 -0
- package/lib/types/src/xai/index.d.ts +18 -0
- package/lib/types/vertexai/models/claude.d.ts.map +1 -1
- package/lib/types/xai/index.d.ts.map +1 -1
- package/package.json +20 -16
- package/src/azure/azure_foundry.ts +450 -0
- package/src/bedrock/index.ts +8 -5
- package/src/groq/index.ts +107 -16
- package/src/index.ts +2 -1
- package/src/mistral/index.ts +3 -2
- package/src/openai/azure_openai.ts +92 -0
- package/src/openai/index.ts +19 -22
- package/src/openai/openai.ts +2 -5
- package/src/openai/openai_format.ts +165 -0
- package/src/vertexai/models/claude.ts +5 -3
- package/src/watsonx/index.ts +5 -5
- 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
|
+
}
|
package/src/openai/index.ts
CHANGED
|
@@ -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 "
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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)
|
|
493
|
-
const messages:
|
|
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:
|
|
491
|
+
content: [{
|
|
492
|
+
type: "text",
|
|
493
|
+
text: response.content ?? ""
|
|
494
|
+
}],
|
|
498
495
|
tool_calls: response.tool_calls,
|
|
499
496
|
});
|
|
500
497
|
}
|
package/src/openai/openai.ts
CHANGED
|
@@ -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
|
|
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) <
|
|
77
|
-
|
|
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
|
|
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
|
|
package/src/watsonx/index.ts
CHANGED
|
@@ -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"
|
|
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
|
|
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 "
|
|
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[]> {
|