symposium 2.4.3 → 3.0.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 +509 -219
- package/CLAUDE.md +101 -0
- package/Contexts/MCPResource.js +19 -0
- package/{GetContextTool.js → GetContextToolkit.js} +5 -5
- package/InputChannel.js +42 -0
- package/MCPServer.js +160 -0
- package/MIGRATION.md +369 -0
- package/Model.js +32 -25
- package/Models/AnthropicModel.js +66 -20
- package/Models/GrokModel.js +8 -8
- package/Models/GroqModel.js +61 -35
- package/Models/LegacyOpenAIModel.js +61 -35
- package/Models/OllamaModel.js +57 -31
- package/Models/OpenAIModel.js +74 -20
- package/README.md +458 -396
- package/Summarizer.js +5 -5
- package/Symposium.js +12 -12
- package/{Tool.js → Toolkit.js} +4 -4
- package/index.js +10 -2
- package/package.json +7 -3
- package/test/agent.test.js +698 -0
- package/test/helpers/mockSdk.js +52 -0
- package/test/mcp.test.js +216 -0
- package/test/models/anthropic.test.js +135 -0
- package/test/models/groq.test.js +71 -0
- package/test/models/legacyOpenai.test.js +87 -0
- package/test/models/ollama.test.js +90 -0
- package/test/models/openai.test.js +168 -0
- package/BufferedEventEmitter.js +0 -28
package/Models/GroqModel.js
CHANGED
|
@@ -27,16 +27,16 @@ export default class GroqModel extends Model {
|
|
|
27
27
|
return this.groq;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async generate(model, thread,
|
|
31
|
-
const parsed = this.parseOptions(options,
|
|
30
|
+
async *generate(model, thread, tools = [], options = {}) {
|
|
31
|
+
const parsed = this.parseOptions(options, tools);
|
|
32
32
|
options = parsed.options;
|
|
33
|
-
|
|
33
|
+
tools = parsed.tools;
|
|
34
34
|
|
|
35
35
|
let messages = thread.messages;
|
|
36
36
|
|
|
37
|
-
if (
|
|
38
|
-
// Se il modello non supporta nativamente
|
|
39
|
-
const
|
|
37
|
+
if (tools.length && !model.tools) {
|
|
38
|
+
// Se il modello non supporta nativamente gli strumenti, inserisco il prompt ad hoc come ultimo messaggio di sistema
|
|
39
|
+
const tools_prompt = this.promptFromTools(options, tools);
|
|
40
40
|
let system_messages = [], other_messages = [], first_found = false;
|
|
41
41
|
for (let message of messages) {
|
|
42
42
|
if (!first_found && message.role !== 'system')
|
|
@@ -48,10 +48,10 @@ export default class GroqModel extends Model {
|
|
|
48
48
|
other_messages.push(message);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
system_messages.push(new Message('system',
|
|
51
|
+
system_messages.push(new Message('system', tools_prompt));
|
|
52
52
|
|
|
53
53
|
messages = [...system_messages, ...other_messages];
|
|
54
|
-
|
|
54
|
+
tools = [];
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const convertedMessages = [];
|
|
@@ -61,45 +61,71 @@ export default class GroqModel extends Model {
|
|
|
61
61
|
const completion_payload = {
|
|
62
62
|
model: model.name,
|
|
63
63
|
messages: convertedMessages,
|
|
64
|
-
tools:
|
|
64
|
+
tools: tools.map(t => ({
|
|
65
65
|
type: 'function',
|
|
66
|
-
function:
|
|
66
|
+
function: t,
|
|
67
67
|
})),
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
if (options.
|
|
70
|
+
if (options.force_tool) {
|
|
71
71
|
completion_payload.tool_choice = {
|
|
72
72
|
type: 'function',
|
|
73
|
-
function: {name: options.
|
|
73
|
+
function: {name: options.force_tool},
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (!completion_payload.tools.length)
|
|
78
78
|
delete completion_payload.tools;
|
|
79
79
|
|
|
80
|
-
const
|
|
81
|
-
const completion = chatCompletion.choices[0].message;
|
|
80
|
+
const stream = await this.getGroq().chat.completions.create({...completion_payload, stream: true});
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
message_content.push({type: 'text', content: completion.content});
|
|
82
|
+
let fullText = '';
|
|
83
|
+
const toolBuffer = new Map();
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
for await (const chunk of stream) {
|
|
86
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
87
|
+
if (!delta)
|
|
88
|
+
continue;
|
|
89
|
+
|
|
90
|
+
if (delta.content) {
|
|
91
|
+
fullText += delta.content;
|
|
92
|
+
yield {type: 'text_delta', content: delta.content};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (delta.tool_calls) {
|
|
96
|
+
for (const tc of delta.tool_calls) {
|
|
97
|
+
const idx = tc.index;
|
|
98
|
+
if (!toolBuffer.has(idx))
|
|
99
|
+
toolBuffer.set(idx, {id: '', name: '', argumentsRaw: ''});
|
|
100
|
+
const buf = toolBuffer.get(idx);
|
|
101
|
+
if (tc.id)
|
|
102
|
+
buf.id = tc.id;
|
|
103
|
+
if (tc.function?.name)
|
|
104
|
+
buf.name = tc.function.name;
|
|
105
|
+
if (tc.function?.arguments)
|
|
106
|
+
buf.argumentsRaw += tc.function.arguments;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
const toolCalls = [];
|
|
112
|
+
for (const [, buf] of [...toolBuffer.entries()].sort((a, b) => a[0] - b[0])) {
|
|
113
|
+
const tc = {
|
|
114
|
+
id: buf.id,
|
|
115
|
+
name: buf.name,
|
|
116
|
+
arguments: buf.argumentsRaw ? JSON.parse(buf.argumentsRaw) : {},
|
|
117
|
+
};
|
|
118
|
+
toolCalls.push(tc);
|
|
119
|
+
yield {type: 'tool_call', content: tc};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const message_content = [];
|
|
123
|
+
if (fullText)
|
|
124
|
+
message_content.push({type: 'text', content: fullText});
|
|
125
|
+
|
|
126
|
+
if (toolCalls.length)
|
|
127
|
+
message_content.push({type: 'tool_call', content: toolCalls});
|
|
128
|
+
|
|
103
129
|
return [
|
|
104
130
|
new Message('assistant', message_content),
|
|
105
131
|
];
|
|
@@ -132,7 +158,7 @@ export default class GroqModel extends Model {
|
|
|
132
158
|
});
|
|
133
159
|
break;
|
|
134
160
|
|
|
135
|
-
case '
|
|
161
|
+
case 'tool_call':
|
|
136
162
|
if (model.tools) {
|
|
137
163
|
messages.push({
|
|
138
164
|
role: message.role,
|
|
@@ -149,13 +175,13 @@ export default class GroqModel extends Model {
|
|
|
149
175
|
} else {
|
|
150
176
|
messages.push({
|
|
151
177
|
role: message.role,
|
|
152
|
-
content: c.content.map(
|
|
178
|
+
content: c.content.map(t => '```CALL \n' + t.name + '\n' + JSON.stringify(t.arguments || {}) + '\n```').join("\n\n"),
|
|
153
179
|
name: message.name,
|
|
154
180
|
});
|
|
155
181
|
}
|
|
156
182
|
break;
|
|
157
183
|
|
|
158
|
-
case '
|
|
184
|
+
case 'tool_result':
|
|
159
185
|
if (model.tools) {
|
|
160
186
|
messages.push({
|
|
161
187
|
role: message.role,
|
|
@@ -166,7 +192,7 @@ export default class GroqModel extends Model {
|
|
|
166
192
|
} else {
|
|
167
193
|
messages.push({
|
|
168
194
|
role: 'user',
|
|
169
|
-
content: '
|
|
195
|
+
content: 'TOOL RESPONSE:\n' + JSON.stringify(c.content.response),
|
|
170
196
|
name: message.name,
|
|
171
197
|
});
|
|
172
198
|
}
|
|
@@ -17,16 +17,16 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
17
17
|
return this.openai;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async generate(model, thread,
|
|
21
|
-
const parsed = this.parseOptions(options,
|
|
20
|
+
async *generate(model, thread, tools = [], options = {}) {
|
|
21
|
+
const parsed = this.parseOptions(options, tools);
|
|
22
22
|
options = parsed.options;
|
|
23
|
-
|
|
23
|
+
tools = parsed.tools;
|
|
24
24
|
|
|
25
25
|
let messages = thread.messages;
|
|
26
26
|
|
|
27
|
-
if (
|
|
28
|
-
// Se il modello non supporta nativamente
|
|
29
|
-
const
|
|
27
|
+
if (tools.length && !model.tools) {
|
|
28
|
+
// Se il modello non supporta nativamente gli strumenti, inserisco il prompt ad hoc come ultimo messaggio di sistema
|
|
29
|
+
const tools_prompt = this.promptFromTools(options, tools);
|
|
30
30
|
let system_messages = [], other_messages = [], first_found = false;
|
|
31
31
|
for (let message of messages) {
|
|
32
32
|
if (!first_found && message.role !== 'system')
|
|
@@ -38,10 +38,10 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
38
38
|
other_messages.push(message);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
system_messages.push(new Message('system',
|
|
41
|
+
system_messages.push(new Message('system', tools_prompt));
|
|
42
42
|
|
|
43
43
|
messages = [...system_messages, ...other_messages];
|
|
44
|
-
|
|
44
|
+
tools = [];
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const convertedMessages = [];
|
|
@@ -51,16 +51,16 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
51
51
|
const completion_payload = {
|
|
52
52
|
model: model.name,
|
|
53
53
|
messages: convertedMessages,
|
|
54
|
-
tools:
|
|
54
|
+
tools: tools.map(t => ({
|
|
55
55
|
type: 'function',
|
|
56
|
-
function:
|
|
56
|
+
function: t,
|
|
57
57
|
})),
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
if (options.
|
|
60
|
+
if (options.force_tool) {
|
|
61
61
|
completion_payload.tool_choice = {
|
|
62
62
|
type: 'function',
|
|
63
|
-
function: {name: options.
|
|
63
|
+
function: {name: options.force_tool},
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -70,29 +70,55 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
70
70
|
if (!completion_payload.tools.length)
|
|
71
71
|
delete completion_payload.tools;
|
|
72
72
|
|
|
73
|
-
const
|
|
74
|
-
const completion = chatCompletion.choices[0].message;
|
|
73
|
+
const stream = await this.getOpenAi().chat.completions.create({...completion_payload, stream: true});
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
message_content.push({type: 'text', content: completion.content});
|
|
75
|
+
let fullText = '';
|
|
76
|
+
const toolBuffer = new Map();
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
for await (const chunk of stream) {
|
|
79
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
80
|
+
if (!delta)
|
|
81
|
+
continue;
|
|
82
|
+
|
|
83
|
+
if (delta.content) {
|
|
84
|
+
fullText += delta.content;
|
|
85
|
+
yield {type: 'text_delta', content: delta.content};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (delta.tool_calls) {
|
|
89
|
+
for (const tc of delta.tool_calls) {
|
|
90
|
+
const idx = tc.index;
|
|
91
|
+
if (!toolBuffer.has(idx))
|
|
92
|
+
toolBuffer.set(idx, {id: '', name: '', argumentsRaw: ''});
|
|
93
|
+
const buf = toolBuffer.get(idx);
|
|
94
|
+
if (tc.id)
|
|
95
|
+
buf.id = tc.id;
|
|
96
|
+
if (tc.function?.name)
|
|
97
|
+
buf.name = tc.function.name;
|
|
98
|
+
if (tc.function?.arguments)
|
|
99
|
+
buf.argumentsRaw += tc.function.arguments;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
94
102
|
}
|
|
95
103
|
|
|
104
|
+
const toolCalls = [];
|
|
105
|
+
for (const [, buf] of [...toolBuffer.entries()].sort((a, b) => a[0] - b[0])) {
|
|
106
|
+
const tc = {
|
|
107
|
+
id: buf.id,
|
|
108
|
+
name: buf.name,
|
|
109
|
+
arguments: buf.argumentsRaw ? JSON.parse(buf.argumentsRaw) : {},
|
|
110
|
+
};
|
|
111
|
+
toolCalls.push(tc);
|
|
112
|
+
yield {type: 'tool_call', content: tc};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const message_content = [];
|
|
116
|
+
if (fullText)
|
|
117
|
+
message_content.push({type: 'text', content: fullText});
|
|
118
|
+
|
|
119
|
+
if (toolCalls.length)
|
|
120
|
+
message_content.push({type: 'tool_call', content: toolCalls});
|
|
121
|
+
|
|
96
122
|
return [
|
|
97
123
|
new Message('assistant', message_content),
|
|
98
124
|
];
|
|
@@ -173,7 +199,7 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
173
199
|
}
|
|
174
200
|
break;
|
|
175
201
|
|
|
176
|
-
case '
|
|
202
|
+
case 'tool_call':
|
|
177
203
|
if (model.tools) {
|
|
178
204
|
messages.push({
|
|
179
205
|
role,
|
|
@@ -190,13 +216,13 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
190
216
|
} else {
|
|
191
217
|
messages.push({
|
|
192
218
|
role,
|
|
193
|
-
content: c.content.map(
|
|
219
|
+
content: c.content.map(t => '```CALL \n' + t.name + '\n' + JSON.stringify(t.arguments || {}) + '\n```').join("\n\n"),
|
|
194
220
|
name: message.name,
|
|
195
221
|
});
|
|
196
222
|
}
|
|
197
223
|
break;
|
|
198
224
|
|
|
199
|
-
case '
|
|
225
|
+
case 'tool_result':
|
|
200
226
|
if (model.tools) {
|
|
201
227
|
messages.push({
|
|
202
228
|
role,
|
|
@@ -207,7 +233,7 @@ export default class LegacyOpenAIModel extends Model {
|
|
|
207
233
|
} else {
|
|
208
234
|
messages.push({
|
|
209
235
|
role: 'user',
|
|
210
|
-
content: '
|
|
236
|
+
content: 'TOOL RESPONSE:\n' + JSON.stringify(c.content.response),
|
|
211
237
|
name: message.name,
|
|
212
238
|
});
|
|
213
239
|
}
|
package/Models/OllamaModel.js
CHANGED
|
@@ -3,9 +3,13 @@ import Model from "../Model.js";
|
|
|
3
3
|
import Message from "../Message.js";
|
|
4
4
|
|
|
5
5
|
export default class OllamaModel extends Model {
|
|
6
|
+
getOllama() {
|
|
7
|
+
return ollama;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
async getModels() {
|
|
7
11
|
try {
|
|
8
|
-
const {models} = await
|
|
12
|
+
const {models} = await this.getOllama().list();
|
|
9
13
|
|
|
10
14
|
const map = new Map();
|
|
11
15
|
|
|
@@ -25,16 +29,16 @@ export default class OllamaModel extends Model {
|
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
async generate(model, thread,
|
|
29
|
-
const parsed = this.parseOptions(options,
|
|
32
|
+
async *generate(model, thread, tools = [], options = {}) {
|
|
33
|
+
const parsed = this.parseOptions(options, tools);
|
|
30
34
|
options = parsed.options;
|
|
31
|
-
|
|
35
|
+
tools = parsed.tools;
|
|
32
36
|
|
|
33
37
|
let messages = thread.messages;
|
|
34
38
|
|
|
35
|
-
if (
|
|
36
|
-
// Se il modello non supporta nativamente
|
|
37
|
-
const
|
|
39
|
+
if (tools.length && !model.tools) {
|
|
40
|
+
// Se il modello non supporta nativamente gli strumenti, inserisco il prompt ad hoc come ultimo messaggio di sistema
|
|
41
|
+
const tools_prompt = this.promptFromTools(options, tools);
|
|
38
42
|
let system_messages = [], other_messages = [], first_found = false;
|
|
39
43
|
for (let message of messages) {
|
|
40
44
|
if (!first_found && message.role !== 'system')
|
|
@@ -46,10 +50,10 @@ export default class OllamaModel extends Model {
|
|
|
46
50
|
other_messages.push(message);
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
system_messages.push(new Message('system',
|
|
53
|
+
system_messages.push(new Message('system', tools_prompt));
|
|
50
54
|
|
|
51
55
|
messages = [...system_messages, ...other_messages];
|
|
52
|
-
|
|
56
|
+
tools = [];
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
const convertedMessages = [];
|
|
@@ -59,16 +63,17 @@ export default class OllamaModel extends Model {
|
|
|
59
63
|
const completion_payload = {
|
|
60
64
|
model: model.name,
|
|
61
65
|
messages: convertedMessages,
|
|
62
|
-
tools:
|
|
66
|
+
tools: tools.map(t => ({
|
|
63
67
|
type: 'function',
|
|
64
|
-
function:
|
|
68
|
+
function: t,
|
|
65
69
|
})),
|
|
70
|
+
stream: true,
|
|
66
71
|
};
|
|
67
72
|
|
|
68
|
-
if (options.
|
|
73
|
+
if (options.force_tool) {
|
|
69
74
|
completion_payload.tool_choice = {
|
|
70
75
|
type: 'function',
|
|
71
|
-
function: {name: options.
|
|
76
|
+
function: {name: options.force_tool},
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
|
|
@@ -81,31 +86,52 @@ export default class OllamaModel extends Model {
|
|
|
81
86
|
if (!completion_payload.tools.length)
|
|
82
87
|
delete completion_payload.tools;
|
|
83
88
|
|
|
84
|
-
const
|
|
85
|
-
const completion = chatCompletion.message;
|
|
89
|
+
const stream = await this.getOllama().chat(completion_payload);
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
let fullText = '';
|
|
92
|
+
let fullThinking = '';
|
|
93
|
+
const toolCalls = [];
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
for await (const chunk of stream) {
|
|
96
|
+
const m = chunk.message;
|
|
97
|
+
if (!m)
|
|
98
|
+
continue;
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
type: '
|
|
97
|
-
|
|
100
|
+
if (m.thinking) {
|
|
101
|
+
fullThinking += m.thinking;
|
|
102
|
+
yield {type: 'reasoning_delta', content: m.thinking};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (m.content) {
|
|
106
|
+
fullText += m.content;
|
|
107
|
+
yield {type: 'text_delta', content: m.content};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (m.tool_calls?.length) {
|
|
111
|
+
for (const tool_call of m.tool_calls) {
|
|
98
112
|
if (!tool_call.function)
|
|
99
113
|
throw new Error('Unsupported tool type');
|
|
100
114
|
|
|
101
|
-
|
|
115
|
+
const tc = {
|
|
102
116
|
name: tool_call.function.name,
|
|
103
117
|
arguments: tool_call.function.arguments || {},
|
|
104
118
|
};
|
|
105
|
-
|
|
106
|
-
|
|
119
|
+
toolCalls.push(tc);
|
|
120
|
+
yield {type: 'tool_call', content: tc};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
107
123
|
}
|
|
108
124
|
|
|
125
|
+
const message_content = [];
|
|
126
|
+
if (fullThinking)
|
|
127
|
+
message_content.push({type: 'reasoning', content: fullThinking});
|
|
128
|
+
|
|
129
|
+
if (fullText)
|
|
130
|
+
message_content.push({type: 'text', content: fullText});
|
|
131
|
+
|
|
132
|
+
if (toolCalls.length)
|
|
133
|
+
message_content.push({type: 'tool_call', content: toolCalls});
|
|
134
|
+
|
|
109
135
|
return [
|
|
110
136
|
new Message('assistant', message_content),
|
|
111
137
|
];
|
|
@@ -130,7 +156,7 @@ export default class OllamaModel extends Model {
|
|
|
130
156
|
});
|
|
131
157
|
break;
|
|
132
158
|
|
|
133
|
-
case '
|
|
159
|
+
case 'tool_call':
|
|
134
160
|
if (model.tools) {
|
|
135
161
|
messages.push({
|
|
136
162
|
role,
|
|
@@ -148,12 +174,12 @@ export default class OllamaModel extends Model {
|
|
|
148
174
|
messages.push({
|
|
149
175
|
role,
|
|
150
176
|
thinking: reasoning || undefined,
|
|
151
|
-
content: c.content.map(
|
|
177
|
+
content: c.content.map(t => '```CALL \n' + t.name + '\n' + JSON.stringify(t.arguments || {}) + '\n```').join("\n\n"),
|
|
152
178
|
});
|
|
153
179
|
}
|
|
154
180
|
break;
|
|
155
181
|
|
|
156
|
-
case '
|
|
182
|
+
case 'tool_result':
|
|
157
183
|
if (model.tools) {
|
|
158
184
|
messages.push({
|
|
159
185
|
role: 'tool',
|
|
@@ -163,7 +189,7 @@ export default class OllamaModel extends Model {
|
|
|
163
189
|
} else {
|
|
164
190
|
messages.push({
|
|
165
191
|
role: 'user',
|
|
166
|
-
content: '
|
|
192
|
+
content: 'TOOL RESPONSE:\n' + JSON.stringify(c.content.response),
|
|
167
193
|
});
|
|
168
194
|
}
|
|
169
195
|
break;
|
package/Models/OpenAIModel.js
CHANGED
|
@@ -67,6 +67,15 @@ export default class OpenAIModel extends Model {
|
|
|
67
67
|
audio: false,
|
|
68
68
|
image_generation: false,
|
|
69
69
|
}],
|
|
70
|
+
['gpt-5.5', {
|
|
71
|
+
name: 'gpt-5.5',
|
|
72
|
+
tiktoken: 'gpt-5',
|
|
73
|
+
tokens: 1000000,
|
|
74
|
+
tools: true,
|
|
75
|
+
structured_output: true,
|
|
76
|
+
audio: false,
|
|
77
|
+
image_generation: false,
|
|
78
|
+
}],
|
|
70
79
|
]);
|
|
71
80
|
}
|
|
72
81
|
|
|
@@ -77,16 +86,16 @@ export default class OpenAIModel extends Model {
|
|
|
77
86
|
return this.openai;
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
async generate(model, thread,
|
|
81
|
-
const parsed = this.parseOptions(options,
|
|
89
|
+
async *generate(model, thread, tools = [], options = {}) {
|
|
90
|
+
const parsed = this.parseOptions(options, tools);
|
|
82
91
|
options = parsed.options;
|
|
83
|
-
|
|
92
|
+
tools = parsed.tools;
|
|
84
93
|
|
|
85
94
|
let messages = thread.messages;
|
|
86
95
|
|
|
87
|
-
if (
|
|
88
|
-
// Se il modello non supporta nativamente
|
|
89
|
-
const
|
|
96
|
+
if (tools.length && !model.tools) {
|
|
97
|
+
// Se il modello non supporta nativamente gli strumenti, inserisco il prompt ad hoc come ultimo messaggio di sistema
|
|
98
|
+
const tools_prompt = this.promptFromTools(options, tools);
|
|
90
99
|
let system_messages = [], other_messages = [], first_found = false;
|
|
91
100
|
for (let message of messages) {
|
|
92
101
|
if (!first_found && message.role !== 'system')
|
|
@@ -98,39 +107,39 @@ export default class OpenAIModel extends Model {
|
|
|
98
107
|
other_messages.push(message);
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
system_messages.push(new Message('system',
|
|
110
|
+
system_messages.push(new Message('system', tools_prompt));
|
|
102
111
|
|
|
103
112
|
messages = [...system_messages, ...other_messages];
|
|
104
|
-
|
|
113
|
+
tools = [];
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
const convertedMessages = [];
|
|
108
117
|
for (let m of messages)
|
|
109
118
|
convertedMessages.push(...(await this.convertMessage(m, model)));
|
|
110
119
|
|
|
111
|
-
const
|
|
120
|
+
const apiTools = tools.map(t => ({
|
|
112
121
|
type: 'function',
|
|
113
|
-
...
|
|
122
|
+
...t,
|
|
114
123
|
}));
|
|
115
124
|
|
|
116
125
|
if (model.tools && model.image_generation && options.image_generation)
|
|
117
|
-
|
|
126
|
+
apiTools.push({type: 'image_generation'});
|
|
118
127
|
|
|
119
128
|
const completion_payload = {
|
|
120
129
|
model: model.name,
|
|
121
130
|
input: convertedMessages,
|
|
122
131
|
store: false,
|
|
123
132
|
include: ['reasoning.encrypted_content'],
|
|
124
|
-
tools,
|
|
133
|
+
tools: apiTools,
|
|
125
134
|
reasoning: {
|
|
126
135
|
summary: 'auto',
|
|
127
136
|
},
|
|
128
137
|
};
|
|
129
138
|
|
|
130
|
-
if (options.
|
|
139
|
+
if (options.force_tool) {
|
|
131
140
|
completion_payload.tool_choice = {
|
|
132
141
|
type: 'function',
|
|
133
|
-
name: options.
|
|
142
|
+
name: options.force_tool,
|
|
134
143
|
};
|
|
135
144
|
}
|
|
136
145
|
|
|
@@ -140,7 +149,52 @@ export default class OpenAIModel extends Model {
|
|
|
140
149
|
if (!completion_payload.tools.length)
|
|
141
150
|
delete completion_payload.tools;
|
|
142
151
|
|
|
143
|
-
const
|
|
152
|
+
const stream = this.getOpenAi().responses.stream(completion_payload);
|
|
153
|
+
|
|
154
|
+
for await (const event of stream) {
|
|
155
|
+
switch (event.type) {
|
|
156
|
+
case 'response.output_text.delta':
|
|
157
|
+
if (event.delta)
|
|
158
|
+
yield {type: 'text_delta', content: event.delta};
|
|
159
|
+
break;
|
|
160
|
+
|
|
161
|
+
case 'response.reasoning_summary_text.delta':
|
|
162
|
+
if (event.delta)
|
|
163
|
+
yield {type: 'reasoning_delta', content: event.delta};
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case 'response.output_item.done':
|
|
167
|
+
if (event.item?.type === 'function_call') {
|
|
168
|
+
yield {
|
|
169
|
+
type: 'tool_call',
|
|
170
|
+
content: {
|
|
171
|
+
id: event.item.call_id,
|
|
172
|
+
name: event.item.name,
|
|
173
|
+
arguments: event.item.arguments ? JSON.parse(event.item.arguments) : {},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
} else if (event.item?.type === 'image_generation_call') {
|
|
177
|
+
const mime = event.item.output_format === 'png' ? 'image/png' : 'image/jpeg';
|
|
178
|
+
yield {
|
|
179
|
+
type: 'image',
|
|
180
|
+
content: {
|
|
181
|
+
type: 'base64',
|
|
182
|
+
mime,
|
|
183
|
+
data: event.item.result,
|
|
184
|
+
},
|
|
185
|
+
meta: {
|
|
186
|
+
id: event.item.id,
|
|
187
|
+
status: event.item.status,
|
|
188
|
+
prompt: event.item.revised_prompt,
|
|
189
|
+
size: event.item.size,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const completion = await stream.finalResponse();
|
|
144
198
|
|
|
145
199
|
const message_content = [];
|
|
146
200
|
for (let output of completion.output) {
|
|
@@ -170,7 +224,7 @@ export default class OpenAIModel extends Model {
|
|
|
170
224
|
|
|
171
225
|
case 'function_call':
|
|
172
226
|
message_content.push({
|
|
173
|
-
type: '
|
|
227
|
+
type: 'tool_call',
|
|
174
228
|
content: [
|
|
175
229
|
{
|
|
176
230
|
id: output.call_id,
|
|
@@ -284,7 +338,7 @@ export default class OpenAIModel extends Model {
|
|
|
284
338
|
}
|
|
285
339
|
break;
|
|
286
340
|
|
|
287
|
-
case '
|
|
341
|
+
case 'tool_call':
|
|
288
342
|
if (model.tools) {
|
|
289
343
|
messages.push({
|
|
290
344
|
type: 'function_call',
|
|
@@ -295,12 +349,12 @@ export default class OpenAIModel extends Model {
|
|
|
295
349
|
} else {
|
|
296
350
|
messages.push({
|
|
297
351
|
role,
|
|
298
|
-
content: c.content.map(
|
|
352
|
+
content: c.content.map(t => '```CALL \n' + t.name + '\n' + JSON.stringify(t.arguments || {}) + '\n```').join("\n\n"),
|
|
299
353
|
});
|
|
300
354
|
}
|
|
301
355
|
break;
|
|
302
356
|
|
|
303
|
-
case '
|
|
357
|
+
case 'tool_result':
|
|
304
358
|
if (model.tools) {
|
|
305
359
|
messages.push({
|
|
306
360
|
type: 'function_call_output',
|
|
@@ -310,7 +364,7 @@ export default class OpenAIModel extends Model {
|
|
|
310
364
|
} else {
|
|
311
365
|
messages.push({
|
|
312
366
|
role: 'user',
|
|
313
|
-
content: '
|
|
367
|
+
content: 'TOOL RESPONSE:\n' + JSON.stringify(c.content.response),
|
|
314
368
|
});
|
|
315
369
|
}
|
|
316
370
|
break;
|