booths 1.4.0 → 1.5.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/README.md +253 -139
- package/dist/index.cjs +60 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +59 -56
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,93 +1,72 @@
|
|
|
1
1
|
# Booths
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/phoneburner/booths/actions/workflows/build-check.yml)
|
|
4
|
+
[](https://github.com/phoneburner/booths/actions/workflows/format-check.yml)
|
|
5
|
+
[](https://github.com/phoneburner/booths/actions/workflows/release.yml)
|
|
6
|
+
[](https://github.com/phoneburner/booths/actions/workflows/test.yml)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
*A modular, extensible framework for building and managing conversational AI agents in TypeScript.*
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
> Booths provides a structured way to define agent capabilities, context, and tools, orchestrated by a `CoreBooth` that manages the interaction loop with your LLM and a rich plugin lifecycle.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
---
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
graph TD
|
|
13
|
-
subgraph Application Layer
|
|
14
|
-
A[Your Application]
|
|
15
|
-
end
|
|
14
|
+
## Table of Contents
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
* [Installation](#installation)
|
|
17
|
+
* [Quick Start Guide](#quick-start-guide)
|
|
18
|
+
* [Architecture Overview](#architecture-overview)
|
|
19
|
+
* [API Reference](#api-reference)
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
D[Interaction Processor]
|
|
23
|
-
E[LLM Adapter]
|
|
24
|
-
F[Registries]
|
|
25
|
-
G[Plugins]
|
|
26
|
-
end
|
|
21
|
+
* [createCoreBooth](#createcorebooth)
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
D -- "uses" --> F
|
|
32
|
-
D -- "executes" --> G
|
|
33
|
-
E -- "communicates with" --> H((LLM))
|
|
34
|
-
F -- "manages" --> I{Booths}
|
|
35
|
-
F -- "manages" --> J{Tools}
|
|
36
|
-
F -- "manages" --> K{Plugins}
|
|
37
|
-
G -- "hook into" --> D
|
|
23
|
+
* [Options](#options)
|
|
24
|
+
* [Examples](#examples)
|
|
25
|
+
* [InteractionProcessor](#interactionprocessor)
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
* [Flow](#flow)
|
|
28
|
+
* [Important Constraints](#important-constraints)
|
|
29
|
+
* [Registries](#registries)
|
|
41
30
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
5. **Registries**: These are responsible for managing the different components of the system:
|
|
47
|
-
* `BoothRegistry`: Manages `BoothConfig` objects that define the behavior of different AI agents.
|
|
48
|
-
* `ToolRegistry`: Manages the tools (functions) that booths can use.
|
|
49
|
-
* `BoothPluginRegistry`: Manages the plugins that hook into the conversational lifecycle.
|
|
50
|
-
6. **Plugins**: These are modules that add functionality to the system by hooking into the `InteractionProcessor`'s lifecycle (e.g., managing conversation history, providing context to the LLM, executing tools).
|
|
31
|
+
* [BoothRegistry](#boothregistry)
|
|
32
|
+
* [ToolRegistry](#toolregistry)
|
|
33
|
+
* [BoothPluginRegistry](#boothpluginregistry)
|
|
34
|
+
* [Plugins](#plugins)
|
|
51
35
|
|
|
52
|
-
|
|
36
|
+
* [Lifecycle Hooks](#lifecycle-hooks)
|
|
37
|
+
* [Best Practices](#best-practices)
|
|
38
|
+
* [Advanced Usage](#advanced-usage)
|
|
53
39
|
|
|
54
|
-
|
|
40
|
+
* [Customizing the end-of-turn marker](#customizing-the-end-of-turn-marker)
|
|
41
|
+
* [Per-tool interception & error recovery](#per-tool-interception--error-recovery)
|
|
42
|
+
* [Types](#types)
|
|
43
|
+
* [Session & State Management](#session--state-management)
|
|
44
|
+
* [Error Handling](#error-handling)
|
|
45
|
+
* [License](#license)
|
|
55
46
|
|
|
56
|
-
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
57
50
|
|
|
58
51
|
```bash
|
|
59
52
|
npm install booths
|
|
60
53
|
```
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- Node.js and npm installed
|
|
65
|
-
- An LLM provider API key (e.g., OpenAI)
|
|
55
|
+
**Prerequisites**
|
|
66
56
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
To build the library:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
npm run build
|
|
73
|
-
```
|
|
57
|
+
* Node.js and npm
|
|
58
|
+
* An API key for your LLM provider (e.g., OpenAI)
|
|
74
59
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
npm run typecheck
|
|
79
|
-
```
|
|
60
|
+
---
|
|
80
61
|
|
|
81
62
|
## Quick Start Guide
|
|
82
63
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
### 1. Define a Booth
|
|
64
|
+
A minimal setup that defines a booth, a tool, an adapter, and starts a conversation.
|
|
86
65
|
|
|
87
|
-
|
|
66
|
+
### 1) Define a Booth
|
|
88
67
|
|
|
89
|
-
```
|
|
90
|
-
//
|
|
68
|
+
```ts
|
|
69
|
+
// my-booths.ts
|
|
91
70
|
import type { BoothConfig } from 'booths';
|
|
92
71
|
|
|
93
72
|
export const pirateBooth: BoothConfig = {
|
|
@@ -99,12 +78,10 @@ export const pirateBooth: BoothConfig = {
|
|
|
99
78
|
};
|
|
100
79
|
```
|
|
101
80
|
|
|
102
|
-
### 2
|
|
81
|
+
### 2) Define a Tool
|
|
103
82
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// in my-tools.ts
|
|
83
|
+
```ts
|
|
84
|
+
// my-tools.ts
|
|
108
85
|
import type { ToolModule } from 'booths';
|
|
109
86
|
|
|
110
87
|
export const tellPirateJokeTool: ToolModule = {
|
|
@@ -113,122 +90,259 @@ export const tellPirateJokeTool: ToolModule = {
|
|
|
113
90
|
description: 'Tells a classic pirate joke.',
|
|
114
91
|
parameters: { type: 'object', properties: {} },
|
|
115
92
|
execute: async () => {
|
|
116
|
-
return { joke:
|
|
93
|
+
return { joke: 'Why are pirates called pirates? Because they arrrr!' };
|
|
117
94
|
},
|
|
118
95
|
};
|
|
119
96
|
```
|
|
120
97
|
|
|
121
|
-
### 3
|
|
122
|
-
|
|
123
|
-
The `CoreBooth` requires an `LLMAdapter` to communicate with your chosen language model. Here is a minimal example for OpenAI.
|
|
98
|
+
### 3) Implement a simple LLM Adapter
|
|
124
99
|
|
|
125
|
-
```
|
|
126
|
-
//
|
|
127
|
-
import type {
|
|
100
|
+
```ts
|
|
101
|
+
// OpenAIAdapter.ts
|
|
102
|
+
import type {
|
|
103
|
+
LLMAdapter,
|
|
104
|
+
ResponseCreateParamsNonStreaming,
|
|
105
|
+
Response,
|
|
106
|
+
ResponseInput,
|
|
107
|
+
} from 'booths';
|
|
128
108
|
import OpenAI from 'openai';
|
|
129
109
|
|
|
130
110
|
export class OpenAIAdapter implements LLMAdapter<Response> {
|
|
131
111
|
private openai: OpenAI;
|
|
132
|
-
|
|
133
112
|
constructor(apiKey: string) {
|
|
134
113
|
this.openai = new OpenAI({ apiKey });
|
|
135
114
|
}
|
|
136
|
-
|
|
137
115
|
async invoke(params: ResponseCreateParamsNonStreaming): Promise<Response> {
|
|
138
116
|
return this.openai.responses.create({ ...params, model: 'gpt-4o' });
|
|
139
117
|
}
|
|
140
|
-
|
|
141
118
|
async interpret(response: Response): Promise<Response> {
|
|
142
119
|
return response;
|
|
143
120
|
}
|
|
144
121
|
}
|
|
145
122
|
```
|
|
146
123
|
|
|
147
|
-
|
|
124
|
+
> **Note**: All necessary types including `ResponseInput` and `ResponseInputItem` can be imported directly from `booths`, eliminating the need to import from the OpenAI library.
|
|
148
125
|
|
|
149
|
-
|
|
126
|
+
### 4) Initialize and talk to the booth
|
|
150
127
|
|
|
151
|
-
```
|
|
152
|
-
//
|
|
128
|
+
```ts
|
|
129
|
+
// main.ts
|
|
153
130
|
import { createCoreBooth } from 'booths';
|
|
154
131
|
import { pirateBooth } from './my-booths';
|
|
155
132
|
import { tellPirateJokeTool } from './my-tools';
|
|
156
|
-
import { OpenAIAdapter } from './
|
|
157
|
-
|
|
158
|
-
// 1. Create the LLM adapter
|
|
159
|
-
const llmAdapter = new OpenAIAdapter('your-openai-api-key');
|
|
133
|
+
import { OpenAIAdapter } from './OpenAIAdapter';
|
|
160
134
|
|
|
161
|
-
|
|
162
|
-
const coreBooth = createCoreBooth(llmAdapter, pirateBooth);
|
|
135
|
+
const llmAdapter = new OpenAIAdapter(process.env.OPENAI_API_KEY!);
|
|
136
|
+
const coreBooth = createCoreBooth(llmAdapter, pirateBooth /*, { endInteractionLoopMarker: '__custom_marker__' }*/);
|
|
163
137
|
|
|
164
|
-
//
|
|
165
|
-
// const coreBooth = createCoreBooth(llmAdapter, pirateBooth, {
|
|
166
|
-
// endInteractionLoopMarker: '__custom_marker__'
|
|
167
|
-
// });
|
|
168
|
-
|
|
169
|
-
// 3. Register the tool (this step will be improved in future versions)
|
|
138
|
+
// Register tools (available to the current booth)
|
|
170
139
|
coreBooth.toolRegistry.registerTools([tellPirateJokeTool]);
|
|
171
140
|
|
|
172
|
-
//
|
|
173
|
-
async function
|
|
174
|
-
const
|
|
175
|
-
const response = await coreBooth.callProcessor.send(userInput);
|
|
176
|
-
|
|
141
|
+
// Send a message and read the result
|
|
142
|
+
async function run() {
|
|
143
|
+
const response = await coreBooth.callProcessor.send('Tell me a pirate joke.');
|
|
177
144
|
console.log(response.output_text);
|
|
178
|
-
// Expected output: "Why are pirates called pirates? Because they arrrr!"
|
|
179
145
|
}
|
|
146
|
+
run();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Architecture Overview
|
|
180
152
|
|
|
181
|
-
|
|
153
|
+
```mermaid
|
|
154
|
+
graph TD
|
|
155
|
+
subgraph Application Layer
|
|
156
|
+
A[Your Application]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
subgraph Booth Service Layer
|
|
160
|
+
C(CoreBooth)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
subgraph Core Components
|
|
164
|
+
D[Interaction Processor]
|
|
165
|
+
E[LLM Adapter]
|
|
166
|
+
F[Registries]
|
|
167
|
+
G[Plugins]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
A -- "initializes and calls" --> C
|
|
171
|
+
C -- "delegates to" --> D
|
|
172
|
+
D -- "uses" --> E
|
|
173
|
+
D -- "uses" --> F
|
|
174
|
+
D -- "executes" --> G
|
|
175
|
+
E -- "communicates with" --> H((LLM))
|
|
176
|
+
F -- "manages" --> I{Booths}
|
|
177
|
+
F -- "manages" --> J{Tools}
|
|
178
|
+
F -- "manages" --> K{Plugins}
|
|
179
|
+
G -- "hook into" --> D
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## API Reference
|
|
185
|
+
|
|
186
|
+
### createCoreBooth
|
|
187
|
+
|
|
188
|
+
Factory that wires an `LLMAdapter`, a `BoothConfig`, and internal registries/plugins into a working `CoreBooth` instance.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
function createCoreBooth(
|
|
192
|
+
adapter: LLMAdapter<any>,
|
|
193
|
+
booth: BoothConfig,
|
|
194
|
+
options?: { endInteractionLoopMarker?: string }
|
|
195
|
+
): CoreBooth
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Options
|
|
199
|
+
|
|
200
|
+
| Name | Type | Default | Description |
|
|
201
|
+
| -------------------------- | -------- | -----------------------------: | ----------------------------------------------------- |
|
|
202
|
+
| `endInteractionLoopMarker` | `string` | `"__awaiting_user_response__"` | Marker used by plugins to determine when a turn ends. |
|
|
203
|
+
|
|
204
|
+
#### Examples
|
|
205
|
+
|
|
206
|
+
**Basic**
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const coreBooth = createCoreBooth(adapter, pirateBooth);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Custom end-of-turn marker**
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
const coreBooth = createCoreBooth(adapter, pirateBooth, {
|
|
216
|
+
endInteractionLoopMarker: '__custom_marker__',
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### InteractionProcessor
|
|
223
|
+
|
|
224
|
+
Engine that manages the interaction loop with the LLM and the plugin lifecycle.
|
|
225
|
+
|
|
226
|
+
#### Flow
|
|
227
|
+
|
|
228
|
+
1. Take user input
|
|
229
|
+
2. Run `onBefore...` plugin hooks
|
|
230
|
+
3. Send payload via `LLMAdapter`
|
|
231
|
+
4. Receive response
|
|
232
|
+
5. Run `onResponseReceived` hooks (e.g., execute tools)
|
|
233
|
+
6. Repeat until `shouldEndInteractionLoop` returns `true`
|
|
234
|
+
7. Run `onAfter...` hooks for cleanup
|
|
235
|
+
|
|
236
|
+
#### Important Constraints
|
|
237
|
+
|
|
238
|
+
* **Ordering matters**: tool execution and hook order follow the loop above.
|
|
239
|
+
* **End-of-turn detection** relies on the configured `endInteractionLoopMarker`.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### Registries
|
|
244
|
+
|
|
245
|
+
Booths uses three registries to manage the system’s moving parts.
|
|
246
|
+
|
|
247
|
+
#### BoothRegistry
|
|
248
|
+
|
|
249
|
+
* Manages `BoothConfig` objects (each is a specialized agent: role, description, tools, examples).
|
|
250
|
+
* Tracks the current context booth.
|
|
251
|
+
|
|
252
|
+
#### ToolRegistry
|
|
253
|
+
|
|
254
|
+
* Stores tools available to the LLM (as `ToolModule`).
|
|
255
|
+
* Provides registration helpers like `registerTools([...])`.
|
|
256
|
+
|
|
257
|
+
#### BoothPluginRegistry
|
|
258
|
+
|
|
259
|
+
* Manages plugins that hook into the interaction lifecycle.
|
|
260
|
+
* Enables modular capabilities like conversation history, context provisioning, tool provisioning/execution, and turn-finalization.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### Plugins
|
|
265
|
+
|
|
266
|
+
Plugins implement `BoothPlugin` and can influence request/response flow and tool execution.
|
|
267
|
+
|
|
268
|
+
#### Lifecycle Hooks
|
|
269
|
+
|
|
270
|
+
* `onBeforeInteractionLoopStart`
|
|
271
|
+
* `onBeforeMessageSend`
|
|
272
|
+
* `onResponseReceived`
|
|
273
|
+
* `onBeforeToolCall`
|
|
274
|
+
* `onAfterToolCall`
|
|
275
|
+
* `onToolCallError`
|
|
276
|
+
* `shouldEndInteractionLoop`
|
|
277
|
+
* `onAfterInteractionLoopEnd`
|
|
278
|
+
|
|
279
|
+
**Built-in plugin capabilities (typical set)**
|
|
280
|
+
|
|
281
|
+
* `ConversationHistoryPlugin` – maintains conversation history
|
|
282
|
+
* `ContextProviderPlugin` – supplies booth context to the LLM
|
|
283
|
+
* `ToolProviderPlugin` – exposes available tools
|
|
284
|
+
* `ToolExecutorPlugin` – executes tool calls with per-call hooks
|
|
285
|
+
* `FinishTurnPlugin` – decides when a turn is complete
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Best Practices
|
|
290
|
+
|
|
291
|
+
* Keep tools **pure** and **idempotent** when possible; return structured results.
|
|
292
|
+
* Favor **small, composable plugins**; use `onBeforeToolCall`/`onAfterToolCall`/`onToolCallError` to isolate auth, caching, and audit.
|
|
293
|
+
* Clearly separate **global tools** vs **booth-specific tools** and document access rules.
|
|
294
|
+
* Centralize **end-of-turn logic** to avoid inconsistent session behavior.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Advanced Usage
|
|
299
|
+
|
|
300
|
+
### Customizing the end-of-turn marker
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
createCoreBooth(adapter, booth, { endInteractionLoopMarker: '__custom__' });
|
|
182
304
|
```
|
|
183
|
-
## How It Works
|
|
184
305
|
|
|
185
|
-
|
|
306
|
+
Use this to coordinate UI or multi-agent systems that rely on explicit “awaiting user” signals.
|
|
307
|
+
|
|
308
|
+
### Per-tool interception & error recovery
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
class AuditPlugin implements BoothPlugin {
|
|
312
|
+
async onBeforeToolCall(ctx) { /* validate or redact */ }
|
|
313
|
+
async onAfterToolCall(ctx) { /* persist results */ }
|
|
314
|
+
async onToolCallError(ctx, err) { /* fallback or retry */ }
|
|
315
|
+
}
|
|
316
|
+
```
|
|
186
317
|
|
|
187
|
-
|
|
318
|
+
These hooks enable authentication, caching, and graceful degradation at the **individual tool** level.
|
|
188
319
|
|
|
189
|
-
|
|
190
|
-
- **`ToolRegistry`**: Manages the tools that can be made available to the LLM. Tools are functions that the AI can decide to call to perform actions or retrieve information.
|
|
191
|
-
- **`BoothPluginRegistry`**: Manages plugins that hook into the interaction lifecycle. This allows for modular and reusable functionality to be added to the system.
|
|
320
|
+
---
|
|
192
321
|
|
|
193
|
-
|
|
322
|
+
## Types
|
|
194
323
|
|
|
195
|
-
|
|
324
|
+
Core concepts you’ll interact with most:
|
|
196
325
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
- `onBeforeToolCall`: Before each individual tool call is executed _(allows modification of tool parameters, validation, and logging)_.
|
|
201
|
-
- `onAfterToolCall`: After each individual tool call is successfully executed _(allows result processing, caching, and transformation)_.
|
|
202
|
-
- `onToolCallError`: When a tool call encounters an error _(allows custom error handling and recovery)_.
|
|
203
|
-
- `shouldEndInteractionLoop`: To determine if the conversation turn is over.
|
|
204
|
-
- `onAfterInteractionLoopEnd`: After the main loop has finished.
|
|
326
|
+
* `BoothConfig`: identity, role, description, `tools: string[]`, sample `examples`
|
|
327
|
+
* `ToolModule`: `{ type: 'function' | ..., name, description, parameters, execute }`
|
|
328
|
+
* `LLMAdapter<TResponse>`: `{ invoke(params): Promise<TResponse>; interpret(response): Promise<TResponse> }`
|
|
205
329
|
|
|
206
|
-
|
|
330
|
+
---
|
|
207
331
|
|
|
208
|
-
|
|
209
|
-
- `ContextProviderPlugin`: Provides the LLM with the context of the current booth.
|
|
210
|
-
- `ToolProviderPlugin`: Provides the LLM with the available tools for the current booth.
|
|
211
|
-
- `ToolExecutorPlugin`: Executes tool calls requested by the LLM with granular hook support for individual tool call interception.
|
|
212
|
-
- `FinishTurnPlugin`: Determines when the LLM's turn is finished and it's waiting for user input. The marker used to detect conversation end can be customized via the `endInteractionLoopMarker` option (defaults to `__awaiting_user_response__`).
|
|
332
|
+
## Session & State Management
|
|
213
333
|
|
|
214
|
-
|
|
334
|
+
* Conversation continuity (history, context booth) is typically maintained by plugins.
|
|
335
|
+
* Tool calls may emit intermediate results/messages; design UI to handle “in-progress” states.
|
|
215
336
|
|
|
216
|
-
|
|
337
|
+
---
|
|
217
338
|
|
|
218
|
-
|
|
219
|
-
- **`onAfterToolCall`**: Process and transform tool results after successful execution (caching, metadata addition, data transformation)
|
|
220
|
-
- **`onToolCallError`**: Handle tool execution errors with custom recovery logic (fallback responses, error logging, graceful degradation)
|
|
339
|
+
## Error Handling
|
|
221
340
|
|
|
222
|
-
|
|
341
|
+
* Surface adapter/transport errors from `invoke`.
|
|
342
|
+
* Prefer plugin-level `onToolCallError` for tool failures; use retries or fallback responses.
|
|
223
343
|
|
|
224
|
-
|
|
344
|
+
---
|
|
225
345
|
|
|
226
|
-
|
|
346
|
+
## License
|
|
227
347
|
|
|
228
|
-
|
|
229
|
-
2. Runs the `onBefore...` plugin hooks.
|
|
230
|
-
3. Sends the payload to the LLM.
|
|
231
|
-
4. Receives the response.
|
|
232
|
-
5. Runs the `onResponseReceived` plugin hooks to process the response (e.g., execute tools).
|
|
233
|
-
6. Repeats this loop until a plugin's `shouldEndInteractionLoop` returns `true`.
|
|
234
|
-
7. Runs the `onAfter...` plugin hooks for cleanup.
|
|
348
|
+
Distributed under the project’s LICENSE file.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class f{plugins=[];registerPlugin(t){if(this.plugins.some(o=>o.id===t.id))throw new Error(`Plugin with ID ${t.id} is already registered.`);this.plugins.push(t)}getPlugins(){return this.plugins}registerPlugins(t){for(const o of t)this.registerPlugin(o)}getPluginById(t){return this.plugins.find(o=>o.id===t)}getPluginsByIds(t){const o=[];for(const e of t){const r=this.getPluginById(e);if(!r)throw new Error(`Plugin with ID ${e} is not registered.`);o.push(r)}return o}unregisterPlugin(t){const o=this.plugins.findIndex(e=>e.id===t);if(o===-1)throw new Error(`Plugin with ID ${t} does not exist.`);this.plugins.splice(o,1)}async runBeforeInteractionLoopStart(t,o,e){let r=o;for(const s of this.plugins)s.onBeforeInteractionLoopStart&&(r=await s.onBeforeInteractionLoopStart(t,r,e));return r}async runBeforeMessageSend(t,o){let e=o;for(const r of this.plugins)r.onBeforeMessageSend&&(e=await r.onBeforeMessageSend(t,e));return e}async runResponseReceived(t,o,e){let r=o;for(const s of this.plugins)s.onResponseReceived&&(r=await s.onResponseReceived(t,r,e));return r}async runShouldEndInteractionLoop(t,o,e){for(const r of this.plugins)if(await r.shouldEndInteractionLoop(t,o,e))return console.log(`BoothPluginRegistry: Plugin ${r.id} indicated loop should end.`),!0;return!1}async runAfterInteractionLoopEnd(t,o){let e=o;for(const r of this.plugins)r.onAfterInteractionLoopEnd&&(e=await r.onAfterInteractionLoopEnd(t,o));return e}async runBeforeToolCall(t,o,e){let r=o;for(const s of this.plugins)s.onBeforeToolCall&&(r=await s.onBeforeToolCall(t,r,e));return r}async runAfterToolCall(t,o,e,r){let s=e;for(const i of this.plugins)i.onAfterToolCall&&(s=await i.onAfterToolCall(t,o,s,r));return s}async runToolCallError(t,o,e,r){let s=`Error: ${e.message}`;for(const i of this.plugins)i.onToolCallError&&(s=await i.onToolCallError(t,o,e,r));return s}}const u={id:"orchestrator",role:`
|
|
2
|
+
This booth serves as the orchestration layer that analyzes user intent and routes
|
|
3
|
+
conversations to the most appropriate specialized booth configuration.
|
|
4
|
+
`,description:`
|
|
5
|
+
You are the orchestration layer responsible for determining which booth configuration
|
|
6
|
+
should be active based on user needs. Focus exclusively on routing - do not answer
|
|
7
|
+
questions or provide information directly to users.
|
|
8
|
+
|
|
9
|
+
[ROUTING STRATEGY]
|
|
10
|
+
- Analyze user request and route to the most appropriate specialized booth immediately
|
|
11
|
+
- This booth is only active for initial routing or when explicitly routed back to
|
|
12
|
+
- Once routed, the target booth handles the conversation until completion or re-routing
|
|
13
|
+
|
|
14
|
+
[ROUTING TARGETS]
|
|
15
|
+
- Ambiguous requests → Ask for clarification, then route appropriately
|
|
16
|
+
|
|
17
|
+
[CORE PRINCIPLES]
|
|
18
|
+
- Maintain illusion of single, continuous assistant
|
|
19
|
+
- Never reference booths, tools, or system mechanics to users
|
|
20
|
+
- Silent routing is preferred when intent is clear
|
|
21
|
+
- Only speak to users when clarification is absolutely necessary
|
|
22
|
+
|
|
23
|
+
[ROUTING BEHAVIOR]
|
|
24
|
+
- Clear intent: Route silently using route_to_booth() - do NOT respond to user
|
|
25
|
+
- Ambiguous intent: Ask user for clarification, then route once clarified
|
|
26
|
+
- Never respond to user AND route - it's either respond OR route, not both
|
|
27
|
+
|
|
28
|
+
[BEHAVIOR EXAMPLES]
|
|
29
|
+
- User: "How do I test my number?" → route_to_booth({ targetBooth: 'page-router-booth' })
|
|
30
|
+
- User: "I need help" → "What specifically would you like help with?" → then route based on response
|
|
31
|
+
`};class _{constructor(t,o){this.baseBooth=t,this.registerBooth(t),this.currentContextId=o||t.id}booths={};currentContextId;hasOrchestrator=!1;onMultiBoothModeEnabled;onMultiBoothModeDisabled;getCurrentContextId(){return this.currentContextId}setCurrentContextId(t){if(!this.booths[t])throw new Error(`Booth with ID ${t} is not registered.`);this.currentContextId=t}get currentContextBoothConfig(){const t=this.getBoothById(this.currentContextId);if(!t)throw new Error(`Booth with ID ${this.currentContextId} is not registered.`);return t}registerBooth(t){if(this.booths[t.id])return;this.booths[t.id]=t,Object.keys(this.booths).filter(e=>e!==u.id).length>1&&!this.hasOrchestrator&&this.enableMultiBoothMode()}get baseBoothConfig(){return this.baseBooth}get orchestratorBoothConfig(){const t=this.getBoothById("orchestrator");if(!t)throw new Error("Base booth does not have an orchestrator configuration.");return t}registerBooths(t){for(const o of t)this.registerBooth(o)}getBoothById(t){return this.booths[t]}getBoothsByIds(t){const o=[];for(const e of t){const r=this.getBoothById(e);if(!r)throw new Error(`Booth with ID ${e} is not registered.`);o.push(r)}return o}getAllBooths(){return this.booths}getSelectableBooths(){const t={};for(const[o,e]of Object.entries(this.booths))o!==this.baseBooth.id&&o!==u.id&&(t[o]=e);return t}toArray(){return Object.values(this.booths)}enableMultiBoothMode(){this.hasOrchestrator||(this.booths[u.id]=u,this.hasOrchestrator=!0,this.currentContextId=u.id,this.onMultiBoothModeEnabled?.())}disableMultiBoothMode(){this.hasOrchestrator&&(delete this.booths[u.id],this.hasOrchestrator=!1,this.currentContextId=this.baseBooth.id,this.onMultiBoothModeDisabled?.())}setMultiBoothModeCallbacks(t,o){this.onMultiBoothModeEnabled=t,this.onMultiBoothModeDisabled=o}get isMultiBoothMode(){return Object.keys(this.booths).filter(o=>o!==u.id).length>1}unregisterBooth(t){if(!this.booths[t])throw new Error(`Booth with ID ${t} does not exist.`);if(t===u.id)throw new Error("Cannot unregister orchestrator booth directly. It will be automatically managed based on booth count.");delete this.booths[t],Object.keys(this.booths).filter(e=>e!==u.id).length<=1&&this.hasOrchestrator&&this.disableMultiBoothMode()}}class v{constructor(t,o,e,r){this.boothRegistry=t,this.boothPlugins=o,this.toolRegistry=e,this.llmAdapter=r}loopLimit=10;createErrorResponse(t,o){const e=t instanceof Error?t.message:"Unknown error occurred while calling LLM",r=t instanceof Error&&"code"in t&&typeof t.code=="string"?t.code:"server_error",s={code:"server_error",message:e};if(r&&(s.code=r),o.model===void 0)throw new Error("Model must be specified in response parameters for error handling.");return{id:`error_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,created_at:Math.floor(Date.now()/1e3),output_text:"An error occurred while communicating with the language model.",error:s,incomplete_details:null,instructions:null,metadata:null,model:o.model,object:"response",output:[{id:`msg_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,content:[{type:"output_text",text:`Error: ${e}. Please try again or contact support if the issue persists.`,annotations:[]}],role:"assistant",status:"completed",type:"message"}],parallel_tool_calls:o.parallel_tool_calls||!1,temperature:o.temperature||null,tool_choice:o.tool_choice||"auto",tools:o.tools||[],top_p:o.top_p||null,status:"failed"}}async callLLM(t,o){try{const e=await this.llmAdapter.invoke(t,o);return await this.llmAdapter.interpret(e)}catch(e){return console.log("Error calling LLM:",e),this.createErrorResponse(e,t)}}async runInteractionLoop(t){let o=0,e=t,r,s=!0;for(;s&&o<this.loopLimit;){o++;const i={toolRegistry:this.toolRegistry,boothRegistry:this.boothRegistry,pluginRegistry:this.boothPlugins,llmAdapter:this.llmAdapter};e=await this.boothPlugins.runBeforeMessageSend(i,e),r=await this.callLLM(e,i),e=await this.boothPlugins.runResponseReceived(i,e,r),await this.boothPlugins.runShouldEndInteractionLoop(i,e,r)&&(console.log("Ending interaction loop because 'shouldEndInteractionLoop' returned true."),s=!1)}if(!r)throw new Error("No response received from LLM");return r}async send(t){let o;typeof t=="string"?o=[{role:"user",content:t}]:o=t;let e={input:o,tools:[]};const r=this.boothPlugins;e=await this.boothPlugins.runBeforeInteractionLoopStart({toolRegistry:this.toolRegistry,boothRegistry:this.boothRegistry,pluginRegistry:r,llmAdapter:this.llmAdapter},e,t);let s=await this.runInteractionLoop(e);return s=await this.boothPlugins.runAfterInteractionLoopEnd({toolRegistry:this.toolRegistry,boothRegistry:this.boothRegistry,pluginRegistry:r,llmAdapter:this.llmAdapter},s),s}}const L={id:"summarizer",role:'You are a highly skilled summarization AI. Your task is to read a conversation history and provide a concise, neutral, and objective summary. The summary should capture the key points, decisions made, and any unresolved questions. It must be written from a third-person perspective and should be clear enough for another AI assistant to understand the full context and continue the conversation seamlessly without needing the original transcript. Do not add any conversational fluff or introductory phrases like "Here is the summary:".',description:"A specialized booth for summarizing conversation histories."},w="route_to_booth";function y(n){const t=n.getSelectableBooths(),o=Object.values(t).map(r=>`- ${r.id}: ${r.role}
|
|
32
|
+
Examples:
|
|
33
|
+
${(r.examples||[]).map(s=>` - "${s}"`).join(`
|
|
34
|
+
`)}`).join(`
|
|
35
|
+
`),e=Object.keys(t);return{type:"function",name:w,description:`
|
|
36
|
+
Routes the conversation to a specialized booth based on the user's needs. Each booth has a
|
|
37
|
+
specific role and set of capabilities.
|
|
38
|
+
`,parameters:{type:"object",properties:{targetBooth:{type:"string",description:`
|
|
39
|
+
The ID of the booth to route the conversation to. Must be one of the following available
|
|
40
|
+
booths:
|
|
41
|
+
${o}
|
|
42
|
+
`,enum:e}},required:["targetBooth"],additionalProperties:!1},strict:!0,execute:async function({targetBooth:r}){try{return n.setCurrentContextId(r),{content:`Routed to booth ${r}`}}catch(s){return console.log("[routeToBoothTool] Error routing to booth:",s),{content:`Error: Unable to route to booth ${r}.`}}}}}class E{sessionHistory=[];plugin_id="conversation-history";plugin_name="Conversation History Plugin";plugin_description="A plugin to manage conversation history in booths.";responseContainsBoothChange(t){return t.output?t.output.some(o=>o.type==="function_call"?o.name===w:o.type==="message"&&"tool_calls"in o&&Array.isArray(o.tool_calls)?o.tool_calls.some(e=>e.type==="function"&&e.function.name===w):!1):!1}constructor(t=[]){this.sessionHistory=t}get history(){return this.sessionHistory}get id(){return this.plugin_id}get name(){return this.plugin_name}get description(){return this.plugin_description}async onBeforeInteractionLoopStart(t,o){const{input:e}=o,r=typeof e=="string"?[{role:"user",content:e}]:e||[];return this.sessionHistory.push(...r),{...o,input:this.sessionHistory}}async onResponseReceived(t,o,e){let s=[...o.input,...e?.output??[]];if(this.responseContainsBoothChange(e)){const l=`Please compact the following LLM conversation history. Take key points and make it useful for
|
|
43
|
+
an LLM that may need to look back for references. Conclude the summary with any next steps or
|
|
44
|
+
actions needed to complete the conversation.
|
|
45
|
+
|
|
46
|
+
The conversation History:
|
|
47
|
+
${JSON.stringify(this.sessionHistory)}`.trim(),c=(await P(t.llmAdapter,L).callProcessor.send(l)).output,g=s.filter(a=>"role"in a&&a.role==="user").pop();console.log("LastUserMessage: ",g),console.log("Summary: ",c),console.log("Summary: ",c.join(""));let b="";const p=c.find(a=>a.type==="message");console.log("summaryMessages",p),p&&"type"in p&&p.type=="message"&&p.content.forEach(a=>{a.type==="output_text"&&(b+=a.text)}),console.log("summaryText",b);const R={role:"developer",content:`A conversation summary up to this point: ${b}`},T=s.filter(a=>!("role"in a&&a.role==="user"||"type"in a&&a.type==="message"));this.sessionHistory=g?[...T,R,g]:[...T,R],s=this.sessionHistory}else this.sessionHistory=s;return{...o,input:s}}async shouldEndInteractionLoop(){return!1}}class I{plugin_id="context-provider";plugin_name="Context Provider Plugin";plugin_description="A plugin to provide context to booths.";get id(){return this.plugin_id}get name(){return this.plugin_name}get description(){return this.plugin_description}async onBeforeMessageSend(t,o){const e=t.boothRegistry;let s=e.baseBoothConfig.description;if(e.isMultiBoothMode){const i=e.orchestratorBoothConfig,l=e.currentContextBoothConfig;s+=`
|
|
48
|
+
|
|
49
|
+
${i.description}`,l.id!==i.id&&(s+=`
|
|
50
|
+
|
|
51
|
+
${l.description}`)}return{...o,instructions:s}}async shouldEndInteractionLoop(){return!1}}class B{tools;constructor(){this.tools=new Map}registerTools(t){t.forEach(o=>this.registerTool(o))}registerTool(t){if(this.tools.has(t.name)){console.warn(`Tool with ID '${t.name}' is already registered. Duplicate registration ignored.`);return}this.tools.set(t.name,t)}getTool(t){const o=this.tools.get(t);if(!o)throw new Error(`Tool with name ${t} is not registered.`);return o}getToolsByNames(t){const o=[];for(const e of t){const r=this.getTool(e);o.push(r)}return o}getGlobalTools(){return Array.from(this.tools.values()).filter(t=>t.global)}getServerTools(){return Array.from(this.tools.values()).filter(t=>t.execute!==void 0)}getLocalTools(){return Array.from(this.tools.values()).filter(t=>t.execute===void 0)}isLocalTool(t){const o=this.tools.get(t);return o?o.execute===void 0:!1}unregisterTool(t){if(!this.tools.has(t))throw new Error(`Tool with ID ${t} is not registered.`);this.tools.delete(t)}}function $(n){switch(n.type){case"function":return`function:${n.name}`;case"mcp":return`mcp:${n.server_label}`;case"file_search":return`file_search:${n.vector_store_ids.join(",")}`;case"web_search_preview":case"web_search_preview_2025_03_11":return`web_search:${n.type}`;case"computer_use_preview":return`computer:${n.environment}:${n.display_width}x${n.display_height}`;case"code_interpreter":return`code_interpreter:${typeof n.container=="string"?n.container:"auto"}`;case"image_generation":return`image_generation:${n.model||"gpt-image-1"}`;case"local_shell":return"local_shell";default:return`${n.type}:${JSON.stringify(n)}`}}function A(n){const t=new Set,o=[];for(const e of n){const r=$(e);t.has(r)||(t.add(r),o.push(e))}return o}class C{description="A plugin to aggregate and provide tools from base and context booths.";id="tool-provider";name="Tool Provider Plugin";async onBeforeMessageSend(t,o){const e=t.boothRegistry.baseBoothConfig,r=t.boothRegistry.currentContextBoothConfig,l=[...e.tools||[],...r?.tools||[]].filter((h,c,g)=>g.indexOf(h)===c).map(h=>t.toolRegistry.getTool(h));if(e.mcp&&l.push(...e.mcp),r?.mcp&&l.push(...r.mcp),t.boothRegistry.isMultiBoothMode){const h=y(t.boothRegistry);l.push(h)}l.push(...t.toolRegistry.getGlobalTools());const d=A(l);return{...o,tools:d}}async shouldEndInteractionLoop(){return!1}}class m{id="tool-executor";name="Tool Executor";description="Checks for tool calls in the response, executes them, and adds the results to the message history.";async executeToolCall(t,o,e){try{const r=await t.pluginRegistry.runBeforeToolCall(t,o,e),s=t.toolRegistry.getTool(r.name);if(!s)return{type:"function_call_output",call_id:r.call_id,output:`Error: Tool '${r.name}' not found.`};if(!s.execute)return{type:"function_call_output",call_id:r.call_id,output:`Error: Tool '${r.name}' does not have an 'execute' method.`};const i=await s.execute(JSON.parse(r.arguments)),l=await t.pluginRegistry.runAfterToolCall(t,r,i,e);return l||console.log(`Booths.ToolExecutorPlugin: Tool execution returned an empty results for ${s.name}. This may result in 400 responses from the LLM.`),{type:"function_call_output",call_id:r.call_id,output:JSON.stringify(l)}}catch(r){console.log(`Error executing tool ${o.name}:`,r);const s=await t.pluginRegistry.runToolCallError(t,o,r,e);return{type:"function_call_output",call_id:o.call_id,output:typeof s=="string"?s:JSON.stringify(s)}}}static extractFunctionCalls(t){return t.filter(o=>o.type==="function_call")}async onResponseReceived(t,o,e){const r=e?.output??[],s=m.extractFunctionCalls(r);if(!s.length)return o;const i=[];for(let l=0;l<s.length;l++){const d=s[l];if(t.toolRegistry.isLocalTool(d.name))continue;const h={responseParams:o,response:e,toolCallIndex:l,totalToolCalls:s.length},c=await this.executeToolCall(t,d,h);i.push(c)}return{...o,input:[...o.input,...i]}}async shouldEndInteractionLoop(){return!1}}class M{constructor(t="__awaiting_user_response__"){this.marker=t}description="A plugin to ensure the interaction loop can be finished.";id="finish-turn-plugin";name="Finish Turn Plugin";async onBeforeMessageSend(t,o){let e=o.instructions||"";return e=`
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
[MUST]
|
|
56
|
+
- Always add the marker "${this.marker}" at the end of your output when you have finished your part
|
|
57
|
+
- This marker indicates that the interaction loop should end.
|
|
58
|
+
|
|
59
|
+
${e}
|
|
60
|
+
`,{...o,instructions:e}}async shouldEndInteractionLoop(t,o,e){const s=JSON.stringify(e.output).includes(this.marker),i=e.status==="failed"||e.error!==null;return s||i}async onAfterInteractionLoopEnd(t,o){const e=JSON.stringify(o);if(e.includes(this.marker)){const r=e.replace(new RegExp(this.marker,"g"),"");return JSON.parse(r)}return o}}function P(n,t){const o=new _(t),e=new B,r=new f;return new x({llmAdapter:n,booths:o,tools:e,boothPlugins:r})}class x{boothPluginRegistry;boothRegistry;callProcessor;systemPluginsRegistry;toolRegistry;constructor(t){if(this.boothPluginRegistry=t?.boothPlugins??new f,this.boothRegistry=t.booths,this.toolRegistry=t?.tools??new B,this.boothRegistry.setMultiBoothModeCallbacks(()=>{const o=y(this.boothRegistry);this.toolRegistry.registerTools([o])},()=>{}),this.boothRegistry.isMultiBoothMode){const o=y(this.boothRegistry);this.toolRegistry.registerTools([o])}this.systemPluginsRegistry=new f,this.systemPluginsRegistry.registerPlugins([new E(t.sessionHistory),new I,new C,new m,new M(t.endInteractionLoopMarker)]),this.systemPluginsRegistry.registerPlugins(this.boothPluginRegistry.getPlugins()),this.callProcessor=new v(this.boothRegistry,this.systemPluginsRegistry,this.toolRegistry,t.llmAdapter)}}exports.BoothPluginRegistry=f;exports.BoothRegistry=_;exports.ContextProviderPlugin=I;exports.ConversationHistoryPlugin=E;exports.CoreBooth=x;exports.FinishTurnPlugin=M;exports.InteractionProcessor=v;exports.ToolExecutorPlugin=m;exports.ToolProviderPlugin=C;exports.ToolRegistry=B;exports.createCoreBooth=P;exports.createRouteToBoothTool=y;
|
package/dist/index.d.ts
CHANGED
|
@@ -408,6 +408,13 @@ export declare class BoothRegistry {
|
|
|
408
408
|
* @returns Record of all booth configurations indexed by their IDs
|
|
409
409
|
*/
|
|
410
410
|
getAllBooths(): Record<string, BoothConfig>;
|
|
411
|
+
/**
|
|
412
|
+
* Returns only booths that should be available for routing (excludes core and orchestrator booths).
|
|
413
|
+
* This prevents double context issues by ensuring system booths are not selectable.
|
|
414
|
+
*
|
|
415
|
+
* @returns Record of selectable booth configurations indexed by their IDs
|
|
416
|
+
*/
|
|
417
|
+
getSelectableBooths(): Record<string, BoothConfig>;
|
|
411
418
|
toArray(): BoothConfig[];
|
|
412
419
|
/**
|
|
413
420
|
* Enables multi-booth mode by registering the orchestrator and setting it as current context.
|
|
@@ -848,6 +855,10 @@ export declare type RepositoryUtilities = {
|
|
|
848
855
|
|
|
849
856
|
export { ResponseCreateParamsNonStreaming }
|
|
850
857
|
|
|
858
|
+
export { ResponseInput }
|
|
859
|
+
|
|
860
|
+
export { ResponseInputItem }
|
|
861
|
+
|
|
851
862
|
/**
|
|
852
863
|
* Represents the result of processing a single tool call.
|
|
853
864
|
*/
|
|
@@ -899,6 +910,12 @@ export declare class ToolExecutorPlugin implements BoothPlugin {
|
|
|
899
910
|
* @private
|
|
900
911
|
*/
|
|
901
912
|
private executeToolCall;
|
|
913
|
+
/**
|
|
914
|
+
* Extracts function call objects from an array of response output items.
|
|
915
|
+
*
|
|
916
|
+
* @param {ResponseOutputItem[]} output - The array of response output items to filter.
|
|
917
|
+
* @return {ResponseFunctionToolCall[]} An array containing only the function call objects from the input.
|
|
918
|
+
*/
|
|
902
919
|
static extractFunctionCalls(output: ResponseOutputItem[]): ResponseFunctionToolCall[];
|
|
903
920
|
/**
|
|
904
921
|
* After a response is received from the LLM, this hook checks for tool calls. If any are found,
|
package/dist/index.js
CHANGED
|
@@ -202,7 +202,7 @@ class y {
|
|
|
202
202
|
return s;
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
const
|
|
205
|
+
const u = {
|
|
206
206
|
id: "orchestrator",
|
|
207
207
|
role: `
|
|
208
208
|
This booth serves as the orchestration layer that analyzes user intent and routes
|
|
@@ -315,7 +315,7 @@ class T {
|
|
|
315
315
|
if (this.booths[t.id])
|
|
316
316
|
return;
|
|
317
317
|
this.booths[t.id] = t, Object.keys(this.booths).filter(
|
|
318
|
-
(e) => e !==
|
|
318
|
+
(e) => e !== u.id
|
|
319
319
|
).length > 1 && !this.hasOrchestrator && this.enableMultiBoothMode();
|
|
320
320
|
}
|
|
321
321
|
/**
|
|
@@ -384,6 +384,18 @@ class T {
|
|
|
384
384
|
getAllBooths() {
|
|
385
385
|
return this.booths;
|
|
386
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Returns only booths that should be available for routing (excludes core and orchestrator booths).
|
|
389
|
+
* This prevents double context issues by ensuring system booths are not selectable.
|
|
390
|
+
*
|
|
391
|
+
* @returns Record of selectable booth configurations indexed by their IDs
|
|
392
|
+
*/
|
|
393
|
+
getSelectableBooths() {
|
|
394
|
+
const t = {};
|
|
395
|
+
for (const [o, e] of Object.entries(this.booths))
|
|
396
|
+
o !== this.baseBooth.id && o !== u.id && (t[o] = e);
|
|
397
|
+
return t;
|
|
398
|
+
}
|
|
387
399
|
toArray() {
|
|
388
400
|
return Object.values(this.booths);
|
|
389
401
|
}
|
|
@@ -392,14 +404,14 @@ class T {
|
|
|
392
404
|
* @private
|
|
393
405
|
*/
|
|
394
406
|
enableMultiBoothMode() {
|
|
395
|
-
this.hasOrchestrator || (this.booths[
|
|
407
|
+
this.hasOrchestrator || (this.booths[u.id] = u, this.hasOrchestrator = !0, this.currentContextId = u.id, this.onMultiBoothModeEnabled?.());
|
|
396
408
|
}
|
|
397
409
|
/**
|
|
398
410
|
* Disables multi-booth mode by unregistering the orchestrator and resetting context to base booth.
|
|
399
411
|
* @private
|
|
400
412
|
*/
|
|
401
413
|
disableMultiBoothMode() {
|
|
402
|
-
this.hasOrchestrator && (delete this.booths[
|
|
414
|
+
this.hasOrchestrator && (delete this.booths[u.id], this.hasOrchestrator = !1, this.currentContextId = this.baseBooth.id, this.onMultiBoothModeDisabled?.());
|
|
403
415
|
}
|
|
404
416
|
/**
|
|
405
417
|
* Sets callback functions for when multi-booth mode is enabled/disabled.
|
|
@@ -413,7 +425,7 @@ class T {
|
|
|
413
425
|
}
|
|
414
426
|
get isMultiBoothMode() {
|
|
415
427
|
return Object.keys(this.booths).filter(
|
|
416
|
-
(o) => o !==
|
|
428
|
+
(o) => o !== u.id
|
|
417
429
|
).length > 1;
|
|
418
430
|
}
|
|
419
431
|
/**
|
|
@@ -425,12 +437,12 @@ class T {
|
|
|
425
437
|
unregisterBooth(t) {
|
|
426
438
|
if (!this.booths[t])
|
|
427
439
|
throw new Error(`Booth with ID ${t} does not exist.`);
|
|
428
|
-
if (t ===
|
|
440
|
+
if (t === u.id)
|
|
429
441
|
throw new Error(
|
|
430
442
|
"Cannot unregister orchestrator booth directly. It will be automatically managed based on booth count."
|
|
431
443
|
);
|
|
432
444
|
delete this.booths[t], Object.keys(this.booths).filter(
|
|
433
|
-
(e) => e !==
|
|
445
|
+
(e) => e !== u.id
|
|
434
446
|
).length <= 1 && this.hasOrchestrator && this.disableMultiBoothMode();
|
|
435
447
|
}
|
|
436
448
|
}
|
|
@@ -522,36 +534,18 @@ class E {
|
|
|
522
534
|
let o = 0, e = t, r, s = !0;
|
|
523
535
|
for (; s && o < this.loopLimit; ) {
|
|
524
536
|
o++;
|
|
525
|
-
const i =
|
|
526
|
-
e = await this.boothPlugins.runBeforeMessageSend(
|
|
527
|
-
{
|
|
528
|
-
toolRegistry: this.toolRegistry,
|
|
529
|
-
boothRegistry: this.boothRegistry,
|
|
530
|
-
pluginRegistry: i,
|
|
531
|
-
llmAdapter: this.llmAdapter
|
|
532
|
-
},
|
|
533
|
-
e
|
|
534
|
-
), r = await this.callLLM(e, {
|
|
537
|
+
const i = {
|
|
535
538
|
toolRegistry: this.toolRegistry,
|
|
536
539
|
boothRegistry: this.boothRegistry,
|
|
537
|
-
pluginRegistry:
|
|
540
|
+
pluginRegistry: this.boothPlugins,
|
|
538
541
|
llmAdapter: this.llmAdapter
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
boothRegistry: this.boothRegistry,
|
|
543
|
-
pluginRegistry: i,
|
|
544
|
-
llmAdapter: this.llmAdapter
|
|
545
|
-
},
|
|
542
|
+
};
|
|
543
|
+
e = await this.boothPlugins.runBeforeMessageSend(i, e), r = await this.callLLM(e, i), e = await this.boothPlugins.runResponseReceived(
|
|
544
|
+
i,
|
|
546
545
|
e,
|
|
547
546
|
r
|
|
548
547
|
), await this.boothPlugins.runShouldEndInteractionLoop(
|
|
549
|
-
|
|
550
|
-
toolRegistry: this.toolRegistry,
|
|
551
|
-
boothRegistry: this.boothRegistry,
|
|
552
|
-
pluginRegistry: i,
|
|
553
|
-
llmAdapter: this.llmAdapter
|
|
554
|
-
},
|
|
548
|
+
i,
|
|
555
549
|
e,
|
|
556
550
|
r
|
|
557
551
|
) && (console.log("Ending interaction loop because 'shouldEndInteractionLoop' returned true."), s = !1);
|
|
@@ -607,7 +601,7 @@ const v = {
|
|
|
607
601
|
description: "A specialized booth for summarizing conversation histories."
|
|
608
602
|
}, m = "route_to_booth";
|
|
609
603
|
function b(n) {
|
|
610
|
-
const t = n.
|
|
604
|
+
const t = n.getSelectableBooths(), o = Object.values(t).map(
|
|
611
605
|
(r) => `- ${r.id}: ${r.role}
|
|
612
606
|
Examples:
|
|
613
607
|
${(r.examples || []).map((s) => ` - "${s}"`).join(`
|
|
@@ -753,20 +747,23 @@ class I {
|
|
|
753
747
|
async onResponseReceived(t, o, e) {
|
|
754
748
|
let s = [...o.input, ...e?.output ?? []];
|
|
755
749
|
if (this.responseContainsBoothChange(e)) {
|
|
756
|
-
const l = `Please
|
|
750
|
+
const l = `Please compact the following LLM conversation history. Take key points and make it useful for
|
|
751
|
+
an LLM that may need to look back for references. Conclude the summary with any next steps or
|
|
752
|
+
actions needed to complete the conversation.
|
|
757
753
|
|
|
758
|
-
|
|
754
|
+
The conversation History:
|
|
755
|
+
${JSON.stringify(this.sessionHistory)}`.trim(), c = (await $(t.llmAdapter, v).callProcessor.send(l)).output, g = s.filter((a) => "role" in a && a.role === "user").pop();
|
|
759
756
|
console.log("LastUserMessage: ", g), console.log("Summary: ", c), console.log("Summary: ", c.join(""));
|
|
760
757
|
let f = "";
|
|
761
758
|
const p = c.find((a) => a.type === "message");
|
|
762
759
|
console.log("summaryMessages", p), p && "type" in p && p.type == "message" && p.content.forEach((a) => {
|
|
763
760
|
a.type === "output_text" && (f += a.text);
|
|
764
761
|
}), console.log("summaryText", f);
|
|
765
|
-
const
|
|
762
|
+
const B = {
|
|
766
763
|
role: "developer",
|
|
767
764
|
content: `A conversation summary up to this point: ${f}`
|
|
768
|
-
},
|
|
769
|
-
this.sessionHistory = g ? [...
|
|
765
|
+
}, R = s.filter((a) => !("role" in a && a.role === "user" || "type" in a && a.type === "message"));
|
|
766
|
+
this.sessionHistory = g ? [...R, B, g] : [...R, B], s = this.sessionHistory;
|
|
770
767
|
} else
|
|
771
768
|
this.sessionHistory = s;
|
|
772
769
|
return {
|
|
@@ -945,7 +942,7 @@ class _ {
|
|
|
945
942
|
this.tools.delete(t);
|
|
946
943
|
}
|
|
947
944
|
}
|
|
948
|
-
function
|
|
945
|
+
function M(n) {
|
|
949
946
|
switch (n.type) {
|
|
950
947
|
case "function":
|
|
951
948
|
return `function:${n.name}`;
|
|
@@ -968,15 +965,15 @@ function x(n) {
|
|
|
968
965
|
return `${n.type}:${JSON.stringify(n)}`;
|
|
969
966
|
}
|
|
970
967
|
}
|
|
971
|
-
function
|
|
968
|
+
function x(n) {
|
|
972
969
|
const t = /* @__PURE__ */ new Set(), o = [];
|
|
973
970
|
for (const e of n) {
|
|
974
|
-
const r =
|
|
971
|
+
const r = M(e);
|
|
975
972
|
t.has(r) || (t.add(r), o.push(e));
|
|
976
973
|
}
|
|
977
974
|
return o;
|
|
978
975
|
}
|
|
979
|
-
class
|
|
976
|
+
class P {
|
|
980
977
|
description = "A plugin to aggregate and provide tools from base and context booths.";
|
|
981
978
|
id = "tool-provider";
|
|
982
979
|
name = "Tool Provider Plugin";
|
|
@@ -989,15 +986,15 @@ class A {
|
|
|
989
986
|
* @returns The updated response parameters with the aggregated list of tools.
|
|
990
987
|
*/
|
|
991
988
|
async onBeforeMessageSend(t, o) {
|
|
992
|
-
const e = t.boothRegistry.baseBoothConfig, r = t.boothRegistry.currentContextBoothConfig, l = [...e.tools || [], ...r?.tools || []].filter((
|
|
993
|
-
(
|
|
989
|
+
const e = t.boothRegistry.baseBoothConfig, r = t.boothRegistry.currentContextBoothConfig, l = [...e.tools || [], ...r?.tools || []].filter((h, c, g) => g.indexOf(h) === c).map(
|
|
990
|
+
(h) => t.toolRegistry.getTool(h)
|
|
994
991
|
);
|
|
995
992
|
if (e.mcp && l.push(...e.mcp), r?.mcp && l.push(...r.mcp), t.boothRegistry.isMultiBoothMode) {
|
|
996
|
-
const
|
|
997
|
-
l.push(
|
|
993
|
+
const h = b(t.boothRegistry);
|
|
994
|
+
l.push(h);
|
|
998
995
|
}
|
|
999
996
|
l.push(...t.toolRegistry.getGlobalTools());
|
|
1000
|
-
const d =
|
|
997
|
+
const d = x(l);
|
|
1001
998
|
return {
|
|
1002
999
|
...o,
|
|
1003
1000
|
tools: d
|
|
@@ -1071,6 +1068,12 @@ class w {
|
|
|
1071
1068
|
};
|
|
1072
1069
|
}
|
|
1073
1070
|
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Extracts function call objects from an array of response output items.
|
|
1073
|
+
*
|
|
1074
|
+
* @param {ResponseOutputItem[]} output - The array of response output items to filter.
|
|
1075
|
+
* @return {ResponseFunctionToolCall[]} An array containing only the function call objects from the input.
|
|
1076
|
+
*/
|
|
1074
1077
|
static extractFunctionCalls(t) {
|
|
1075
1078
|
return t.filter(
|
|
1076
1079
|
(o) => o.type === "function_call"
|
|
@@ -1094,12 +1097,12 @@ class w {
|
|
|
1094
1097
|
const d = s[l];
|
|
1095
1098
|
if (t.toolRegistry.isLocalTool(d.name))
|
|
1096
1099
|
continue;
|
|
1097
|
-
const
|
|
1100
|
+
const h = {
|
|
1098
1101
|
responseParams: o,
|
|
1099
1102
|
response: e,
|
|
1100
1103
|
toolCallIndex: l,
|
|
1101
1104
|
totalToolCalls: s.length
|
|
1102
|
-
}, c = await this.executeToolCall(t, d,
|
|
1105
|
+
}, c = await this.executeToolCall(t, d, h);
|
|
1103
1106
|
i.push(c);
|
|
1104
1107
|
}
|
|
1105
1108
|
return {
|
|
@@ -1116,7 +1119,7 @@ class w {
|
|
|
1116
1119
|
return !1;
|
|
1117
1120
|
}
|
|
1118
1121
|
}
|
|
1119
|
-
class
|
|
1122
|
+
class L {
|
|
1120
1123
|
constructor(t = "__awaiting_user_response__") {
|
|
1121
1124
|
this.marker = t;
|
|
1122
1125
|
}
|
|
@@ -1176,14 +1179,14 @@ class P {
|
|
|
1176
1179
|
}
|
|
1177
1180
|
function $(n, t) {
|
|
1178
1181
|
const o = new T(t), e = new _(), r = new y();
|
|
1179
|
-
return new
|
|
1182
|
+
return new A({
|
|
1180
1183
|
llmAdapter: n,
|
|
1181
1184
|
booths: o,
|
|
1182
1185
|
tools: e,
|
|
1183
1186
|
boothPlugins: r
|
|
1184
1187
|
});
|
|
1185
1188
|
}
|
|
1186
|
-
class
|
|
1189
|
+
class A {
|
|
1187
1190
|
/**
|
|
1188
1191
|
* Represents a registry that maintains a collection of plugins for a booth system.
|
|
1189
1192
|
* The boothPluginRegistry is used to manage and access plugins that enhance
|
|
@@ -1252,9 +1255,9 @@ class L {
|
|
|
1252
1255
|
this.systemPluginsRegistry = new y(), this.systemPluginsRegistry.registerPlugins([
|
|
1253
1256
|
new I(t.sessionHistory),
|
|
1254
1257
|
new C(),
|
|
1255
|
-
new
|
|
1258
|
+
new P(),
|
|
1256
1259
|
new w(),
|
|
1257
|
-
new
|
|
1260
|
+
new L(t.endInteractionLoopMarker)
|
|
1258
1261
|
]), this.systemPluginsRegistry.registerPlugins(this.boothPluginRegistry.getPlugins()), this.callProcessor = new E(
|
|
1259
1262
|
this.boothRegistry,
|
|
1260
1263
|
this.systemPluginsRegistry,
|
|
@@ -1268,11 +1271,11 @@ export {
|
|
|
1268
1271
|
T as BoothRegistry,
|
|
1269
1272
|
C as ContextProviderPlugin,
|
|
1270
1273
|
I as ConversationHistoryPlugin,
|
|
1271
|
-
|
|
1272
|
-
|
|
1274
|
+
A as CoreBooth,
|
|
1275
|
+
L as FinishTurnPlugin,
|
|
1273
1276
|
E as InteractionProcessor,
|
|
1274
1277
|
w as ToolExecutorPlugin,
|
|
1275
|
-
|
|
1278
|
+
P as ToolProviderPlugin,
|
|
1276
1279
|
_ as ToolRegistry,
|
|
1277
1280
|
$ as createCoreBooth,
|
|
1278
1281
|
b as createRouteToBoothTool
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "booths",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
|
-
"import": "./dist/index.js"
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
16
17
|
}
|
|
17
18
|
},
|
|
18
19
|
"scripts": {
|