symposium 2.1.10 → 2.2.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/Agent.js +3 -5
- package/Models/DeepSeekModel.js +2 -2
- package/Models/GrokModel.js +2 -2
- package/Models/LegacyOpenAIModel.js +223 -0
- package/Models/OpenAIModel.js +76 -48
- package/package.json +2 -2
package/Agent.js
CHANGED
|
@@ -219,11 +219,9 @@ export default class Agent {
|
|
|
219
219
|
if (response_format && response_format.count <= 100) { // OpenAI does not support structured output if there are more than 100 parameters
|
|
220
220
|
completion_options.response_format = {
|
|
221
221
|
type: 'json_schema',
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
strict: true,
|
|
226
|
-
},
|
|
222
|
+
name: this.utility.function.name,
|
|
223
|
+
schema: response_format.obj,
|
|
224
|
+
strict: true,
|
|
227
225
|
};
|
|
228
226
|
} else {
|
|
229
227
|
completion_options.functions = [
|
package/Models/DeepSeekModel.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import LegacyOpenAIModel from "./LegacyOpenAIModel.js";
|
|
2
2
|
import OpenAI from "openai";
|
|
3
3
|
|
|
4
|
-
export default class DeepSeekModel extends
|
|
4
|
+
export default class DeepSeekModel extends LegacyOpenAIModel {
|
|
5
5
|
async getModels() {
|
|
6
6
|
return new Map([
|
|
7
7
|
['deepseek-chat', {
|
package/Models/GrokModel.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import LegacyOpenAIModel from "./LegacyOpenAIModel.js";
|
|
2
2
|
import OpenAI from "openai";
|
|
3
3
|
|
|
4
|
-
export default class GrokModel extends
|
|
4
|
+
export default class GrokModel extends LegacyOpenAIModel {
|
|
5
5
|
async getModels() {
|
|
6
6
|
return new Map([
|
|
7
7
|
['grok-4', {
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import Model from "../Model.js";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import Message from "../Message.js";
|
|
4
|
+
import {encoding_for_model} from "tiktoken";
|
|
5
|
+
|
|
6
|
+
export default class LegacyOpenAIModel extends Model {
|
|
7
|
+
openai;
|
|
8
|
+
|
|
9
|
+
async getModels() {
|
|
10
|
+
return new Map([]);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getOpenAi() {
|
|
14
|
+
if (!this.openai)
|
|
15
|
+
this.openai = new OpenAI({apiKey: process.env.OPENAI_API_KEY});
|
|
16
|
+
|
|
17
|
+
return this.openai;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async generate(model, thread, functions = [], options = {}) {
|
|
21
|
+
const parsed = this.parseOptions(options, functions);
|
|
22
|
+
options = parsed.options;
|
|
23
|
+
functions = parsed.functions;
|
|
24
|
+
|
|
25
|
+
let messages = thread.messages;
|
|
26
|
+
|
|
27
|
+
if (functions.length && !model.tools) {
|
|
28
|
+
// Se il modello non supporta nativamente le funzioni, inserisco il prompt ad hoc come ultimo messaggio di sistema
|
|
29
|
+
const functions_prompt = this.promptFromFunctions(options, functions);
|
|
30
|
+
let system_messages = [], other_messages = [], first_found = false;
|
|
31
|
+
for (let message of messages) {
|
|
32
|
+
if (!first_found && message.role !== 'system')
|
|
33
|
+
first_found = true;
|
|
34
|
+
|
|
35
|
+
if (!first_found)
|
|
36
|
+
system_messages.push(message);
|
|
37
|
+
else
|
|
38
|
+
other_messages.push(message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
system_messages.push(new Message('system', functions_prompt));
|
|
42
|
+
|
|
43
|
+
messages = [...system_messages, ...other_messages];
|
|
44
|
+
functions = [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const convertedMessages = [];
|
|
48
|
+
for (let m of messages)
|
|
49
|
+
convertedMessages.push(...this.convertMessage(m, model));
|
|
50
|
+
|
|
51
|
+
const completion_payload = {
|
|
52
|
+
model: model.name,
|
|
53
|
+
messages: convertedMessages,
|
|
54
|
+
tools: functions.map(f => ({
|
|
55
|
+
type: 'function',
|
|
56
|
+
function: f,
|
|
57
|
+
})),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (options.force_function) {
|
|
61
|
+
completion_payload.tool_choice = {
|
|
62
|
+
type: 'function',
|
|
63
|
+
function: {name: options.force_function},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (options.response_format)
|
|
68
|
+
completion_payload.response_format = options.response_format;
|
|
69
|
+
|
|
70
|
+
if (!completion_payload.tools.length)
|
|
71
|
+
delete completion_payload.tools;
|
|
72
|
+
|
|
73
|
+
const chatCompletion = await this.getOpenAi().chat.completions.create(completion_payload);
|
|
74
|
+
const completion = chatCompletion.choices[0].message;
|
|
75
|
+
|
|
76
|
+
const message_content = [];
|
|
77
|
+
if (completion.content)
|
|
78
|
+
message_content.push({type: 'text', content: completion.content});
|
|
79
|
+
|
|
80
|
+
if (completion.tool_calls?.length) {
|
|
81
|
+
message_content.push({
|
|
82
|
+
type: 'function',
|
|
83
|
+
content: completion.tool_calls.map(tool_call => {
|
|
84
|
+
if (tool_call.type !== 'function')
|
|
85
|
+
throw new Error('Unsupported tool type ' + tool_call.type);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
id: tool_call.id,
|
|
89
|
+
name: tool_call.function.name,
|
|
90
|
+
arguments: tool_call.function.arguments ? JSON.parse(tool_call.function.arguments) : {},
|
|
91
|
+
};
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return [
|
|
97
|
+
new Message('assistant', message_content),
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async countTokens(thread) {
|
|
102
|
+
try {
|
|
103
|
+
const model = (await this.getModels()).get(thread.state.model);
|
|
104
|
+
const encoder = encoding_for_model(model.tiktoken || model.name);
|
|
105
|
+
|
|
106
|
+
const texts = [];
|
|
107
|
+
for (let message of thread.messages)
|
|
108
|
+
texts.push(message.content.map(m => typeof m.content === 'string' ? m.content : JSON.stringify(m.content)).join(''));
|
|
109
|
+
return encoder.encode(texts.join('')).length;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
throw new Error('Error while counting tokens');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
convertMessage(message, model) {
|
|
116
|
+
const messages = [],
|
|
117
|
+
role = message.role === 'system' ? this.system_role_name : message.role;
|
|
118
|
+
|
|
119
|
+
for (let c of message.content) {
|
|
120
|
+
switch (c.type) {
|
|
121
|
+
case 'text':
|
|
122
|
+
messages.push({
|
|
123
|
+
role,
|
|
124
|
+
content: c.content,
|
|
125
|
+
name: message.name,
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case 'image':
|
|
130
|
+
messages.push({
|
|
131
|
+
role,
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: 'image_url',
|
|
135
|
+
image_url: {
|
|
136
|
+
url: c.content.type === 'base64' ? 'data:' + c.content.mime + ';base64,' + c.content.data : c.content.data,
|
|
137
|
+
detail: c.content.detail || 'auto',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
name: message.name,
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case 'audio':
|
|
146
|
+
if (model.audio) {
|
|
147
|
+
if (c.content.type !== 'base64')
|
|
148
|
+
throw new Error('Audio content must be base64 encoded for this model');
|
|
149
|
+
if (!['audio/mpeg', 'audio/wav'].includes(c.content.mime))
|
|
150
|
+
throw new Error('Audio content must have a valid MIME type');
|
|
151
|
+
|
|
152
|
+
messages.push({
|
|
153
|
+
role,
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: 'input_audio',
|
|
157
|
+
input_audio: {
|
|
158
|
+
data: c.content.data,
|
|
159
|
+
format: c.content.mime === 'audio/mpeg' ? 'mp3' : 'wav',
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
name: message.name,
|
|
164
|
+
});
|
|
165
|
+
} else if (c.content.transcription) {
|
|
166
|
+
messages.push({
|
|
167
|
+
role,
|
|
168
|
+
content: '[transcribed] ' + c.content.transcription,
|
|
169
|
+
name: message.name,
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
throw new Error('Audio content is not supported by this model');
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
|
|
176
|
+
case 'function':
|
|
177
|
+
if (model.tools) {
|
|
178
|
+
messages.push({
|
|
179
|
+
role,
|
|
180
|
+
name: message.name,
|
|
181
|
+
tool_calls: c.content.map(tool_call => ({
|
|
182
|
+
id: tool_call.id,
|
|
183
|
+
type: 'function',
|
|
184
|
+
function: {
|
|
185
|
+
name: tool_call.name,
|
|
186
|
+
arguments: tool_call.arguments ? JSON.stringify(tool_call.arguments) : '{}',
|
|
187
|
+
},
|
|
188
|
+
})),
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
messages.push({
|
|
192
|
+
role,
|
|
193
|
+
content: c.content.map(f => '```CALL \n' + f.name + '\n' + JSON.stringify(f.arguments || {}) + '\n```').join("\n\n"),
|
|
194
|
+
name: message.name,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
|
|
199
|
+
case 'function_response':
|
|
200
|
+
if (model.tools) {
|
|
201
|
+
messages.push({
|
|
202
|
+
role,
|
|
203
|
+
tool_call_id: c.content.id,
|
|
204
|
+
content: JSON.stringify(c.content.response),
|
|
205
|
+
name: message.name,
|
|
206
|
+
});
|
|
207
|
+
} else {
|
|
208
|
+
messages.push({
|
|
209
|
+
role: 'user',
|
|
210
|
+
content: 'FUNCTION RESPONSE:\n' + JSON.stringify(c.content.response),
|
|
211
|
+
name: message.name,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
default:
|
|
217
|
+
throw new Error('Message type unsupported by this model');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return messages;
|
|
222
|
+
}
|
|
223
|
+
}
|
package/Models/OpenAIModel.js
CHANGED
|
@@ -22,6 +22,7 @@ export default class OpenAIModel extends Model {
|
|
|
22
22
|
tools: true,
|
|
23
23
|
structured_output: true,
|
|
24
24
|
audio: true,
|
|
25
|
+
image_generation: true,
|
|
25
26
|
}],
|
|
26
27
|
['gpt-5-mini', {
|
|
27
28
|
name: 'gpt-5-mini',
|
|
@@ -73,7 +74,7 @@ export default class OpenAIModel extends Model {
|
|
|
73
74
|
|
|
74
75
|
const tools = functions.map(f => ({
|
|
75
76
|
type: 'function',
|
|
76
|
-
|
|
77
|
+
...f,
|
|
77
78
|
}));
|
|
78
79
|
|
|
79
80
|
if (model.tools && model.image_generation && options.image_generation)
|
|
@@ -81,44 +82,77 @@ export default class OpenAIModel extends Model {
|
|
|
81
82
|
|
|
82
83
|
const completion_payload = {
|
|
83
84
|
model: model.name,
|
|
84
|
-
|
|
85
|
+
input: convertedMessages,
|
|
86
|
+
store: false,
|
|
87
|
+
include: ['reasoning.encrypted_content'],
|
|
85
88
|
tools,
|
|
89
|
+
reasoning: {
|
|
90
|
+
summary: 'auto',
|
|
91
|
+
},
|
|
86
92
|
};
|
|
87
93
|
|
|
88
94
|
if (options.force_function) {
|
|
89
95
|
completion_payload.tool_choice = {
|
|
90
96
|
type: 'function',
|
|
91
|
-
|
|
97
|
+
name: options.force_function,
|
|
92
98
|
};
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
if (options.response_format)
|
|
96
|
-
completion_payload.
|
|
102
|
+
completion_payload.text = {format: options.response_format};
|
|
97
103
|
|
|
98
104
|
if (!completion_payload.tools.length)
|
|
99
105
|
delete completion_payload.tools;
|
|
100
106
|
|
|
101
|
-
const
|
|
102
|
-
const completion = chatCompletion.choices[0].message;
|
|
107
|
+
const completion = await this.getOpenAi().responses.create(completion_payload);
|
|
103
108
|
|
|
104
109
|
const message_content = [];
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
for (let output of completion.output) {
|
|
111
|
+
switch (output.type) {
|
|
112
|
+
case 'message':
|
|
113
|
+
let text = output.content.map(c => c.text).join('\n');
|
|
114
|
+
message_content.push({type: 'text', content: text});
|
|
115
|
+
break;
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
case 'image_generation_call':
|
|
118
|
+
const mime = output.output_format === 'png' ? 'image/png' : 'image/jpeg';
|
|
119
|
+
message_content.push({
|
|
120
|
+
type: 'image',
|
|
121
|
+
source: {
|
|
122
|
+
type: 'base64',
|
|
123
|
+
media_type: mime,
|
|
124
|
+
data: output.result,
|
|
125
|
+
},
|
|
126
|
+
meta: {
|
|
127
|
+
id: output.id,
|
|
128
|
+
status: output.status,
|
|
129
|
+
prompt: output.revised_prompt,
|
|
130
|
+
size: output.size,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case 'function_call':
|
|
136
|
+
message_content.push({
|
|
137
|
+
type: 'function',
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
id: output.call_id,
|
|
141
|
+
name: output.name,
|
|
142
|
+
arguments: output.arguments ? JSON.parse(output.arguments) : {},
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
});
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 'reasoning':
|
|
149
|
+
message_content.push({
|
|
150
|
+
type: 'reasoning',
|
|
151
|
+
content: output.summary?.length ? output.summary.map(s => s.text).join('\n') : null,
|
|
152
|
+
original: output,
|
|
153
|
+
});
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
122
156
|
}
|
|
123
157
|
|
|
124
158
|
return [
|
|
@@ -150,7 +184,6 @@ export default class OpenAIModel extends Model {
|
|
|
150
184
|
messages.push({
|
|
151
185
|
role,
|
|
152
186
|
content: c.content,
|
|
153
|
-
name: message.name,
|
|
154
187
|
});
|
|
155
188
|
break;
|
|
156
189
|
|
|
@@ -158,15 +191,17 @@ export default class OpenAIModel extends Model {
|
|
|
158
191
|
messages.push({
|
|
159
192
|
role,
|
|
160
193
|
content: [
|
|
161
|
-
{
|
|
162
|
-
type: '
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
194
|
+
c.meta.id ? {
|
|
195
|
+
type: 'image_generation_call',
|
|
196
|
+
id: c.meta.id,
|
|
197
|
+
result: c.source.data,
|
|
198
|
+
status: c.meta.status,
|
|
199
|
+
} : {
|
|
200
|
+
type: 'input_image',
|
|
201
|
+
image_url: c.content.type === 'base64' ? 'data:' + c.content.mime + ';base64,' + c.content.data : c.content.data,
|
|
202
|
+
detail: c.content.detail || 'auto',
|
|
167
203
|
},
|
|
168
204
|
],
|
|
169
|
-
name: message.name,
|
|
170
205
|
});
|
|
171
206
|
break;
|
|
172
207
|
|
|
@@ -188,13 +223,11 @@ export default class OpenAIModel extends Model {
|
|
|
188
223
|
},
|
|
189
224
|
},
|
|
190
225
|
],
|
|
191
|
-
name: message.name,
|
|
192
226
|
});
|
|
193
227
|
} else if (c.content.transcription) {
|
|
194
228
|
messages.push({
|
|
195
229
|
role,
|
|
196
230
|
content: '[transcribed] ' + c.content.transcription,
|
|
197
|
-
name: message.name,
|
|
198
231
|
});
|
|
199
232
|
} else {
|
|
200
233
|
throw new Error('Audio content is not supported by this model');
|
|
@@ -204,22 +237,15 @@ export default class OpenAIModel extends Model {
|
|
|
204
237
|
case 'function':
|
|
205
238
|
if (model.tools) {
|
|
206
239
|
messages.push({
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
type: 'function',
|
|
212
|
-
function: {
|
|
213
|
-
name: tool_call.name,
|
|
214
|
-
arguments: tool_call.arguments ? JSON.stringify(tool_call.arguments) : '{}',
|
|
215
|
-
},
|
|
216
|
-
})),
|
|
240
|
+
type: 'function_call',
|
|
241
|
+
call_id: c.content[0].id,
|
|
242
|
+
name: c.content[0].name,
|
|
243
|
+
arguments: c.content[0].arguments ? JSON.stringify(c.content[0].arguments) : '{}',
|
|
217
244
|
});
|
|
218
245
|
} else {
|
|
219
246
|
messages.push({
|
|
220
247
|
role,
|
|
221
248
|
content: c.content.map(f => '```CALL \n' + f.name + '\n' + JSON.stringify(f.arguments || {}) + '\n```').join("\n\n"),
|
|
222
|
-
name: message.name,
|
|
223
249
|
});
|
|
224
250
|
}
|
|
225
251
|
break;
|
|
@@ -227,20 +253,22 @@ export default class OpenAIModel extends Model {
|
|
|
227
253
|
case 'function_response':
|
|
228
254
|
if (model.tools) {
|
|
229
255
|
messages.push({
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
name: message.name,
|
|
256
|
+
type: 'function_call_output',
|
|
257
|
+
call_id: c.content.id,
|
|
258
|
+
output: JSON.stringify(c.content.response),
|
|
234
259
|
});
|
|
235
260
|
} else {
|
|
236
261
|
messages.push({
|
|
237
262
|
role: 'user',
|
|
238
263
|
content: 'FUNCTION RESPONSE:\n' + JSON.stringify(c.content.response),
|
|
239
|
-
name: message.name,
|
|
240
264
|
});
|
|
241
265
|
}
|
|
242
266
|
break;
|
|
243
267
|
|
|
268
|
+
case 'reasoning':
|
|
269
|
+
messages.push(c.original);
|
|
270
|
+
break;
|
|
271
|
+
|
|
244
272
|
default:
|
|
245
273
|
throw new Error('Message type unsupported by this model');
|
|
246
274
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "symposium",
|
|
4
|
-
"version": "2.1
|
|
4
|
+
"version": "2.2.1",
|
|
5
5
|
"description": "Agents",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"author": "Domenico Giambra",
|
|
8
8
|
"license": "ISC",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@anthropic-ai/sdk": "^0.
|
|
10
|
+
"@anthropic-ai/sdk": "^0.68.0",
|
|
11
11
|
"groq-sdk": "^0.34.0",
|
|
12
12
|
"ollama": "^0.6.0",
|
|
13
13
|
"openai": "^6.0.0",
|