symposium 1.0.5 → 1.2.0
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 +70 -0
- package/Context.js +15 -0
- package/Contexts/File.js +27 -0
- package/Contexts/Text.js +17 -0
- package/GetContextTool.js +42 -0
- package/README.md +35 -3
- package/Symposium.js +1 -1
- package/package.json +1 -1
package/Agent.js
CHANGED
|
@@ -4,6 +4,10 @@ import BufferedEventEmitter from "./BufferedEventEmitter.js";
|
|
|
4
4
|
|
|
5
5
|
import Symposium from "./Symposium.js";
|
|
6
6
|
import Thread from "./Thread.js";
|
|
7
|
+
import Tool from "./Tool.js";
|
|
8
|
+
import Context from "./Context.js";
|
|
9
|
+
import Text from "./Contexts/Text.js";
|
|
10
|
+
import GetContextTool from "./GetContextTool.js";
|
|
7
11
|
|
|
8
12
|
export default class Agent {
|
|
9
13
|
name = 'Agent';
|
|
@@ -12,6 +16,7 @@ export default class Agent {
|
|
|
12
16
|
threads;
|
|
13
17
|
functions = null;
|
|
14
18
|
tools = new Map();
|
|
19
|
+
context = [];
|
|
15
20
|
default_model = 'gpt-4o';
|
|
16
21
|
max_retries = 5;
|
|
17
22
|
type = 'chat'; // chat, utility
|
|
@@ -61,11 +66,76 @@ export default class Agent {
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
addTool(tool) {
|
|
69
|
+
if (!(tool instanceof Tool) || !tool.name)
|
|
70
|
+
throw new Error('Tool must be an instance of Tool class');
|
|
71
|
+
if (this.tools.has(tool.name))
|
|
72
|
+
throw new Error('Tool with name ' + tool.name + ' already exists in agent');
|
|
73
|
+
|
|
64
74
|
this.tools.set(tool.name, tool);
|
|
65
75
|
}
|
|
66
76
|
|
|
77
|
+
async addContext(context, options = {}) {
|
|
78
|
+
if (typeof context === 'string')
|
|
79
|
+
context = new Text(context);
|
|
80
|
+
if (!(context instanceof Context))
|
|
81
|
+
throw new Error('Context must be an instance of Context class');
|
|
82
|
+
|
|
83
|
+
options = {
|
|
84
|
+
type: 'always', // always, on_request
|
|
85
|
+
description: null,
|
|
86
|
+
...options,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// TODO: summarization based on tokens
|
|
90
|
+
// TODO: RAG
|
|
91
|
+
|
|
92
|
+
const title = await context.getTitle();
|
|
93
|
+
this.context.push({title, context, options});
|
|
94
|
+
}
|
|
95
|
+
|
|
67
96
|
async initThread(thread) {
|
|
68
97
|
await this.doInitThread(thread);
|
|
98
|
+
|
|
99
|
+
let context_texts = [], is_there_on_request = false;
|
|
100
|
+
for (let {title, context, options} of this.context) {
|
|
101
|
+
switch (options.type) {
|
|
102
|
+
case 'always':
|
|
103
|
+
const text = await context.getText();
|
|
104
|
+
if (text)
|
|
105
|
+
context_texts.push('<context>' + text + '</context>');
|
|
106
|
+
break;
|
|
107
|
+
|
|
108
|
+
case 'on_request':
|
|
109
|
+
is_there_on_request = true;
|
|
110
|
+
context_texts.push('<context_on_request><name>' + title + '</name><description>' + options.description + '</description></context_on_request>');
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
default:
|
|
114
|
+
throw new Error('Bad context option type ' + options.type);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (context_texts.length) {
|
|
119
|
+
let context_string = context_texts.join('\n');
|
|
120
|
+
if (is_there_on_request) {
|
|
121
|
+
context_string = '<important>Some of the context is available to you immediately here, while longer texts may be available only on request; you are provided with a title and a description. If you think it may be useful for your current task, you can request the text via the get_context tool</important>';
|
|
122
|
+
if (!this.tools.has('get_context'))
|
|
123
|
+
this.addTool(new GetContextTool(this));
|
|
124
|
+
}
|
|
125
|
+
context_string = '\n<context_info>' + context_string + '</context_info>';
|
|
126
|
+
|
|
127
|
+
let system_message_found = null;
|
|
128
|
+
for (let messages of thread.messages) {
|
|
129
|
+
if (messages.role === 'system')
|
|
130
|
+
system_message_found = messages;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (system_message_found)
|
|
134
|
+
system_message_found.content[0].content += context_string;
|
|
135
|
+
else
|
|
136
|
+
thread.addMessage('system', context_string);
|
|
137
|
+
}
|
|
138
|
+
|
|
69
139
|
await thread.storeState();
|
|
70
140
|
}
|
|
71
141
|
|
package/Context.js
ADDED
package/Contexts/File.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import Context from "../Context.js";
|
|
3
|
+
|
|
4
|
+
export default class File extends Context {
|
|
5
|
+
constructor(file) {
|
|
6
|
+
super();
|
|
7
|
+
this.file = file;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async getTitle() {
|
|
11
|
+
if (this.file.startsWith('http://') || this.file.startsWith('https://'))
|
|
12
|
+
return this.file;
|
|
13
|
+
else
|
|
14
|
+
return this.file.split('/').pop();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getText() {
|
|
18
|
+
if (this.file.startsWith('http://') || this.file.startsWith('https://')) {
|
|
19
|
+
return fetch(this.file);
|
|
20
|
+
} else {
|
|
21
|
+
if (fs.existsSync(this.file))
|
|
22
|
+
return fs.promises.readFile(this.file, "utf8");
|
|
23
|
+
else
|
|
24
|
+
throw new Error(`File not found: ${this.file}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/Contexts/Text.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Context from "../Context.js";
|
|
2
|
+
|
|
3
|
+
export default class Text extends Context {
|
|
4
|
+
constructor(text, title = null) {
|
|
5
|
+
super();
|
|
6
|
+
this.text = text;
|
|
7
|
+
this.title = title;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async getTitle() {
|
|
11
|
+
return this.title;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async getText() {
|
|
15
|
+
return this.text;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Tool from "./Tool.js";
|
|
2
|
+
|
|
3
|
+
export default class GetContextTool extends Tool {
|
|
4
|
+
name = 'get_context';
|
|
5
|
+
|
|
6
|
+
constructor(agent) {
|
|
7
|
+
super();
|
|
8
|
+
this.agent = agent;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getFunctions() {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
name: 'get_context',
|
|
15
|
+
description: 'Get the text from a specific context snippet',
|
|
16
|
+
parameters: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
title: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ['title'],
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async callFunction(thread, name, payload) {
|
|
30
|
+
if (name !== 'get_context')
|
|
31
|
+
return {error: `Function ${name} not found`};
|
|
32
|
+
|
|
33
|
+
const title = payload.title;
|
|
34
|
+
const context = this.agent.context.find(c => c.title === title && c.options.type === 'on_request');
|
|
35
|
+
if (!context)
|
|
36
|
+
return {error: `Context with title ${title} not found`};
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
context: await context.context.getText(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
package/README.md
CHANGED
|
@@ -62,9 +62,41 @@ async function main() {
|
|
|
62
62
|
main();
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
### 2.
|
|
65
|
+
### 2. One shot prompts
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
You can also use the static `Symposium.prompt()` method for one-off prompts without creating an agent.
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { Symposium } from 'symposium';
|
|
71
|
+
await Symposium.init();
|
|
72
|
+
|
|
73
|
+
const response = await Symposium.prompt('Translate the text from English to French.', 'Hello, how are you?');
|
|
74
|
+
console.log(response); // "Bonjour, comment ça va?"
|
|
75
|
+
|
|
76
|
+
const structured_response = await Symposium.prompt('Extract name and emails from the following email', email_text, {
|
|
77
|
+
response: {
|
|
78
|
+
type: 'json',
|
|
79
|
+
function: {
|
|
80
|
+
name: 'extract_data',
|
|
81
|
+
parameters: {
|
|
82
|
+
type: 'array',
|
|
83
|
+
items: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
name: {type: 'string'},
|
|
87
|
+
email: {type: 'string'},
|
|
88
|
+
},
|
|
89
|
+
required: ['name', 'email'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Create your Agent
|
|
98
|
+
|
|
99
|
+
For more structured and/or reusable tasks, create a new class that extends `Agent`. At a minimum, you'll want to define a name and a system prompt.
|
|
68
100
|
|
|
69
101
|
```javascript
|
|
70
102
|
// MyChatAgent.js
|
|
@@ -80,7 +112,7 @@ export default class MyChatAgent extends Agent {
|
|
|
80
112
|
}
|
|
81
113
|
```
|
|
82
114
|
|
|
83
|
-
###
|
|
115
|
+
### 4. Start a Conversation
|
|
84
116
|
|
|
85
117
|
Now you can instantiate your agent and start a conversation.
|
|
86
118
|
|
package/Symposium.js
CHANGED
|
@@ -116,7 +116,7 @@ export default class Symposium {
|
|
|
116
116
|
return mimeToExt[mime] || null;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
static async
|
|
119
|
+
static async prompt(system, prompt, options = {}) {
|
|
120
120
|
const agent = new Agent(options.agent || {});
|
|
121
121
|
agent.type = 'utility';
|
|
122
122
|
agent.utility = options.response || {
|