symposium 0.2.1 → 0.3.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 CHANGED
@@ -1,15 +1,14 @@
1
1
  import Symposium from "./Symposium.js";
2
- import Conversation from "./Conversation.js";
2
+ import Thread from "./Thread.js";
3
3
 
4
4
  export default class Agent {
5
5
  name = 'Agent';
6
6
  descriptionForFront = '';
7
7
  options = {};
8
- conversations;
8
+ threads;
9
9
  functions = null;
10
10
  middlewares = new Map();
11
11
  tools = new Map();
12
- commands;
13
12
  default_model = 'gpt-4-turbo';
14
13
 
15
14
  constructor(options) {
@@ -21,88 +20,23 @@ export default class Agent {
21
20
  if (this.options.memory_handler)
22
21
  this.options.memory_handler.setAgent(this);
23
22
 
24
- this.commands = new Map();
25
-
26
- this.commands.set('start', {
27
- description: '',
28
- show_in_help: false,
29
- exec: async conversation => {
30
- await conversation.reply('Benvenuto! Digita /help per un aiuto sui comandi, oppure procedi pure se già sai come usare.');
31
- }
32
- });
33
-
34
- this.commands.set('model', {
35
- description: 'Per impostare l\'utilizzo di GPT 3 o 4 (o vedere il modello che si sta usando)',
36
- show_in_help: true,
37
- exec: async (conversation, args) => {
38
- if (args) {
39
- const model_to_switch = Symposium.getModelByLabel(args);
40
- if (model_to_switch) {
41
- await conversation.setState({model: model_to_switch.name});
42
- await conversation.reply('# Da ora in poi uso ' + model_to_switch.label + '!');
43
- } else {
44
- await conversation.reply("# Versione modello non riconosciuta!\nModelli disponibili:\n" + Symposium.models.map(m => m.label).join("\n"));
45
- }
46
- } else {
47
- const currentModel = Symposium.getModelByName(conversation.state.model);
48
- await conversation.reply('# Il modello attualmente in uso è ' + currentModel.label);
49
- }
50
- },
51
- });
52
-
53
- this.commands.set('reset', {
54
- description: 'Reimposta la conversazione, facendo dimenticare al bot tutto ciò che viene prima',
55
- show_in_help: true,
56
- exec: async conversation => {
57
- await this.reset(conversation);
58
- await conversation.reply('# Conversazione resettata');
59
- }
60
- });
61
-
62
- this.commands.set('logout', {
63
- description: 'Reimposta i token di autenticazione',
64
- show_in_help: true,
65
- exec: async conversation => {
66
- await this.logout(conversation);
67
- await conversation.reply('# Logout effettuato');
68
- }
69
- });
70
-
71
- this.commands.set('help', {
72
- description: 'Aiuto sui comandi',
73
- show_in_help: true,
74
- exec: async conversation => {
75
- let help = "Comandi disponibili:\n";
76
- for (let command of this.commands.entries()) {
77
- if (!command[1].show_in_help)
78
- continue;
79
- help += '/' + command[0];
80
- if (command[1].description)
81
- help += ' -> ' + command[1].description;
82
- help += "\n";
83
- }
84
-
85
- await conversation.reply(help);
86
- }
87
- });
88
-
89
- this.conversations = new Map();
23
+ this.threads = new Map();
90
24
  }
91
25
 
92
- async logout(conversation) {
93
- conversation.auth = new Map();
26
+ async logout(thread) {
27
+ thread.auth = new Map();
94
28
  }
95
29
 
96
- async reset(conversation) {
97
- await conversation.flush();
98
- await this.resetState(conversation);
99
- await this.initConversation(conversation);
100
- await conversation.storeState();
30
+ async reset(thread) {
31
+ await thread.flush();
32
+ await this.resetState(thread);
33
+ await this.initThread(thread);
34
+ await thread.storeState();
101
35
  }
102
36
 
103
- async resetState(conversation) {
104
- conversation.state = await this.getDefaultState();
105
- conversation.state.model = Symposium.getModelByLabel(this.default_model).name;
37
+ async resetState(thread) {
38
+ thread.state = await this.getDefaultState();
39
+ thread.state.model = Symposium.getModelByLabel(this.default_model).name;
106
40
  }
107
41
 
108
42
  async getDefaultState() {
@@ -117,89 +51,60 @@ export default class Agent {
117
51
  this.middlewares.set(middleware.name, middleware);
118
52
  }
119
53
 
120
- async initConversation(conversation) {
121
- await this.doInitConversation(conversation);
122
- await conversation.storeState();
54
+ async initThread(thread) {
55
+ await this.doInitThread(thread);
56
+ await thread.storeState();
123
57
  }
124
58
 
125
- async doInitConversation(conversation) {
59
+ async doInitThread(thread) {
126
60
  }
127
61
 
128
- async getConversation(id, reply = null) {
129
- let conversation = this.conversations.get(id);
130
- if (!conversation) {
131
- conversation = new Conversation(this.name + '-' + id);
62
+ async getThread(id, reply = null) {
63
+ let thread = this.threads.get(id);
64
+ if (!thread) {
65
+ thread = new Thread(this.name + '-' + id);
132
66
 
133
- if (!(await conversation.loadState())) {
134
- await this.resetState(conversation);
135
- await this.initConversation(conversation);
67
+ if (!(await thread.loadState())) {
68
+ await this.resetState(thread);
69
+ await this.initThread(thread);
136
70
  }
137
71
 
138
- this.conversations.set(id, conversation);
72
+ this.threads.set(id, thread);
139
73
  }
140
74
 
141
75
  if (reply)
142
- conversation.reply = reply;
76
+ thread.reply = reply;
143
77
 
144
- return conversation;
78
+ return thread;
145
79
  }
146
80
 
147
- async getConversationIfExists(id) {
148
- if (this.conversations.has(id))
149
- return this.conversations.get(id);
81
+ async getThreadIfExists(id) {
82
+ if (this.threads.has(id))
83
+ return this.threads.get(id);
150
84
 
151
- const conversation = new Conversation(this.name + '-' + id);
85
+ const thread = new Thread(this.name + '-' + id);
152
86
 
153
- if (await conversation.loadState())
154
- return conversation;
87
+ if (await thread.loadState())
88
+ return thread;
155
89
 
156
90
  return null;
157
91
  }
158
92
 
159
- async message(conversation, text) {
160
- if (text.startsWith('/')) {
161
- const fullCommand = text.trim().split(' ');
162
- const command = fullCommand.shift().substring(1);
163
- const command_args = fullCommand.length ? fullCommand.join(' ').trim() : null;
164
- try {
165
- await this.executeCommand(conversation, command, command_args);
166
- } catch (e) {
167
- await conversation.reply(e.message || e.error || JSON.stringify(e));
168
- }
169
-
170
- return;
171
- }
172
-
173
- await this.execute(conversation, text);
174
- }
175
-
176
- async executeCommand(conversation, name, args) {
177
- const command = this.commands.get(name);
178
- if (!command)
179
- throw new Error('Comando non riconosciuto');
93
+ async message(thread, text) {
94
+ await this.log('user_message', text);
95
+ thread.addUserMessage(text);
180
96
 
181
- await command.exec(conversation, args);
97
+ await this.execute(thread, text);
182
98
  }
183
99
 
184
- async execute(conversation, user_message) {
185
- for (let middleware of this.middlewares.values()) {
186
- let proceed = await middleware.before_add(conversation, user_message);
187
- if (!proceed)
188
- return;
189
- }
190
-
191
- if (user_message) {
192
- await this.log('user_message', user_message);
193
- conversation.addUserMessage(user_message);
194
- }
195
-
100
+ async execute(thread) {
196
101
  if (this.options.memory_handler)
197
- conversation = await this.options.memory_handler.handle(conversation);
102
+ thread = await this.options.memory_handler.handle(thread);
198
103
 
199
104
  for (let middleware of this.middlewares.values()) {
200
- let proceed = await middleware.before_exec(conversation, user_message);
105
+ let proceed = await middleware.before_exec(thread);
201
106
  if (!proceed) {
202
- await conversation.storeState();
107
+ await thread.storeState();
203
108
  return;
204
109
  }
205
110
  }
@@ -210,25 +115,25 @@ export default class Agent {
210
115
  completion_payload.function_call = {name: this.options.talking_function.name};
211
116
  }
212
117
 
213
- const completion = await this.generateCompletion(conversation, completion_payload);
118
+ const completion = await this.generateCompletion(thread, completion_payload);
214
119
 
215
- await this.handleCompletion(conversation, completion);
120
+ await this.handleCompletion(thread, completion);
216
121
 
217
122
  const reversedMiddlewares = [...this.middlewares.values()];
218
123
  reversedMiddlewares.reverse();
219
124
 
220
125
  for (let middleware of reversedMiddlewares) {
221
- let proceed = await middleware.after_exec(conversation, user_message);
126
+ let proceed = await middleware.after_exec(thread);
222
127
  if (!proceed)
223
128
  return;
224
129
  }
225
130
  }
226
131
 
227
- async generateCompletion(conversation, payload = {}, retry_counter = 1) {
132
+ async generateCompletion(thread, payload = {}, retry_counter = 1) {
228
133
  try {
229
134
  const completion_payload = {
230
- model: conversation.state.model,
231
- messages: conversation.getMessagesJson(),
135
+ model: thread.state.model,
136
+ messages: thread.getMessagesJson(),
232
137
  functions: await this.getFunctions(),
233
138
  ...payload,
234
139
  };
@@ -257,42 +162,42 @@ export default class Agent {
257
162
  setTimeout(resolve, 1000);
258
163
  });
259
164
 
260
- return this.generateCompletion(conversation, payload, retry_counter + 1);
165
+ return this.generateCompletion(thread, payload, retry_counter + 1);
261
166
  }
262
167
 
263
- await conversation.reply('# Errore ' + error.response.status + ': ' + JSON.stringify(error.response.data));
168
+ await thread.reply('# Errore ' + error.response.status + ': ' + JSON.stringify(error.response.data));
264
169
  } else if (error.message) {
265
170
  console.error(error.message);
266
- await conversation.reply('# Errore ' + error.message);
171
+ await thread.reply('# Errore ' + error.message);
267
172
  } else {
268
173
  console.error(error);
269
- await conversation.reply('# Errore interno');
174
+ await thread.reply('# Errore interno');
270
175
  }
271
176
  }
272
177
  }
273
178
 
274
- async handleCompletion(conversation, completion) {
179
+ async handleCompletion(thread, completion) {
275
180
  if (this.options.talking_function && completion.function_call) {
276
181
  const text = completion.function_call.arguments[Object.keys(this.options.talking_function.parameters.properties)[0]];
277
- conversation.addAssistantMessage(text);
182
+ thread.addAssistantMessage(text);
278
183
  await this.log('ai_message', text);
279
- await conversation.reply(text);
280
- return conversation.storeState()
184
+ await thread.reply(text);
185
+ return thread.storeState()
281
186
  }
282
187
 
283
- conversation.addAssistantMessage(completion.content, completion.function_call ? {
188
+ thread.addAssistantMessage(completion.content, completion.function_call ? {
284
189
  ...completion.function_call,
285
190
  arguments: JSON.stringify(completion.function_call.arguments),
286
191
  } : null);
287
192
  if (completion.content) {
288
193
  await this.log('ai_message', completion.content);
289
- await conversation.reply(completion.content);
194
+ await thread.reply(completion.content);
290
195
  }
291
196
 
292
197
  if (completion?.function_call)
293
- return this.callFunction(conversation, completion.function_call);
198
+ return this.callFunction(thread, completion.function_call);
294
199
  else
295
- return conversation.storeState();
200
+ return thread.storeState();
296
201
  }
297
202
 
298
203
  async getFunctions(parsed = true) {
@@ -318,7 +223,7 @@ export default class Agent {
318
223
  return this.functions;
319
224
  }
320
225
 
321
- async callFunction(conversation, function_call) {
226
+ async callFunction(thread, function_call) {
322
227
  let functions = await this.getFunctions(false);
323
228
  if (!functions.has(function_call.name))
324
229
  throw new Error('Unrecognized function ' + function_call.name);
@@ -326,15 +231,15 @@ export default class Agent {
326
231
  await this.log('function_call', function_call);
327
232
 
328
233
  try {
329
- const response = await functions.get(function_call.name).tool.callFunction(conversation, function_call.name, function_call.arguments);
330
- conversation.addFunctionMessage(response, function_call.name);
234
+ const response = await functions.get(function_call.name).tool.callFunction(thread, function_call.name, function_call.arguments);
235
+ thread.addFunctionMessage(response, function_call.name);
331
236
  await this.log('function_response', response);
332
237
  } catch (error) {
333
- conversation.addFunctionMessage({error}, function_call.name);
238
+ thread.addFunctionMessage({error}, function_call.name);
334
239
  await this.log('function_response', {error});
335
240
  }
336
241
 
337
- await this.execute(conversation);
242
+ await this.execute(thread);
338
243
  }
339
244
 
340
245
  async log(type, payload) {
@@ -342,7 +247,7 @@ export default class Agent {
342
247
  return this.options.logger.log(this.name, type, payload);
343
248
  }
344
249
 
345
- async getPromptWordsForTranscription(conversation) {
250
+ async getPromptWordsForTranscription(thread) {
346
251
  return [this.name];
347
252
  }
348
253
  }
package/MemoryHandler.js CHANGED
@@ -5,6 +5,6 @@ export default class MemoryHandler {
5
5
  this.agent = agent;
6
6
  }
7
7
 
8
- async handle(conversation) {
8
+ async handle(thread) {
9
9
  }
10
10
  }
package/Middleware.js CHANGED
@@ -6,15 +6,11 @@ export default class Middleware {
6
6
  this.agent = agent;
7
7
  }
8
8
 
9
- async before_add(conversation, user_message) {
9
+ async before_exec(thread) {
10
10
  return true;
11
11
  }
12
12
 
13
- async before_exec(conversation, user_message) {
14
- return true;
15
- }
16
-
17
- async after_exec(conversation, user_message) {
13
+ async after_exec(thread) {
18
14
  return true;
19
15
  }
20
16
  }
package/Summarizer.js CHANGED
@@ -9,55 +9,55 @@ export default class Summarizer extends MemoryHandler {
9
9
  this.summary_length = summary_length;
10
10
  }
11
11
 
12
- async handle(conversation) {
13
- const model = Symposium.getModelByName(conversation.state.model);
12
+ async handle(thread) {
13
+ const model = Symposium.getModelByName(thread.state.model);
14
14
  if (!model)
15
- return conversation;
15
+ return thread;
16
16
 
17
17
  const encoder = encoding_for_model(model.name_for_tiktoken);
18
- const tokens = this.countTokens(encoder, conversation);
18
+ const tokens = this.countTokens(encoder, thread);
19
19
  if (tokens >= model.tokens * this.threshold)
20
- return await this.summarize(encoder, conversation, model.tokens * this.summary_length);
20
+ return await this.summarize(encoder, thread, model.tokens * this.summary_length);
21
21
  else
22
- return conversation;
22
+ return thread;
23
23
  }
24
24
 
25
- countTokens(encoder, conversation) {
26
- return encoder.encode(conversation.messages.map(m => m.text).join('')).length;
25
+ countTokens(encoder, thread) {
26
+ return encoder.encode(thread.messages.map(m => m.text).join('')).length;
27
27
  }
28
28
 
29
- async summarize(encoder, conversation, maxLength) {
30
- let summaryConversation = conversation.clone(false);
29
+ async summarize(encoder, thread, maxLength) {
30
+ let summaryThread = thread.clone(false);
31
31
 
32
32
  let currentStep = 'system';
33
- for (let message of conversation.messages) {
33
+ for (let message of thread.messages) {
34
34
  switch (currentStep) {
35
35
  case 'system':
36
36
  if (message.role !== 'system')
37
37
  currentStep = 'summary';
38
38
  break;
39
39
  case 'summary':
40
- if (message.role === 'user' && this.hasPassedLimit(encoder, summaryConversation, maxLength)) {
41
- summaryConversation = await this.doSummarize(summaryConversation, maxLength);
40
+ if (message.role === 'user' && this.hasPassedLimit(encoder, summaryThread, maxLength)) {
41
+ summaryThread = await this.doSummarize(summaryThread, maxLength);
42
42
  currentStep = 'retain';
43
43
  }
44
44
  break;
45
45
  }
46
46
 
47
- summaryConversation.messages.push(message);
47
+ summaryThread.messages.push(message);
48
48
  }
49
49
 
50
- return summaryConversation;
50
+ return summaryThread;
51
51
  }
52
52
 
53
- hasPassedLimit(encoder, conversation, maxLength) {
54
- let length = this.countTokens(encoder, conversation);
53
+ hasPassedLimit(encoder, thread, maxLength) {
54
+ let length = this.countTokens(encoder, thread);
55
55
  return length > maxLength;
56
56
  }
57
57
 
58
- async doSummarize(conversation, maxLength) {
59
- conversation.addSystemMessage('Summarize the conversation up to this moment.');
60
- const summary = await this.agent.generateCompletion(conversation, {
58
+ async doSummarize(thread, maxLength) {
59
+ thread.addSystemMessage('Summarize the conversation up to this moment.');
60
+ const summary = await this.agent.generateCompletion(thread, {
61
61
  functions: [
62
62
  {
63
63
  name: 'summarize',
@@ -79,16 +79,16 @@ export default class Summarizer extends MemoryHandler {
79
79
  if (!summary)
80
80
  return false;
81
81
 
82
- let summarizedConversation = conversation.clone(false);
83
- for (let message of conversation.messages) {
82
+ let summarizedThread = thread.clone(false);
83
+ for (let message of thread.messages) {
84
84
  if (message.role === 'system' && !message.tags.includes('summary')) {
85
- summarizedConversation.messages.push(message);
85
+ summarizedThread.messages.push(message);
86
86
  } else {
87
- summarizedConversation.addSystemMessage("This is what happened until now:\n" + summary.function_call.arguments.summary, ['summary']);
87
+ summarizedThread.addSystemMessage("This is what happened until now:\n" + summary.function_call.arguments.summary, ['summary']);
88
88
  break;
89
89
  }
90
90
  }
91
91
 
92
- return summarizedConversation;
92
+ return summarizedThread;
93
93
  }
94
94
  }
package/Symposium.js CHANGED
@@ -34,14 +34,14 @@ export default class Symposium {
34
34
  return this.models.find(model => model.name === name);
35
35
  }
36
36
 
37
- static async transcribe(agent, file, conversation) {
38
- let words = await agent.getPromptWordsForTranscription(conversation);
37
+ static async transcribe(agent, file, thread) {
38
+ const words = await agent.getPromptWordsForTranscription(thread);
39
39
 
40
- let response = await this.openai.audio.transcriptions.create({
40
+ const response = await this.getOpenAi().then(openai => openai.audio.transcriptions.create({
41
41
  file,
42
42
  model: 'whisper-1',
43
43
  prompt: words.join(', '),
44
- });
44
+ }));
45
45
  return response.text;
46
46
  }
47
47
  }
@@ -1,7 +1,7 @@
1
1
  import Message from "./Message.js";
2
2
  import Redis from "@travio/redis";
3
3
 
4
- export default class Conversation {
4
+ export default class Thread {
5
5
  id;
6
6
  reply;
7
7
  messages = [];
@@ -13,12 +13,12 @@ export default class Conversation {
13
13
  }
14
14
 
15
15
  clone(keepMessages = true) {
16
- let newConversation = new Conversation(this.id);
17
- newConversation.reply = this.reply;
18
- newConversation.state = this.state;
16
+ let newThread = new Thread(this.id);
17
+ newThread.reply = this.reply;
18
+ newThread.state = this.state;
19
19
  if (keepMessages)
20
- newConversation.messages = [...this.messages];
21
- return newConversation;
20
+ newThread.messages = [...this.messages];
21
+ return newThread;
22
22
  }
23
23
 
24
24
  async flush() {
@@ -29,7 +29,7 @@ export default class Conversation {
29
29
  await this.flush();
30
30
  this.state = {};
31
31
 
32
- const conv = await Redis.get('conversation-' + this.id);
32
+ const conv = await Redis.get('thread-' + this.id);
33
33
  if (conv) {
34
34
  this.auth = new Map(conv.auth || []);
35
35
  this.state = conv.state || {};
@@ -47,7 +47,7 @@ export default class Conversation {
47
47
  }
48
48
 
49
49
  async storeState() {
50
- await Redis.set('conversation-' + this.id, {
50
+ await Redis.set('thread-' + this.id, {
51
51
  auth: [...this.auth.entries()],
52
52
  state: this.state,
53
53
  messages: this.messages,
package/Tool.js CHANGED
@@ -5,7 +5,7 @@ export default class Tool {
5
5
  return [];
6
6
  }
7
7
 
8
- async callFunction(conversation, name, payload) {
8
+ async callFunction(thread, name, payload) {
9
9
  return {error: 'callFunction is yet to be implemented'};
10
10
  }
11
11
  }
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import Symposium from "./Symposium.js";
4
4
  import Agent from "./Agent.js";
5
- import Conversation from "./Conversation.js";
5
+ import Thread from "./Thread.js";
6
6
  import Message from "./Message.js";
7
7
  import Tool from "./Tool.js";
8
8
  import Logger from "./Logger.js";
@@ -13,7 +13,7 @@ import Summarizer from "./Summarizer.js";
13
13
  export {
14
14
  Symposium,
15
15
  Agent,
16
- Conversation,
16
+ Thread,
17
17
  Message,
18
18
  Tool,
19
19
  Logger,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "symposium",
4
- "version": "0.2.1",
4
+ "version": "0.3.1",
5
5
  "description": "Agents",
6
6
  "main": "index.js",
7
7
  "author": "Domenico Giambra",