symposium 0.4.5 → 0.4.7
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 +48 -17
- package/Message.js +16 -6
- package/Model.js +2 -0
- package/Summarizer.js +3 -2
- package/Thread.js +4 -25
- package/models/AnthropicModel.js +37 -10
- package/models/OpenAIModel.js +40 -14
- package/package.json +1 -1
- package/Response.js +0 -4
package/Agent.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Symposium from "./Symposium.js";
|
|
2
2
|
import Thread from "./Thread.js";
|
|
3
|
+
import Message from "./Message.js";
|
|
3
4
|
|
|
4
5
|
export default class Agent {
|
|
5
6
|
name = 'Agent';
|
|
@@ -88,7 +89,7 @@ export default class Agent {
|
|
|
88
89
|
|
|
89
90
|
async message(thread, text) {
|
|
90
91
|
await this.log('user_message', text);
|
|
91
|
-
thread.
|
|
92
|
+
thread.addMessage('user', text);
|
|
92
93
|
|
|
93
94
|
await this.execute(thread, text);
|
|
94
95
|
}
|
|
@@ -123,7 +124,8 @@ export default class Agent {
|
|
|
123
124
|
async generateCompletion(thread, payload = {}, retry_counter = 1) {
|
|
124
125
|
try {
|
|
125
126
|
const model = Symposium.getModelByName(thread.state.model);
|
|
126
|
-
|
|
127
|
+
const messages = await model.generate(thread, payload, await this.getFunctions());
|
|
128
|
+
return messages.map(m => model.supports_functions ? m : this.parseFunctions(m));
|
|
127
129
|
} catch (error) {
|
|
128
130
|
if (error.response) {
|
|
129
131
|
console.error(error.response.status);
|
|
@@ -148,22 +150,51 @@ export default class Agent {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
parseFunctions(message) {
|
|
154
|
+
const newContent = [];
|
|
155
|
+
for (let m of message.content) {
|
|
156
|
+
if (m.type === 'text' && m.content.match(/```\nCALL [A-Za-z0-9_]+\n(\{.+\}\n)?```/)) {
|
|
157
|
+
const splitted = m.content.split('```');
|
|
158
|
+
for (let text of splitted) {
|
|
159
|
+
const match = text.match(/^CALL ([A-Za-z0-9_]+)\n([\s\S]*)$/);
|
|
160
|
+
if (match)
|
|
161
|
+
m.push({type: 'function', content: {name: match[1], arguments: JSON.parse(match[2] || '{}')}});
|
|
162
|
+
else
|
|
163
|
+
m.push({type: 'text', content: text.trim()});
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
newContent.push(m);
|
|
167
|
+
}
|
|
156
168
|
}
|
|
157
169
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
message.content = newContent;
|
|
171
|
+
return message;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async handleCompletion(thread, completion) {
|
|
175
|
+
let call_function = null;
|
|
176
|
+
for (let message of completion) {
|
|
177
|
+
thread.addDirectMessage(message);
|
|
178
|
+
await this.log('ai_message', message);
|
|
179
|
+
|
|
180
|
+
for (let m of message.content) {
|
|
181
|
+
switch (m.type) {
|
|
182
|
+
case 'text':
|
|
183
|
+
await thread.reply(m.content);
|
|
184
|
+
break;
|
|
185
|
+
|
|
186
|
+
case 'function':
|
|
187
|
+
if (call_function)
|
|
188
|
+
throw new Error('The model replied with more than one function');
|
|
189
|
+
else
|
|
190
|
+
call_function = m.content;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
163
194
|
}
|
|
164
195
|
|
|
165
|
-
if (
|
|
166
|
-
return this.callFunction(thread,
|
|
196
|
+
if (call_function)
|
|
197
|
+
return this.callFunction(thread, call_function);
|
|
167
198
|
else
|
|
168
199
|
return thread.storeState();
|
|
169
200
|
}
|
|
@@ -199,11 +230,11 @@ export default class Agent {
|
|
|
199
230
|
await this.log('function_call', function_call);
|
|
200
231
|
|
|
201
232
|
try {
|
|
202
|
-
const response = await functions.get(function_call.name).tool.callFunction(thread, function_call.name, function_call.
|
|
203
|
-
thread.
|
|
233
|
+
const response = await functions.get(function_call.name).tool.callFunction(thread, function_call.name, function_call.arguments);
|
|
234
|
+
thread.addMessage('function', JSON.stringify(response), function_call.name);
|
|
204
235
|
await this.log('function_response', response);
|
|
205
236
|
} catch (error) {
|
|
206
|
-
thread.
|
|
237
|
+
thread.addMessage('function', JSON.stringify({error}), function_call.name);
|
|
207
238
|
await this.log('function_response', {error});
|
|
208
239
|
}
|
|
209
240
|
|
package/Message.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
export default class Message {
|
|
2
2
|
role;
|
|
3
|
-
|
|
4
|
-
name;
|
|
5
|
-
function_call;
|
|
3
|
+
content = [];
|
|
4
|
+
name = undefined;
|
|
6
5
|
tags = [];
|
|
7
6
|
|
|
8
|
-
constructor(role,
|
|
7
|
+
constructor(role, content = [], name = undefined, tags = []) {
|
|
9
8
|
this.role = role;
|
|
10
|
-
this.text = text;
|
|
11
9
|
this.name = name;
|
|
12
|
-
this.function_call = function_call;
|
|
13
10
|
this.tags = tags;
|
|
11
|
+
|
|
12
|
+
if (typeof content === 'string') {
|
|
13
|
+
this.content = [
|
|
14
|
+
{
|
|
15
|
+
type: 'text',
|
|
16
|
+
content,
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
} else if (typeof content === 'object') {
|
|
20
|
+
this.content = Array.isArray(content) ? content : [content];
|
|
21
|
+
} else {
|
|
22
|
+
throw new Error('Unrecognized message type');
|
|
23
|
+
}
|
|
14
24
|
}
|
|
15
25
|
}
|
package/Model.js
CHANGED
package/Summarizer.js
CHANGED
|
@@ -56,7 +56,7 @@ export default class Summarizer extends MemoryHandler {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
async doSummarize(thread, maxLength) {
|
|
59
|
-
thread.
|
|
59
|
+
thread.addMessage('system', 'Summarize the conversation up to this moment.');
|
|
60
60
|
const summary = await this.agent.generateCompletion(thread, {
|
|
61
61
|
functions: [
|
|
62
62
|
{
|
|
@@ -79,12 +79,13 @@ export default class Summarizer extends MemoryHandler {
|
|
|
79
79
|
if (!summary)
|
|
80
80
|
return false;
|
|
81
81
|
|
|
82
|
+
// TODO: sistemare con nuova interfaccia
|
|
82
83
|
let summarizedThread = thread.clone(false);
|
|
83
84
|
for (let message of thread.messages) {
|
|
84
85
|
if (message.role === 'system' && !message.tags.includes('summary')) {
|
|
85
86
|
summarizedThread.messages.push(message);
|
|
86
87
|
} else {
|
|
87
|
-
summarizedThread.
|
|
88
|
+
summarizedThread.addMessage('system', "This is what happened until now:\n" + summary.function_call.arguments.summary, ['summary']);
|
|
88
89
|
break;
|
|
89
90
|
}
|
|
90
91
|
}
|
package/Thread.js
CHANGED
|
@@ -33,7 +33,7 @@ export default class Thread {
|
|
|
33
33
|
const conv = await Redis.get('thread-' + this.id);
|
|
34
34
|
if (conv) {
|
|
35
35
|
this.state = conv.state || {};
|
|
36
|
-
this.messages = conv.messages.map(m =>
|
|
36
|
+
this.messages = conv.messages.map(m => new Message(m.role, m.content, m.tags));
|
|
37
37
|
return true;
|
|
38
38
|
} else {
|
|
39
39
|
return false;
|
|
@@ -53,33 +53,12 @@ export default class Thread {
|
|
|
53
53
|
}, 0);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
return this.messages.map(m => ({
|
|
58
|
-
role: m.role,
|
|
59
|
-
content: m.text,
|
|
60
|
-
name: m.name || undefined,
|
|
61
|
-
function_call: m.function_call || undefined,
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
addMessage(message) {
|
|
56
|
+
addDirectMessage(message) {
|
|
66
57
|
this.messages.push(message);
|
|
67
58
|
}
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
this.
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
addUserMessage(text, name = null, tags = []) {
|
|
74
|
-
this.messages.push(new Message('user', text, name, null, tags));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
addAssistantMessage(text, function_call = null, tags = []) {
|
|
78
|
-
this.messages.push(new Message('assistant', text, null, function_call, tags));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
addFunctionMessage(response, name = null, tags = []) {
|
|
82
|
-
this.messages.push(new Message('function', JSON.stringify(response), name, null, tags));
|
|
60
|
+
addMessage(role, content = [], name = undefined, tags = []) {
|
|
61
|
+
this.addDirectMessage(new Message(role, content, name, tags));
|
|
83
62
|
}
|
|
84
63
|
|
|
85
64
|
removeMessagesWithTag(tag) {
|
package/models/AnthropicModel.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Model from "../Model.js";
|
|
2
2
|
import Anthropic from '@anthropic-ai/sdk';
|
|
3
|
-
import Response from "../Response.js";
|
|
4
3
|
import Message from "../Message.js";
|
|
5
4
|
|
|
6
5
|
export default class AnthropicModel extends Model {
|
|
@@ -34,23 +33,51 @@ export default class AnthropicModel extends Model {
|
|
|
34
33
|
|
|
35
34
|
const message = await this.getAnthropic().messages.create(completion_payload);
|
|
36
35
|
|
|
37
|
-
const
|
|
36
|
+
const message_content = [];
|
|
38
37
|
if (message.content) {
|
|
39
|
-
for (let m of message.content)
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
for (let m of message.content) {
|
|
39
|
+
switch (m.type) {
|
|
40
|
+
case 'text':
|
|
41
|
+
message_content.push({type: 'text', content: m.text});
|
|
42
|
+
break;
|
|
43
|
+
|
|
44
|
+
default:
|
|
45
|
+
throw new Error('Unrecognized message type in Anthropic response');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
return
|
|
50
|
+
return [
|
|
51
|
+
new Message('assistant', message_content),
|
|
52
|
+
];
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
convertMessages(thread) {
|
|
48
56
|
let system = [], messages = [];
|
|
49
|
-
for (let message of thread.
|
|
50
|
-
if (message.role === 'system')
|
|
57
|
+
for (let message of thread.messages) {
|
|
58
|
+
if (message.role === 'system') {
|
|
51
59
|
system.push(message.content);
|
|
52
|
-
else
|
|
53
|
-
|
|
60
|
+
} else {
|
|
61
|
+
switch (message.type) {
|
|
62
|
+
case 'text':
|
|
63
|
+
messages.push({
|
|
64
|
+
role: message.role,
|
|
65
|
+
type: 'text',
|
|
66
|
+
content: message.content,
|
|
67
|
+
});
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case 'function':
|
|
71
|
+
messages.push({
|
|
72
|
+
role: message.role,
|
|
73
|
+
type: 'text',
|
|
74
|
+
content: '```CALL \n' + message.content.name + '\n' + JSON.stringify(message.content.arguments || {}) + '\n```',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
default:
|
|
78
|
+
throw new Error('Message type unsupported by this model');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
54
81
|
}
|
|
55
82
|
|
|
56
83
|
return [system.length ? system.join("\n") : undefined, messages];
|
package/models/OpenAIModel.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Response from "../Response.js";
|
|
2
1
|
import Model from "../Model.js";
|
|
3
2
|
import OpenAI from "openai";
|
|
4
3
|
import Message from "../Message.js";
|
|
@@ -15,7 +14,7 @@ export default class OpenAIModel extends Model {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
async generate(thread, payload = {}, functions = []) {
|
|
18
|
-
let messages = thread.
|
|
17
|
+
let messages = thread.messages;
|
|
19
18
|
|
|
20
19
|
if (functions.length && !this.supports_functions) {
|
|
21
20
|
// Se il modello non supporta nativamente le funzioni, inserisco il prompt ad hoc come ultimo messaggio di sistema
|
|
@@ -31,7 +30,7 @@ export default class OpenAIModel extends Model {
|
|
|
31
30
|
other_messages.push(message);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
system_messages.push(
|
|
33
|
+
system_messages.push(new Message('system', functions_prompt));
|
|
35
34
|
|
|
36
35
|
messages = [...system_messages, ...other_messages];
|
|
37
36
|
functions = [];
|
|
@@ -39,7 +38,7 @@ export default class OpenAIModel extends Model {
|
|
|
39
38
|
|
|
40
39
|
const completion_payload = {
|
|
41
40
|
model: this.name,
|
|
42
|
-
messages,
|
|
41
|
+
messages: messages.map(m => this.convertMessage(m)),
|
|
43
42
|
functions,
|
|
44
43
|
...payload,
|
|
45
44
|
};
|
|
@@ -51,19 +50,46 @@ export default class OpenAIModel extends Model {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
const chatCompletion = await this.getOpenAi().chat.completions.create(completion_payload);
|
|
54
|
-
|
|
55
|
-
const response = new Response;
|
|
56
53
|
const completion = chatCompletion.choices[0].message;
|
|
54
|
+
|
|
55
|
+
const message_content = [];
|
|
57
56
|
if (completion.content)
|
|
58
|
-
|
|
57
|
+
message_content.push({type: 'text', content: completion.content});
|
|
58
|
+
if (completion.function_call && completion.function_call.arguments)
|
|
59
|
+
message_content.push({type: 'function', content: completion.function_call});
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
-
}
|
|
61
|
+
return [
|
|
62
|
+
new Message('assistant', message_content),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
convertMessage(message) {
|
|
67
|
+
switch (message.type) {
|
|
68
|
+
case 'text':
|
|
69
|
+
return {
|
|
70
|
+
role: message.role,
|
|
71
|
+
content: message.content,
|
|
72
|
+
name: message.name,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
case 'function':
|
|
76
|
+
if (this.supports_functions) {
|
|
77
|
+
return {
|
|
78
|
+
role: message.role,
|
|
79
|
+
content: null,
|
|
80
|
+
name: message.name,
|
|
81
|
+
function_call: message.content,
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
return {
|
|
85
|
+
role: message.role,
|
|
86
|
+
content: message.content,
|
|
87
|
+
name: message.name,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
throw new Error('Message type unsupported by this model');
|
|
93
|
+
}
|
|
68
94
|
}
|
|
69
95
|
}
|
package/package.json
CHANGED
package/Response.js
DELETED