confused-ai-core 0.1.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/FEATURES.md +169 -0
- package/package.json +119 -0
- package/src/agent.ts +187 -0
- package/src/agentic/index.ts +87 -0
- package/src/agentic/runner.ts +386 -0
- package/src/agentic/types.ts +91 -0
- package/src/artifacts/artifact.ts +417 -0
- package/src/artifacts/index.ts +42 -0
- package/src/artifacts/media.ts +304 -0
- package/src/cli/index.ts +122 -0
- package/src/core/base-agent.ts +151 -0
- package/src/core/context-builder.ts +106 -0
- package/src/core/index.ts +8 -0
- package/src/core/schemas.ts +17 -0
- package/src/core/types.ts +158 -0
- package/src/create-agent.ts +309 -0
- package/src/debug-logger.ts +188 -0
- package/src/dx/agent.ts +88 -0
- package/src/dx/define-agent.ts +183 -0
- package/src/dx/dev-logger.ts +57 -0
- package/src/dx/index.ts +11 -0
- package/src/errors.ts +175 -0
- package/src/execution/engine.ts +522 -0
- package/src/execution/graph-builder.ts +362 -0
- package/src/execution/index.ts +8 -0
- package/src/execution/types.ts +257 -0
- package/src/execution/worker-pool.ts +308 -0
- package/src/extensions/index.ts +123 -0
- package/src/guardrails/allowlist.ts +155 -0
- package/src/guardrails/index.ts +17 -0
- package/src/guardrails/types.ts +159 -0
- package/src/guardrails/validator.ts +265 -0
- package/src/index.ts +74 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/types.ts +52 -0
- package/src/learning/in-memory-store.ts +72 -0
- package/src/learning/index.ts +6 -0
- package/src/learning/types.ts +42 -0
- package/src/llm/cache.ts +300 -0
- package/src/llm/index.ts +22 -0
- package/src/llm/model-resolver.ts +81 -0
- package/src/llm/openai-provider.ts +313 -0
- package/src/llm/openrouter-provider.ts +29 -0
- package/src/llm/types.ts +131 -0
- package/src/memory/in-memory-store.ts +255 -0
- package/src/memory/index.ts +7 -0
- package/src/memory/types.ts +193 -0
- package/src/memory/vector-store.ts +251 -0
- package/src/observability/console-logger.ts +123 -0
- package/src/observability/index.ts +12 -0
- package/src/observability/metrics.ts +85 -0
- package/src/observability/otlp-exporter.ts +417 -0
- package/src/observability/tracer.ts +105 -0
- package/src/observability/types.ts +341 -0
- package/src/orchestration/agent-adapter.ts +33 -0
- package/src/orchestration/index.ts +34 -0
- package/src/orchestration/load-balancer.ts +151 -0
- package/src/orchestration/mcp-types.ts +59 -0
- package/src/orchestration/message-bus.ts +192 -0
- package/src/orchestration/orchestrator.ts +349 -0
- package/src/orchestration/pipeline.ts +66 -0
- package/src/orchestration/supervisor.ts +107 -0
- package/src/orchestration/swarm.ts +1099 -0
- package/src/orchestration/toolkit.ts +47 -0
- package/src/orchestration/types.ts +339 -0
- package/src/planner/classical-planner.ts +383 -0
- package/src/planner/index.ts +8 -0
- package/src/planner/llm-planner.ts +353 -0
- package/src/planner/types.ts +227 -0
- package/src/planner/validator.ts +297 -0
- package/src/production/circuit-breaker.ts +290 -0
- package/src/production/graceful-shutdown.ts +251 -0
- package/src/production/health.ts +333 -0
- package/src/production/index.ts +57 -0
- package/src/production/latency-eval.ts +62 -0
- package/src/production/rate-limiter.ts +287 -0
- package/src/production/resumable-stream.ts +289 -0
- package/src/production/types.ts +81 -0
- package/src/sdk/index.ts +374 -0
- package/src/session/db-driver.ts +50 -0
- package/src/session/in-memory-store.ts +235 -0
- package/src/session/index.ts +12 -0
- package/src/session/sql-store.ts +315 -0
- package/src/session/sqlite-store.ts +61 -0
- package/src/session/types.ts +153 -0
- package/src/tools/base-tool.ts +223 -0
- package/src/tools/browser-tool.ts +123 -0
- package/src/tools/calculator-tool.ts +265 -0
- package/src/tools/file-tools.ts +394 -0
- package/src/tools/github-tool.ts +432 -0
- package/src/tools/hackernews-tool.ts +187 -0
- package/src/tools/http-tool.ts +118 -0
- package/src/tools/index.ts +99 -0
- package/src/tools/jira-tool.ts +373 -0
- package/src/tools/notion-tool.ts +322 -0
- package/src/tools/openai-tool.ts +236 -0
- package/src/tools/registry.ts +131 -0
- package/src/tools/serpapi-tool.ts +234 -0
- package/src/tools/shell-tool.ts +118 -0
- package/src/tools/slack-tool.ts +327 -0
- package/src/tools/telegram-tool.ts +127 -0
- package/src/tools/types.ts +229 -0
- package/src/tools/websearch-tool.ts +335 -0
- package/src/tools/wikipedia-tool.ts +177 -0
- package/src/tools/yfinance-tool.ts +33 -0
- package/src/voice/index.ts +17 -0
- package/src/voice/voice-provider.ts +228 -0
- package/tests/artifact.test.ts +241 -0
- package/tests/circuit-breaker.test.ts +171 -0
- package/tests/health.test.ts +192 -0
- package/tests/llm-cache.test.ts +186 -0
- package/tests/rate-limiter.test.ts +161 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notion tool implementation - TypeScript NotionTools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { BaseTool, BaseToolConfig } from './base-tool.js';
|
|
7
|
+
import { ToolContext, ToolCategory } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Notion API types
|
|
11
|
+
*/
|
|
12
|
+
interface NotionPage {
|
|
13
|
+
id: string;
|
|
14
|
+
url: string;
|
|
15
|
+
properties: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface NotionSearchResult {
|
|
19
|
+
results: NotionPage[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface NotionResult {
|
|
23
|
+
data?: unknown;
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Base Notion tool with common authentication
|
|
29
|
+
*/
|
|
30
|
+
abstract class BaseNotionTool<TParams extends z.ZodObject<Record<string, z.ZodType>>> extends BaseTool<TParams, NotionResult> {
|
|
31
|
+
protected token: string;
|
|
32
|
+
protected databaseId?: string;
|
|
33
|
+
protected baseUrl = 'https://api.notion.com/v1';
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
config: Partial<Omit<BaseToolConfig<TParams>, 'parameters'>> & {
|
|
37
|
+
token?: string;
|
|
38
|
+
databaseId?: string;
|
|
39
|
+
},
|
|
40
|
+
params: TParams
|
|
41
|
+
) {
|
|
42
|
+
super({
|
|
43
|
+
name: config.name || 'notion.tool',
|
|
44
|
+
description: config.description || 'Notion tool',
|
|
45
|
+
parameters: params,
|
|
46
|
+
category: config.category || ToolCategory.API,
|
|
47
|
+
permissions: {
|
|
48
|
+
allowNetwork: true,
|
|
49
|
+
maxExecutionTimeMs: 30000,
|
|
50
|
+
...config.permissions,
|
|
51
|
+
},
|
|
52
|
+
...config,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.token = config.token || process.env.NOTION_API_TOKEN || '';
|
|
56
|
+
this.databaseId = config.databaseId || process.env.NOTION_DATABASE_ID;
|
|
57
|
+
|
|
58
|
+
if (!this.token) {
|
|
59
|
+
throw new Error('Notion API token is required. Set NOTION_API_TOKEN environment variable or pass token in config.');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected async notionRequest(endpoint: string, options: RequestInit = {}): Promise<Response> {
|
|
64
|
+
return fetch(`${this.baseUrl}${endpoint}`, {
|
|
65
|
+
...options,
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: `Bearer ${this.token}`,
|
|
68
|
+
'Notion-Version': '2022-06-28',
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
...(options.headers || {}),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create page tool
|
|
78
|
+
*/
|
|
79
|
+
const NotionCreatePageParameters = z.object({
|
|
80
|
+
parent_database_id: z.string().optional().describe('Database ID to create page in (overrides default)'),
|
|
81
|
+
title: z.string().describe('The page title'),
|
|
82
|
+
content: z.string().describe('The page content'),
|
|
83
|
+
properties: z.record(z.string(), z.unknown()).optional().describe('Additional properties for the page'),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export class NotionCreatePageTool extends BaseNotionTool<typeof NotionCreatePageParameters> {
|
|
87
|
+
constructor(
|
|
88
|
+
config?: Partial<Omit<BaseToolConfig<typeof NotionCreatePageParameters>, 'parameters'>> & {
|
|
89
|
+
token?: string;
|
|
90
|
+
databaseId?: string;
|
|
91
|
+
}
|
|
92
|
+
) {
|
|
93
|
+
super(
|
|
94
|
+
{
|
|
95
|
+
name: config?.name ?? 'notion.create_page',
|
|
96
|
+
description: config?.description ?? 'Create a new page in Notion',
|
|
97
|
+
...config,
|
|
98
|
+
},
|
|
99
|
+
NotionCreatePageParameters
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected async performExecute(
|
|
104
|
+
params: z.infer<typeof NotionCreatePageParameters>,
|
|
105
|
+
_context: ToolContext
|
|
106
|
+
): Promise<NotionResult> {
|
|
107
|
+
const databaseId = params.parent_database_id || this.databaseId;
|
|
108
|
+
|
|
109
|
+
if (!databaseId) {
|
|
110
|
+
return {
|
|
111
|
+
error: 'Database ID is required. Set NOTION_DATABASE_ID environment variable, pass databaseId in config, or provide parent_database_id in parameters.',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const body: Record<string, unknown> = {
|
|
117
|
+
parent: { database_id: databaseId },
|
|
118
|
+
properties: {
|
|
119
|
+
Name: {
|
|
120
|
+
title: [{ text: { content: params.title } }],
|
|
121
|
+
},
|
|
122
|
+
...params.properties,
|
|
123
|
+
},
|
|
124
|
+
children: [
|
|
125
|
+
{
|
|
126
|
+
object: 'block',
|
|
127
|
+
type: 'paragraph',
|
|
128
|
+
paragraph: {
|
|
129
|
+
rich_text: [{ type: 'text', text: { content: params.content } }],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const response = await this.notionRequest('/pages', {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
body: JSON.stringify(body),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
const errorData = (await response.json()) as { message?: string };
|
|
142
|
+
throw new Error(errorData.message || `Notion API error: ${response.status}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const data = (await response.json()) as NotionPage;
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
data: {
|
|
149
|
+
id: data.id,
|
|
150
|
+
url: data.url,
|
|
151
|
+
title: params.title,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return {
|
|
156
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Search pages tool
|
|
164
|
+
*/
|
|
165
|
+
const NotionSearchParameters = z.object({
|
|
166
|
+
query: z.string().describe('Search query string'),
|
|
167
|
+
filter: z.enum(['page', 'database']).optional().describe('Filter by type'),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export class NotionSearchTool extends BaseNotionTool<typeof NotionSearchParameters> {
|
|
171
|
+
constructor(
|
|
172
|
+
config?: Partial<Omit<BaseToolConfig<typeof NotionSearchParameters>, 'parameters'>> & {
|
|
173
|
+
token?: string;
|
|
174
|
+
}
|
|
175
|
+
) {
|
|
176
|
+
super(
|
|
177
|
+
{
|
|
178
|
+
name: config?.name ?? 'notion.search',
|
|
179
|
+
description: config?.description ?? 'Search for pages and databases in Notion',
|
|
180
|
+
...config,
|
|
181
|
+
},
|
|
182
|
+
NotionSearchParameters
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
protected async performExecute(
|
|
187
|
+
params: z.infer<typeof NotionSearchParameters>,
|
|
188
|
+
_context: ToolContext
|
|
189
|
+
): Promise<NotionResult> {
|
|
190
|
+
try {
|
|
191
|
+
const body: Record<string, unknown> = {
|
|
192
|
+
query: params.query,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (params.filter) {
|
|
196
|
+
body.filter = {
|
|
197
|
+
value: params.filter,
|
|
198
|
+
property: 'object',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const response = await this.notionRequest('/search', {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
body: JSON.stringify(body),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const errorData = (await response.json()) as { message?: string };
|
|
209
|
+
throw new Error(errorData.message || `Notion API error: ${response.status}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const data = (await response.json()) as NotionSearchResult;
|
|
213
|
+
|
|
214
|
+
const results = data.results.map((page) => ({
|
|
215
|
+
id: page.id,
|
|
216
|
+
url: page.url,
|
|
217
|
+
object: page.properties,
|
|
218
|
+
}));
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
data: {
|
|
222
|
+
count: results.length,
|
|
223
|
+
results,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return {
|
|
228
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Update page tool
|
|
236
|
+
*/
|
|
237
|
+
const NotionUpdatePageParameters = z.object({
|
|
238
|
+
page_id: z.string().describe('The page ID to update'),
|
|
239
|
+
content: z.string().describe('Content to append to the page'),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
export class NotionUpdatePageTool extends BaseNotionTool<typeof NotionUpdatePageParameters> {
|
|
243
|
+
constructor(
|
|
244
|
+
config?: Partial<Omit<BaseToolConfig<typeof NotionUpdatePageParameters>, 'parameters'>> & {
|
|
245
|
+
token?: string;
|
|
246
|
+
}
|
|
247
|
+
) {
|
|
248
|
+
super(
|
|
249
|
+
{
|
|
250
|
+
name: config?.name ?? 'notion.update_page',
|
|
251
|
+
description: config?.description ?? 'Add content to an existing Notion page',
|
|
252
|
+
...config,
|
|
253
|
+
},
|
|
254
|
+
NotionUpdatePageParameters
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
protected async performExecute(
|
|
259
|
+
params: z.infer<typeof NotionUpdatePageParameters>,
|
|
260
|
+
_context: ToolContext
|
|
261
|
+
): Promise<NotionResult> {
|
|
262
|
+
try {
|
|
263
|
+
const response = await this.notionRequest(`/blocks/${params.page_id}/children`, {
|
|
264
|
+
method: 'PATCH',
|
|
265
|
+
body: JSON.stringify({
|
|
266
|
+
children: [
|
|
267
|
+
{
|
|
268
|
+
object: 'block',
|
|
269
|
+
type: 'paragraph',
|
|
270
|
+
paragraph: {
|
|
271
|
+
rich_text: [{ type: 'text', text: { content: params.content } }],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
}),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
if (!response.ok) {
|
|
279
|
+
const errorData = (await response.json()) as { message?: string };
|
|
280
|
+
throw new Error(errorData.message || `Notion API error: ${response.status}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
data: {
|
|
285
|
+
pageId: params.page_id,
|
|
286
|
+
message: 'Content added successfully',
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
} catch (error) {
|
|
290
|
+
return {
|
|
291
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Notion toolkit
|
|
299
|
+
*/
|
|
300
|
+
export class NotionToolkit {
|
|
301
|
+
static create(options?: {
|
|
302
|
+
token?: string;
|
|
303
|
+
databaseId?: string;
|
|
304
|
+
enableCreatePage?: boolean;
|
|
305
|
+
enableSearch?: boolean;
|
|
306
|
+
enableUpdatePage?: boolean;
|
|
307
|
+
}): Array<NotionCreatePageTool | NotionSearchTool | NotionUpdatePageTool> {
|
|
308
|
+
const tools: Array<NotionCreatePageTool | NotionSearchTool | NotionUpdatePageTool> = [];
|
|
309
|
+
|
|
310
|
+
if (options?.enableCreatePage !== false) {
|
|
311
|
+
tools.push(new NotionCreatePageTool({ token: options?.token, databaseId: options?.databaseId }));
|
|
312
|
+
}
|
|
313
|
+
if (options?.enableSearch !== false) {
|
|
314
|
+
tools.push(new NotionSearchTool({ token: options?.token }));
|
|
315
|
+
}
|
|
316
|
+
if (options?.enableUpdatePage !== false) {
|
|
317
|
+
tools.push(new NotionUpdatePageTool({ token: options?.token }));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return tools;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI tool implementation - TypeScript OpenAITools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { BaseTool, BaseToolConfig } from './base-tool.js';
|
|
7
|
+
import { ToolContext, ToolCategory } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* OpenAI API types
|
|
11
|
+
*/
|
|
12
|
+
interface OpenAIImageResponse {
|
|
13
|
+
data: Array<{
|
|
14
|
+
url?: string;
|
|
15
|
+
b64_json?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface OpenAITranscriptionResponse {
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface OpenAIResult {
|
|
24
|
+
data?: unknown;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Base OpenAI tool with common authentication
|
|
30
|
+
*/
|
|
31
|
+
abstract class BaseOpenAITool<TParams extends z.ZodObject<Record<string, z.ZodType>>> extends BaseTool<TParams, OpenAIResult> {
|
|
32
|
+
protected apiKey: string;
|
|
33
|
+
protected baseUrl = 'https://api.openai.com/v1';
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
config: Partial<Omit<BaseToolConfig<TParams>, 'parameters'>> & {
|
|
37
|
+
apiKey?: string;
|
|
38
|
+
},
|
|
39
|
+
params: TParams
|
|
40
|
+
) {
|
|
41
|
+
super({
|
|
42
|
+
name: config.name || 'openai.tool',
|
|
43
|
+
description: config.description || 'OpenAI tool',
|
|
44
|
+
parameters: params,
|
|
45
|
+
category: config.category || ToolCategory.AI,
|
|
46
|
+
permissions: {
|
|
47
|
+
allowNetwork: true,
|
|
48
|
+
maxExecutionTimeMs: 60000,
|
|
49
|
+
...config.permissions,
|
|
50
|
+
},
|
|
51
|
+
...config,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.apiKey = config.apiKey || process.env.OPENAI_API_KEY || '';
|
|
55
|
+
|
|
56
|
+
if (!this.apiKey) {
|
|
57
|
+
throw new Error('OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass apiKey in config.');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected async openAIRequest(endpoint: string, options: RequestInit = {}): Promise<Response> {
|
|
62
|
+
return fetch(`${this.baseUrl}${endpoint}`, {
|
|
63
|
+
...options,
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
66
|
+
...(options.headers || {}),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate image tool
|
|
74
|
+
*/
|
|
75
|
+
const OpenAIGenerateImageParameters = z.object({
|
|
76
|
+
prompt: z.string().describe('Text description of the image to generate'),
|
|
77
|
+
size: z.enum(['256x256', '512x512', '1024x1024', '1792x1024', '1024x1792']).optional().default('1024x1024'),
|
|
78
|
+
quality: z.enum(['standard', 'hd']).optional().default('standard'),
|
|
79
|
+
style: z.enum(['vivid', 'natural']).optional().default('vivid'),
|
|
80
|
+
model: z.enum(['dall-e-2', 'dall-e-3', 'gpt-image-1']).optional().default('dall-e-3'),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export class OpenAIGenerateImageTool extends BaseOpenAITool<typeof OpenAIGenerateImageParameters> {
|
|
84
|
+
constructor(
|
|
85
|
+
config?: Partial<Omit<BaseToolConfig<typeof OpenAIGenerateImageParameters>, 'parameters'>> & {
|
|
86
|
+
apiKey?: string;
|
|
87
|
+
}
|
|
88
|
+
) {
|
|
89
|
+
super(
|
|
90
|
+
{
|
|
91
|
+
name: config?.name ?? 'openai.generate_image',
|
|
92
|
+
description: config?.description ?? 'Generate an image using OpenAI DALL-E',
|
|
93
|
+
...config,
|
|
94
|
+
},
|
|
95
|
+
OpenAIGenerateImageParameters
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected async performExecute(
|
|
100
|
+
params: z.infer<typeof OpenAIGenerateImageParameters>,
|
|
101
|
+
_context: ToolContext
|
|
102
|
+
): Promise<OpenAIResult> {
|
|
103
|
+
try {
|
|
104
|
+
const body: Record<string, unknown> = {
|
|
105
|
+
model: params.model,
|
|
106
|
+
prompt: params.prompt,
|
|
107
|
+
n: 1,
|
|
108
|
+
size: params.size,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (params.model === 'dall-e-3') {
|
|
112
|
+
body.quality = params.quality;
|
|
113
|
+
body.style = params.style;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const response = await this.openAIRequest('/images/generations', {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify(body),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
const errorData = (await response.json()) as { error?: { message?: string } };
|
|
126
|
+
throw new Error(errorData.error?.message || `OpenAI API error: ${response.status}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const data = (await response.json()) as OpenAIImageResponse;
|
|
130
|
+
const imageData = data.data[0];
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
data: {
|
|
134
|
+
url: imageData.url,
|
|
135
|
+
b64_json: imageData.b64_json,
|
|
136
|
+
prompt: params.prompt,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return {
|
|
141
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Transcribe audio tool
|
|
149
|
+
*/
|
|
150
|
+
const OpenAITranscribeAudioParameters = z.object({
|
|
151
|
+
audio_url: z.string().describe('URL or path to the audio file'),
|
|
152
|
+
model: z.enum(['whisper-1']).optional().default('whisper-1'),
|
|
153
|
+
language: z.string().optional().describe('Language code (e.g., en, es)'),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
export class OpenAITranscribeAudioTool extends BaseOpenAITool<typeof OpenAITranscribeAudioParameters> {
|
|
157
|
+
constructor(
|
|
158
|
+
config?: Partial<Omit<BaseToolConfig<typeof OpenAITranscribeAudioParameters>, 'parameters'>> & {
|
|
159
|
+
apiKey?: string;
|
|
160
|
+
}
|
|
161
|
+
) {
|
|
162
|
+
super(
|
|
163
|
+
{
|
|
164
|
+
name: config?.name ?? 'openai.transcribe_audio',
|
|
165
|
+
description: config?.description ?? 'Transcribe audio using OpenAI Whisper',
|
|
166
|
+
...config,
|
|
167
|
+
},
|
|
168
|
+
OpenAITranscribeAudioParameters
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected async performExecute(
|
|
173
|
+
params: z.infer<typeof OpenAITranscribeAudioParameters>,
|
|
174
|
+
_context: ToolContext
|
|
175
|
+
): Promise<OpenAIResult> {
|
|
176
|
+
try {
|
|
177
|
+
// For URL-based audio, we need to fetch it first
|
|
178
|
+
const audioResponse = await fetch(params.audio_url);
|
|
179
|
+
if (!audioResponse.ok) {
|
|
180
|
+
throw new Error(`Failed to fetch audio: ${audioResponse.status}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const audioBlob = await audioResponse.blob();
|
|
184
|
+
const formData = new FormData();
|
|
185
|
+
formData.append('file', audioBlob, 'audio.mp3');
|
|
186
|
+
formData.append('model', params.model);
|
|
187
|
+
if (params.language) {
|
|
188
|
+
formData.append('language', params.language);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const response = await this.openAIRequest('/audio/transcriptions', {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body: formData,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
const errorData = (await response.json()) as { error?: { message?: string } };
|
|
198
|
+
throw new Error(errorData.error?.message || `OpenAI API error: ${response.status}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const data = (await response.json()) as OpenAITranscriptionResponse;
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
data: {
|
|
205
|
+
text: data.text,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return {
|
|
210
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* OpenAI toolkit
|
|
218
|
+
*/
|
|
219
|
+
export class OpenAIToolkit {
|
|
220
|
+
static create(options?: {
|
|
221
|
+
apiKey?: string;
|
|
222
|
+
enableImageGeneration?: boolean;
|
|
223
|
+
enableTranscription?: boolean;
|
|
224
|
+
}): Array<OpenAIGenerateImageTool | OpenAITranscribeAudioTool> {
|
|
225
|
+
const tools: Array<OpenAIGenerateImageTool | OpenAITranscribeAudioTool> = [];
|
|
226
|
+
|
|
227
|
+
if (options?.enableImageGeneration !== false) {
|
|
228
|
+
tools.push(new OpenAIGenerateImageTool({ apiKey: options?.apiKey }));
|
|
229
|
+
}
|
|
230
|
+
if (options?.enableTranscription !== false) {
|
|
231
|
+
tools.push(new OpenAITranscribeAudioTool({ apiKey: options?.apiKey }));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return tools;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry implementation and tool provider helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ToolRegistry, Tool, ToolCategory } from './types.js';
|
|
6
|
+
import type { EntityId } from '../core/types.js';
|
|
7
|
+
|
|
8
|
+
/** Pass an array or registry when configuring agents. */
|
|
9
|
+
export type ToolProvider = Tool[] | ToolRegistry;
|
|
10
|
+
|
|
11
|
+
/** Normalize tools to a ToolRegistry (extensible: plug any tools). */
|
|
12
|
+
export function toToolRegistry(tools: ToolProvider): ToolRegistry {
|
|
13
|
+
if (Array.isArray(tools)) {
|
|
14
|
+
const reg = new ToolRegistryImpl();
|
|
15
|
+
for (const t of tools) reg.register(t);
|
|
16
|
+
return reg;
|
|
17
|
+
}
|
|
18
|
+
return tools;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Implementation of ToolRegistry
|
|
23
|
+
*/
|
|
24
|
+
export class ToolRegistryImpl implements ToolRegistry {
|
|
25
|
+
private tools: Map<EntityId, Tool> = new Map();
|
|
26
|
+
private nameIndex: Map<string, EntityId> = new Map();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register a tool
|
|
30
|
+
*/
|
|
31
|
+
register(tool: Tool): void {
|
|
32
|
+
if (this.tools.has(tool.id)) {
|
|
33
|
+
throw new Error(`Tool with ID ${tool.id} is already registered`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (this.nameIndex.has(tool.name)) {
|
|
37
|
+
throw new Error(`Tool with name ${tool.name} is already registered`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.tools.set(tool.id, tool);
|
|
41
|
+
this.nameIndex.set(tool.name, tool.id);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Unregister a tool by ID
|
|
46
|
+
*/
|
|
47
|
+
unregister(toolId: EntityId): boolean {
|
|
48
|
+
const tool = this.tools.get(toolId);
|
|
49
|
+
if (!tool) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.tools.delete(toolId);
|
|
54
|
+
this.nameIndex.delete(tool.name);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get a tool by ID
|
|
60
|
+
*/
|
|
61
|
+
get(toolId: EntityId): Tool | undefined {
|
|
62
|
+
return this.tools.get(toolId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get a tool by name
|
|
67
|
+
*/
|
|
68
|
+
getByName(name: string): Tool | undefined {
|
|
69
|
+
const id = this.nameIndex.get(name);
|
|
70
|
+
if (!id) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return this.tools.get(id);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* List all registered tools
|
|
78
|
+
*/
|
|
79
|
+
list(): Tool[] {
|
|
80
|
+
return Array.from(this.tools.values());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* List tools by category
|
|
85
|
+
*/
|
|
86
|
+
listByCategory(category: ToolCategory): Tool[] {
|
|
87
|
+
return this.list().filter(tool => tool.category === category);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Search tools by name or description
|
|
92
|
+
*/
|
|
93
|
+
search(query: string): Tool[] {
|
|
94
|
+
const lowerQuery = query.toLowerCase();
|
|
95
|
+
return this.list().filter(
|
|
96
|
+
tool =>
|
|
97
|
+
tool.name.toLowerCase().includes(lowerQuery) ||
|
|
98
|
+
tool.description.toLowerCase().includes(lowerQuery) ||
|
|
99
|
+
(tool.tags?.some(tag => tag.toLowerCase().includes(lowerQuery)) ?? false)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if a tool is registered
|
|
105
|
+
*/
|
|
106
|
+
has(toolId: EntityId): boolean {
|
|
107
|
+
return this.tools.has(toolId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Check if a tool name is registered
|
|
112
|
+
*/
|
|
113
|
+
hasName(name: string): boolean {
|
|
114
|
+
return this.nameIndex.has(name);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Clear all registered tools
|
|
119
|
+
*/
|
|
120
|
+
clear(): void {
|
|
121
|
+
this.tools.clear();
|
|
122
|
+
this.nameIndex.clear();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the number of registered tools
|
|
127
|
+
*/
|
|
128
|
+
size(): number {
|
|
129
|
+
return this.tools.size;
|
|
130
|
+
}
|
|
131
|
+
}
|