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 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.addUserMessage(text);
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
- return model.generate(thread, payload, await this.getFunctions());
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
- async handleCompletion(thread, completion) {
152
- for (let message of completion.messages) {
153
- thread.addMessage(message);
154
- await this.log('ai_message', message.text);
155
- await thread.reply(message.text);
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
- if (completion.function) {
159
- thread.addAssistantMessage('', {
160
- name: completion.function.name,
161
- arguments: JSON.stringify(completion.function_call.args),
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 (completion.function)
166
- return this.callFunction(thread, completion.function);
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.args);
203
- thread.addFunctionMessage(response, function_call.name);
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.addFunctionMessage({error}, function_call.name);
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
- text;
4
- name;
5
- function_call;
3
+ content = [];
4
+ name = undefined;
6
5
  tags = [];
7
6
 
8
- constructor(role, text, name = null, function_call = null, tags = []) {
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
@@ -1,3 +1,5 @@
1
+ import Message from "./Message.js";
2
+
1
3
  export default class Model {
2
4
  type = 'llm';
3
5
  name;
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.addSystemMessage('Summarize the conversation up to this moment.');
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.addSystemMessage("This is what happened until now:\n" + summary.function_call.arguments.summary, ['summary']);
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 => (new Message(m.role, m.text, m.name, m.function_call, m.tags || [])));
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
- getMessagesJson() {
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
- addSystemMessage(text, tags = []) {
70
- this.messages.push(new Message('system', text, null, null, tags));
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) {
@@ -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 response = new Response;
36
+ const message_content = [];
38
37
  if (message.content) {
39
- for (let m of message.content)
40
- // TODO: supporto ad altri tipi oltre a text (m.type)
41
- response.messages.push(new Message('assistant', m.text));
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 response;
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.getMessagesJson()) {
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
- messages.push(message);
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];
@@ -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.getMessagesJson();
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({role: 'system', content: functions_prompt});
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
- response.messages.push(new Message('assistant', completion.content));
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
- if (completion.function_call && completion.function_call.arguments) {
61
- response.function = {
62
- name: completion.function_call.name,
63
- args: JSON.parse(completion.function_call.arguments),
64
- };
65
- }
61
+ return [
62
+ new Message('assistant', message_content),
63
+ ];
64
+ }
66
65
 
67
- return response;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "symposium",
4
- "version": "0.4.5",
4
+ "version": "0.4.7",
5
5
  "description": "Agents",
6
6
  "main": "index.js",
7
7
  "author": "Domenico Giambra",
package/Response.js DELETED
@@ -1,4 +0,0 @@
1
- export default class Response {
2
- messages = [];
3
- function = null;
4
- }