@drax/ai-back 3.26.0 → 3.28.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/.env +4 -0
- package/dist/providers/OpenAiProvider.js +91 -14
- package/package.json +3 -3
- package/src/index.ts +3 -1
- package/src/interfaces/IAIProvider.ts +10 -0
- package/src/providers/OpenAiProvider.ts +109 -16
- package/test/OpenAiProvider.test.ts +145 -2
- package/test/setup/MongoInMemory.ts +53 -0
- package/test/setup/TestSetup.ts +248 -0
- package/test/setup/data/admin-role.ts +13 -0
- package/test/setup/data/basic-user.ts +14 -0
- package/test/setup/data/restricted-role.ts +10 -0
- package/test/setup/data/root-user.ts +14 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/IAIProvider.d.ts +9 -1
- package/types/interfaces/IAIProvider.d.ts.map +1 -1
- package/types/providers/OpenAiProvider.d.ts +12 -1
- package/types/providers/OpenAiProvider.d.ts.map +1 -1
package/.env
ADDED
|
@@ -92,6 +92,11 @@ class OpenAiProvider {
|
|
|
92
92
|
userContent: input.userContent,
|
|
93
93
|
memory: input.memory,
|
|
94
94
|
knowledgeBase: input.knowledgeBase,
|
|
95
|
+
tools: input.tools?.map(tool => ({
|
|
96
|
+
name: tool.name,
|
|
97
|
+
description: tool.description,
|
|
98
|
+
parameters: tool.parameters,
|
|
99
|
+
})),
|
|
95
100
|
});
|
|
96
101
|
}
|
|
97
102
|
serializePromptOutput(output) {
|
|
@@ -155,6 +160,58 @@ class OpenAiProvider {
|
|
|
155
160
|
});
|
|
156
161
|
return response.data[0].embedding;
|
|
157
162
|
}
|
|
163
|
+
mapTools(tools = []) {
|
|
164
|
+
return tools.map(tool => ({
|
|
165
|
+
type: "function",
|
|
166
|
+
function: {
|
|
167
|
+
name: tool.name,
|
|
168
|
+
description: tool.description,
|
|
169
|
+
parameters: tool.parameters ?? {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {},
|
|
172
|
+
additionalProperties: false,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
parseToolArguments(args) {
|
|
178
|
+
if (!args) {
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return JSON.parse(args);
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
throw new Error(`Invalid tool arguments: ${args}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
serializeToolOutput(output) {
|
|
189
|
+
if (typeof output === "string") {
|
|
190
|
+
return output;
|
|
191
|
+
}
|
|
192
|
+
if (output === undefined) {
|
|
193
|
+
return "";
|
|
194
|
+
}
|
|
195
|
+
return JSON.stringify(output);
|
|
196
|
+
}
|
|
197
|
+
async buildToolMessages(toolCalls = [], tools = []) {
|
|
198
|
+
const toolMessages = [];
|
|
199
|
+
for (const toolCall of toolCalls) {
|
|
200
|
+
const toolName = toolCall.function?.name;
|
|
201
|
+
const tool = tools.find(t => t.name === toolName);
|
|
202
|
+
if (!tool) {
|
|
203
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
204
|
+
}
|
|
205
|
+
const args = this.parseToolArguments(toolCall.function?.arguments);
|
|
206
|
+
const output = await tool.execute(args);
|
|
207
|
+
toolMessages.push({
|
|
208
|
+
role: "tool",
|
|
209
|
+
tool_call_id: toolCall.id,
|
|
210
|
+
content: this.serializeToolOutput(output),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return toolMessages;
|
|
214
|
+
}
|
|
158
215
|
async prompt(input) {
|
|
159
216
|
if (!input.systemPrompt) {
|
|
160
217
|
throw new Error("systemPrompt required");
|
|
@@ -170,21 +227,41 @@ class OpenAiProvider {
|
|
|
170
227
|
const model = input.model ?? (this.hasImageInput(input) ? this.visionModel ?? this.model : this.model);
|
|
171
228
|
const startedAt = new Date();
|
|
172
229
|
const startTime = performance.now();
|
|
230
|
+
let tokens = 0;
|
|
231
|
+
let inputTokens = 0;
|
|
232
|
+
let outputTokens = 0;
|
|
173
233
|
try {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
234
|
+
const messages = [
|
|
235
|
+
{ role: 'system', content: systemPrompt },
|
|
236
|
+
...this.mapHistory(input.history),
|
|
237
|
+
{ role: 'user', content: userInput },
|
|
238
|
+
];
|
|
239
|
+
const tools = input.tools ?? [];
|
|
240
|
+
const maxIterations = input.toolMaxIterations ?? 5;
|
|
241
|
+
let output;
|
|
242
|
+
for (let iteration = 0; iteration < maxIterations; iteration++) {
|
|
243
|
+
const chatCompletion = await this.client.chat.completions.create({
|
|
244
|
+
messages,
|
|
245
|
+
...(input.zodSchema ? { response_format: zodResponseFormat(input.zodSchema, "event") } : {}),
|
|
246
|
+
...(input.jsonSchema ? { response_format: input.jsonSchema } : {}),
|
|
247
|
+
...(tools.length > 0 ? { tools: this.mapTools(tools) } : {}),
|
|
248
|
+
model: model,
|
|
249
|
+
});
|
|
250
|
+
tokens += chatCompletion.usage?.total_tokens ?? 0;
|
|
251
|
+
inputTokens += chatCompletion.usage?.prompt_tokens ?? 0;
|
|
252
|
+
outputTokens += chatCompletion.usage?.completion_tokens ?? 0;
|
|
253
|
+
const message = chatCompletion.choices[0].message;
|
|
254
|
+
const toolCalls = message.tool_calls ?? [];
|
|
255
|
+
if (toolCalls.length === 0) {
|
|
256
|
+
output = message.content;
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
messages.push(message);
|
|
260
|
+
messages.push(...await this.buildToolMessages(toolCalls, tools));
|
|
261
|
+
}
|
|
262
|
+
if (output === undefined) {
|
|
263
|
+
throw new Error(`Tool max iterations reached: ${maxIterations}`);
|
|
264
|
+
}
|
|
188
265
|
const endTime = performance.now();
|
|
189
266
|
const time = endTime - startTime;
|
|
190
267
|
const endedAt = new Date();
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.28.0",
|
|
7
7
|
"description": "Ai utils",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@drax/ai-share": "^3.18.0",
|
|
22
|
-
"@drax/crud-back": "^3.
|
|
22
|
+
"@drax/crud-back": "^3.28.0",
|
|
23
23
|
"mongoose": "^8.23.0",
|
|
24
24
|
"mongoose-paginate-v2": "^1.8.3"
|
|
25
25
|
},
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"typescript": "^5.9.3",
|
|
45
45
|
"vitest": "^3.0.8"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "f9787aec59da68ec6d84d0c5e3ee3471eb65ad07"
|
|
48
48
|
}
|
package/src/index.ts
CHANGED
|
@@ -27,7 +27,8 @@ import type {
|
|
|
27
27
|
IPromptMessage,
|
|
28
28
|
IPromptMemory,
|
|
29
29
|
IPromptParams,
|
|
30
|
-
IPromptResponse
|
|
30
|
+
IPromptResponse,
|
|
31
|
+
IPromptTool
|
|
31
32
|
} from "./interfaces/IAIProvider.js";
|
|
32
33
|
|
|
33
34
|
export type {
|
|
@@ -37,6 +38,7 @@ export type {
|
|
|
37
38
|
IPromptParams,
|
|
38
39
|
IPromptMessage,
|
|
39
40
|
IPromptMemory,
|
|
41
|
+
IPromptTool,
|
|
40
42
|
IPromptImage,
|
|
41
43
|
IPromptImageDetail,
|
|
42
44
|
IPromptContentPart,
|
|
@@ -32,6 +32,13 @@ interface IPromptMemory {
|
|
|
32
32
|
value: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
interface IPromptTool {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
parameters?: object;
|
|
39
|
+
execute: (args: any) => any | Promise<any>;
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
interface IPromptParams {
|
|
36
43
|
systemPrompt: string,
|
|
37
44
|
userInput?: string,
|
|
@@ -51,6 +58,8 @@ interface IPromptParams {
|
|
|
51
58
|
knowledgeBaseHeader?: string | '[KNOWLEDGE BASE]' | '[BASE DE CONOCIMIENTO]',
|
|
52
59
|
zodSchema?: ZodSchema<any>,
|
|
53
60
|
jsonSchema?: object,
|
|
61
|
+
tools?: IPromptTool[],
|
|
62
|
+
toolMaxIterations?: number,
|
|
54
63
|
model?: string,
|
|
55
64
|
operationTitle?: string,
|
|
56
65
|
operationGroup?: string,
|
|
@@ -78,6 +87,7 @@ export type {
|
|
|
78
87
|
IPromptResponse,
|
|
79
88
|
IPromptMessage,
|
|
80
89
|
IPromptMemory,
|
|
90
|
+
IPromptTool,
|
|
81
91
|
IPromptImage,
|
|
82
92
|
IPromptImageDetail,
|
|
83
93
|
IPromptContentPart,
|
|
@@ -5,7 +5,8 @@ import type {
|
|
|
5
5
|
IPromptContentPart,
|
|
6
6
|
IPromptMessage,
|
|
7
7
|
IPromptParams,
|
|
8
|
-
IPromptResponse
|
|
8
|
+
IPromptResponse,
|
|
9
|
+
IPromptTool
|
|
9
10
|
} from "../interfaces/IAIProvider";
|
|
10
11
|
import type {AILogService} from "../services/AILogService";
|
|
11
12
|
import type {IAILogBase} from "@drax/ai-share";
|
|
@@ -129,6 +130,11 @@ class OpenAiProvider implements IAIProvider{
|
|
|
129
130
|
userContent: input.userContent,
|
|
130
131
|
memory: input.memory,
|
|
131
132
|
knowledgeBase: input.knowledgeBase,
|
|
133
|
+
tools: input.tools?.map(tool => ({
|
|
134
|
+
name: tool.name,
|
|
135
|
+
description: tool.description,
|
|
136
|
+
parameters: tool.parameters,
|
|
137
|
+
})),
|
|
132
138
|
})
|
|
133
139
|
}
|
|
134
140
|
|
|
@@ -221,6 +227,69 @@ class OpenAiProvider implements IAIProvider{
|
|
|
221
227
|
return response.data[0].embedding;
|
|
222
228
|
}
|
|
223
229
|
|
|
230
|
+
protected mapTools(tools: IPromptTool[] = []){
|
|
231
|
+
return tools.map(tool => ({
|
|
232
|
+
type: "function" as const,
|
|
233
|
+
function: {
|
|
234
|
+
name: tool.name,
|
|
235
|
+
description: tool.description,
|
|
236
|
+
parameters: tool.parameters ?? {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {},
|
|
239
|
+
additionalProperties: false,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
}))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
protected parseToolArguments(args: string | undefined){
|
|
246
|
+
if(!args){
|
|
247
|
+
return {}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try{
|
|
251
|
+
return JSON.parse(args)
|
|
252
|
+
}catch(e){
|
|
253
|
+
throw new Error(`Invalid tool arguments: ${args}`)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
protected serializeToolOutput(output: unknown){
|
|
258
|
+
if(typeof output === "string"){
|
|
259
|
+
return output
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if(output === undefined){
|
|
263
|
+
return ""
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return JSON.stringify(output)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
protected async buildToolMessages(toolCalls: any[] = [], tools: IPromptTool[] = []){
|
|
270
|
+
const toolMessages: any[] = []
|
|
271
|
+
|
|
272
|
+
for(const toolCall of toolCalls){
|
|
273
|
+
const toolName = toolCall.function?.name
|
|
274
|
+
const tool = tools.find(t => t.name === toolName)
|
|
275
|
+
|
|
276
|
+
if(!tool){
|
|
277
|
+
throw new Error(`Tool not found: ${toolName}`)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const args = this.parseToolArguments(toolCall.function?.arguments)
|
|
281
|
+
const output = await tool.execute(args)
|
|
282
|
+
|
|
283
|
+
toolMessages.push({
|
|
284
|
+
role: "tool",
|
|
285
|
+
tool_call_id: toolCall.id,
|
|
286
|
+
content: this.serializeToolOutput(output),
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return toolMessages
|
|
291
|
+
}
|
|
292
|
+
|
|
224
293
|
async prompt(input: IPromptParams): Promise<IPromptResponse> {
|
|
225
294
|
|
|
226
295
|
if(!input.systemPrompt){
|
|
@@ -242,25 +311,49 @@ class OpenAiProvider implements IAIProvider{
|
|
|
242
311
|
const model = input.model ?? (this.hasImageInput(input) ? this.visionModel ?? this.model : this.model)
|
|
243
312
|
const startedAt = new Date()
|
|
244
313
|
const startTime = performance.now()
|
|
314
|
+
let tokens = 0
|
|
315
|
+
let inputTokens = 0
|
|
316
|
+
let outputTokens = 0
|
|
245
317
|
|
|
246
318
|
try {
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
319
|
+
const messages: any[] = [
|
|
320
|
+
{role: 'system', content: systemPrompt},
|
|
321
|
+
...this.mapHistory(input.history),
|
|
322
|
+
{role: 'user', content: userInput},
|
|
323
|
+
]
|
|
324
|
+
const tools = input.tools ?? []
|
|
325
|
+
const maxIterations = input.toolMaxIterations ?? 5
|
|
326
|
+
let output: any
|
|
327
|
+
|
|
328
|
+
for(let iteration = 0; iteration < maxIterations; iteration++){
|
|
329
|
+
const chatCompletion = await this.client.chat.completions.create({
|
|
330
|
+
messages,
|
|
331
|
+
|
|
332
|
+
...(input.zodSchema ? {response_format: zodResponseFormat(input.zodSchema, "event")} : {}),
|
|
333
|
+
...(input.jsonSchema ? {response_format: input.jsonSchema} : {}),
|
|
334
|
+
...(tools.length > 0 ? {tools: this.mapTools(tools)} : {}),
|
|
335
|
+
model: model,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
tokens += chatCompletion.usage?.total_tokens ?? 0
|
|
339
|
+
inputTokens += chatCompletion.usage?.prompt_tokens ?? 0
|
|
340
|
+
outputTokens += chatCompletion.usage?.completion_tokens ?? 0
|
|
341
|
+
|
|
342
|
+
const message = chatCompletion.choices[0].message
|
|
343
|
+
const toolCalls = message.tool_calls ?? []
|
|
344
|
+
|
|
345
|
+
if(toolCalls.length === 0){
|
|
346
|
+
output = message.content
|
|
347
|
+
break
|
|
348
|
+
}
|
|
258
349
|
|
|
350
|
+
messages.push(message)
|
|
351
|
+
messages.push(...await this.buildToolMessages(toolCalls, tools))
|
|
352
|
+
}
|
|
259
353
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const outputTokens = chatCompletion.usage.completion_tokens
|
|
354
|
+
if(output === undefined){
|
|
355
|
+
throw new Error(`Tool max iterations reached: ${maxIterations}`)
|
|
356
|
+
}
|
|
264
357
|
|
|
265
358
|
const endTime = performance.now()
|
|
266
359
|
const time = endTime - startTime
|
|
@@ -1,11 +1,66 @@
|
|
|
1
|
-
import {describe, test, expect} from 'vitest'
|
|
1
|
+
import {describe, test, expect, beforeAll, afterAll} from 'vitest'
|
|
2
2
|
import {OpenAiProvider, OpenAiProviderFactory} from "../src";
|
|
3
3
|
import z from "zod";
|
|
4
|
-
import {IPromptMemory, IPromptMessage} from "../src/interfaces/IAIProvider";
|
|
4
|
+
import {IPromptMemory, IPromptMessage, IPromptTool} from "../src/interfaces/IAIProvider";
|
|
5
|
+
import {TestSetup} from "./setup/TestSetup";
|
|
6
|
+
import {existsSync, readFileSync} from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
|
|
9
|
+
function loadEnvFile(){
|
|
10
|
+
const envPaths = [
|
|
11
|
+
path.resolve(process.cwd(), ".env"),
|
|
12
|
+
path.resolve(process.cwd(), "packages/ai/ai-back/.env"),
|
|
13
|
+
]
|
|
14
|
+
const envPath = envPaths.find(filePath => existsSync(filePath))
|
|
15
|
+
|
|
16
|
+
if(!envPath){
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const envContent = readFileSync(envPath, "utf8")
|
|
21
|
+
|
|
22
|
+
for(const line of envContent.split(/\r?\n/)){
|
|
23
|
+
const trimmedLine = line.trim()
|
|
24
|
+
|
|
25
|
+
if(!trimmedLine || trimmedLine.startsWith("#")){
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const separatorIndex = trimmedLine.indexOf("=")
|
|
30
|
+
|
|
31
|
+
if(separatorIndex === -1){
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const key = trimmedLine.slice(0, separatorIndex).trim()
|
|
36
|
+
let value = trimmedLine.slice(separatorIndex + 1).trim()
|
|
37
|
+
|
|
38
|
+
if((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))){
|
|
39
|
+
value = value.slice(1, -1)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if(process.env[key] === undefined || process.env[key] === ""){
|
|
43
|
+
process.env[key] = value
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
loadEnvFile()
|
|
5
49
|
|
|
6
50
|
|
|
7
51
|
describe('OpenAi Test', () => {
|
|
8
52
|
|
|
53
|
+
let testSetup = new TestSetup("mongo")
|
|
54
|
+
|
|
55
|
+
beforeAll(async () => {
|
|
56
|
+
await testSetup.setup()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
afterAll(async () => {
|
|
60
|
+
await testSetup.dropAndClose()
|
|
61
|
+
return
|
|
62
|
+
})
|
|
63
|
+
|
|
9
64
|
test('OpenAi prompt supports image inputs and vision model fallback', async () => {
|
|
10
65
|
let request: any
|
|
11
66
|
|
|
@@ -97,6 +152,94 @@ describe('OpenAi Test', () => {
|
|
|
97
152
|
})
|
|
98
153
|
})
|
|
99
154
|
|
|
155
|
+
test('OpenAi prompt executes tools and sends tool output back to model', async () => {
|
|
156
|
+
const requests: any[] = []
|
|
157
|
+
const weatherTool: IPromptTool = {
|
|
158
|
+
name: 'get_weather',
|
|
159
|
+
description: 'Get weather for a city',
|
|
160
|
+
parameters: {
|
|
161
|
+
type: 'object',
|
|
162
|
+
properties: {
|
|
163
|
+
city: {type: 'string'}
|
|
164
|
+
},
|
|
165
|
+
required: ['city'],
|
|
166
|
+
additionalProperties: false
|
|
167
|
+
},
|
|
168
|
+
execute: async ({city}) => ({city, temperature: 21})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
class MockedOpenAiProvider extends OpenAiProvider {
|
|
172
|
+
constructor() {
|
|
173
|
+
super('test-key', 'gpt-4.1-mini')
|
|
174
|
+
this._client = {
|
|
175
|
+
chat: {
|
|
176
|
+
completions: {
|
|
177
|
+
create: async (payload: any) => {
|
|
178
|
+
requests.push(payload)
|
|
179
|
+
|
|
180
|
+
if(requests.length === 1){
|
|
181
|
+
return {
|
|
182
|
+
choices: [{
|
|
183
|
+
message: {
|
|
184
|
+
role: 'assistant',
|
|
185
|
+
content: null,
|
|
186
|
+
tool_calls: [{
|
|
187
|
+
id: 'call_123',
|
|
188
|
+
type: 'function',
|
|
189
|
+
function: {
|
|
190
|
+
name: 'get_weather',
|
|
191
|
+
arguments: '{"city":"Buenos Aires"}'
|
|
192
|
+
}
|
|
193
|
+
}]
|
|
194
|
+
}
|
|
195
|
+
}],
|
|
196
|
+
usage: {
|
|
197
|
+
total_tokens: 15,
|
|
198
|
+
prompt_tokens: 10,
|
|
199
|
+
completion_tokens: 5
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
choices: [{message: {role: 'assistant', content: '21 grados'}}],
|
|
206
|
+
usage: {
|
|
207
|
+
total_tokens: 9,
|
|
208
|
+
prompt_tokens: 7,
|
|
209
|
+
completion_tokens: 2
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const openAi = new MockedOpenAiProvider()
|
|
220
|
+
const r = await openAi.prompt({
|
|
221
|
+
systemPrompt: 'You are an AI assistant.',
|
|
222
|
+
userInput: 'How is the weather in Buenos Aires?',
|
|
223
|
+
tools: [weatherTool]
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
expect(r.output).toBe('21 grados')
|
|
227
|
+
expect(r.tokens).toBe(24)
|
|
228
|
+
expect(requests[0].tools).toEqual([{
|
|
229
|
+
type: 'function',
|
|
230
|
+
function: {
|
|
231
|
+
name: 'get_weather',
|
|
232
|
+
description: 'Get weather for a city',
|
|
233
|
+
parameters: weatherTool.parameters
|
|
234
|
+
}
|
|
235
|
+
}])
|
|
236
|
+
expect(requests[1].messages[3]).toEqual({
|
|
237
|
+
role: 'tool',
|
|
238
|
+
tool_call_id: 'call_123',
|
|
239
|
+
content: '{"city":"Buenos Aires","temperature":21}'
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
100
243
|
test('OpenAi Factory', () => {
|
|
101
244
|
const openAi = OpenAiProviderFactory.instance()
|
|
102
245
|
expect(openAi).toBeInstanceOf(OpenAiProvider)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {mongoose} from '@drax/common-back';
|
|
2
|
+
import {MongoMemoryServer} from 'mongodb-memory-server';
|
|
3
|
+
|
|
4
|
+
class MongoInMemory {
|
|
5
|
+
|
|
6
|
+
mongoServer: MongoMemoryServer
|
|
7
|
+
|
|
8
|
+
async connect() {
|
|
9
|
+
this.mongoServer = await MongoMemoryServer.create();
|
|
10
|
+
if (this.mongoServer.state == "new") {
|
|
11
|
+
await this.mongoServer.start()
|
|
12
|
+
}
|
|
13
|
+
if (!mongoose.connection.readyState) {
|
|
14
|
+
await mongoose.connect(this.mongoServer.getUri(), {dbName: "verifyMASTER"});
|
|
15
|
+
}
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get mongooseStatus() {
|
|
20
|
+
return mongoose.connection.readyState
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get serverStatus() {
|
|
24
|
+
return this.mongoServer.state
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get status() {
|
|
28
|
+
return mongoose.connection.readyState
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async disconnect() {
|
|
32
|
+
await mongoose.disconnect();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async dropData() {
|
|
36
|
+
const collections = await mongoose.connection.listCollections()
|
|
37
|
+
for (let collection of collections) {
|
|
38
|
+
console.log(`Dropping collection: ${collection.name}`)
|
|
39
|
+
await mongoose.connection.dropCollection(collection.name)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async dropAndClose() {
|
|
45
|
+
if (this.mongoServer) {
|
|
46
|
+
await mongoose.connection.dropDatabase();
|
|
47
|
+
await mongoose.connection.close();
|
|
48
|
+
await this.mongoServer.stop();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default MongoInMemory
|