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,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram tool implementation - TypeScript TelegramTools
|
|
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
|
+
* Telegram API response
|
|
11
|
+
*/
|
|
12
|
+
interface TelegramResponse {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
result?: unknown;
|
|
15
|
+
description?: string;
|
|
16
|
+
error_code?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface TelegramResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
message?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
response?: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parameters for sending a Telegram message
|
|
28
|
+
*/
|
|
29
|
+
const TelegramSendMessageParameters = z.object({
|
|
30
|
+
message: z.string().describe('The message to send'),
|
|
31
|
+
chat_id: z.string().optional().describe('Chat ID to send message to (overrides default)'),
|
|
32
|
+
parse_mode: z.enum(['HTML', 'Markdown', 'MarkdownV2']).optional().describe('Parse mode for the message'),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Telegram tool for sending messages
|
|
37
|
+
*/
|
|
38
|
+
export class TelegramTool extends BaseTool<typeof TelegramSendMessageParameters, TelegramResult> {
|
|
39
|
+
private token: string;
|
|
40
|
+
private defaultChatId: string;
|
|
41
|
+
private baseUrl = 'https://api.telegram.org';
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
config?: Partial<Omit<BaseToolConfig<typeof TelegramSendMessageParameters>, 'parameters'>> & {
|
|
45
|
+
token?: string;
|
|
46
|
+
chatId?: string;
|
|
47
|
+
}
|
|
48
|
+
) {
|
|
49
|
+
super({
|
|
50
|
+
name: config?.name ?? 'telegram.send_message',
|
|
51
|
+
description: config?.description ?? 'Send a message to a Telegram chat',
|
|
52
|
+
parameters: TelegramSendMessageParameters,
|
|
53
|
+
category: config?.category ?? ToolCategory.API,
|
|
54
|
+
permissions: {
|
|
55
|
+
allowNetwork: true,
|
|
56
|
+
maxExecutionTimeMs: 30000,
|
|
57
|
+
...config?.permissions,
|
|
58
|
+
},
|
|
59
|
+
...config,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
this.token = config?.token || process.env.TELEGRAM_TOKEN || '';
|
|
63
|
+
this.defaultChatId = config?.chatId || process.env.TELEGRAM_CHAT_ID || '';
|
|
64
|
+
|
|
65
|
+
if (!this.token) {
|
|
66
|
+
throw new Error('Telegram token is required. Set TELEGRAM_TOKEN environment variable or pass token in config.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
protected async performExecute(
|
|
71
|
+
params: z.infer<typeof TelegramSendMessageParameters>,
|
|
72
|
+
_context: ToolContext
|
|
73
|
+
): Promise<TelegramResult> {
|
|
74
|
+
const chatId = params.chat_id || this.defaultChatId;
|
|
75
|
+
|
|
76
|
+
if (!chatId) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: 'Chat ID is required. Set TELEGRAM_CHAT_ID environment variable, pass chatId in config, or provide chat_id in parameters.',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(`${this.baseUrl}/bot${this.token}/sendMessage`, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
},
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
chat_id: chatId,
|
|
91
|
+
text: params.message,
|
|
92
|
+
parse_mode: params.parse_mode,
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const data = (await response.json()) as TelegramResponse;
|
|
97
|
+
|
|
98
|
+
if (data.ok) {
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
message: 'Message sent successfully',
|
|
102
|
+
response: data.result,
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: data.description || 'Unknown error from Telegram API',
|
|
108
|
+
response: data,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Telegram toolkit
|
|
122
|
+
*/
|
|
123
|
+
export class TelegramToolkit {
|
|
124
|
+
static create(options: { token?: string; chatId?: string }): Array<TelegramTool> {
|
|
125
|
+
return [new TelegramTool({ token: options.token, chatId: options.chatId })];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool integration types and interfaces
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EntityId } from '../core/types.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tool parameter schema using Zod
|
|
10
|
+
*/
|
|
11
|
+
export type ToolParameters = z.ZodObject<Record<string, z.ZodType>>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Tool execution context
|
|
15
|
+
*/
|
|
16
|
+
export interface ToolContext {
|
|
17
|
+
readonly toolId: EntityId;
|
|
18
|
+
readonly agentId: EntityId;
|
|
19
|
+
readonly sessionId: string;
|
|
20
|
+
readonly timeoutMs?: number;
|
|
21
|
+
readonly permissions: ToolPermissions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Tool permissions
|
|
26
|
+
*/
|
|
27
|
+
export interface ToolPermissions {
|
|
28
|
+
readonly allowNetwork: boolean;
|
|
29
|
+
readonly allowFileSystem: boolean;
|
|
30
|
+
readonly allowedPaths?: string[];
|
|
31
|
+
readonly allowedHosts?: string[];
|
|
32
|
+
readonly maxExecutionTimeMs: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Tool execution result
|
|
37
|
+
*/
|
|
38
|
+
export interface ToolResult<T = unknown> {
|
|
39
|
+
readonly success: boolean;
|
|
40
|
+
readonly data?: T;
|
|
41
|
+
readonly error?: ToolError;
|
|
42
|
+
readonly executionTimeMs: number;
|
|
43
|
+
readonly metadata: ToolExecutionMetadata;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Tool error details
|
|
48
|
+
*/
|
|
49
|
+
export interface ToolError {
|
|
50
|
+
readonly code: string;
|
|
51
|
+
readonly message: string;
|
|
52
|
+
readonly details?: unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Tool execution metadata
|
|
57
|
+
*/
|
|
58
|
+
export interface ToolExecutionMetadata {
|
|
59
|
+
readonly startTime: Date;
|
|
60
|
+
readonly endTime: Date;
|
|
61
|
+
readonly retries: number;
|
|
62
|
+
readonly tokensUsed?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Tool definition
|
|
67
|
+
*/
|
|
68
|
+
export interface Tool<TParams extends ToolParameters = ToolParameters, TOutput = unknown> {
|
|
69
|
+
readonly id: EntityId;
|
|
70
|
+
readonly name: string;
|
|
71
|
+
readonly description: string;
|
|
72
|
+
readonly parameters: TParams;
|
|
73
|
+
readonly permissions: ToolPermissions;
|
|
74
|
+
readonly category: ToolCategory;
|
|
75
|
+
readonly version: string;
|
|
76
|
+
readonly author?: string;
|
|
77
|
+
readonly tags?: string[];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Execute the tool with validated parameters
|
|
81
|
+
*/
|
|
82
|
+
execute(params: z.infer<TParams>, context: ToolContext): Promise<ToolResult<TOutput>>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Validate parameters without executing
|
|
86
|
+
*/
|
|
87
|
+
validate(params: unknown): params is z.infer<TParams>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Tool categories
|
|
92
|
+
*/
|
|
93
|
+
export enum ToolCategory {
|
|
94
|
+
WEB = 'web',
|
|
95
|
+
DATABASE = 'database',
|
|
96
|
+
FILE_SYSTEM = 'file_system',
|
|
97
|
+
API = 'api',
|
|
98
|
+
UTILITY = 'utility',
|
|
99
|
+
AI = 'ai',
|
|
100
|
+
CUSTOM = 'custom',
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Tool registry for managing available tools
|
|
105
|
+
*/
|
|
106
|
+
export interface ToolRegistry {
|
|
107
|
+
/**
|
|
108
|
+
* Register a tool
|
|
109
|
+
*/
|
|
110
|
+
register(tool: Tool): void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Unregister a tool by ID
|
|
114
|
+
*/
|
|
115
|
+
unregister(toolId: EntityId): boolean;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get a tool by ID
|
|
119
|
+
*/
|
|
120
|
+
get(toolId: EntityId): Tool | undefined;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get a tool by name
|
|
124
|
+
*/
|
|
125
|
+
getByName(name: string): Tool | undefined;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* List all registered tools
|
|
129
|
+
*/
|
|
130
|
+
list(): Tool[];
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* List tools by category
|
|
134
|
+
*/
|
|
135
|
+
listByCategory(category: ToolCategory): Tool[];
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Search tools by name or description
|
|
139
|
+
*/
|
|
140
|
+
search(query: string): Tool[];
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if a tool is registered
|
|
144
|
+
*/
|
|
145
|
+
has(toolId: EntityId): boolean;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Clear all registered tools
|
|
149
|
+
*/
|
|
150
|
+
clear(): void;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Tool sandbox configuration
|
|
155
|
+
*/
|
|
156
|
+
export interface ToolSandboxConfig {
|
|
157
|
+
readonly enabled: boolean;
|
|
158
|
+
readonly timeoutMs: number;
|
|
159
|
+
readonly maxMemoryMb: number;
|
|
160
|
+
readonly allowedModules?: string[];
|
|
161
|
+
readonly blockedModules?: string[];
|
|
162
|
+
readonly environmentVariables?: Record<string, string>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Tool middleware for intercepting tool calls
|
|
167
|
+
*/
|
|
168
|
+
export interface ToolMiddleware {
|
|
169
|
+
/**
|
|
170
|
+
* Called before tool execution
|
|
171
|
+
*/
|
|
172
|
+
beforeExecute?: (tool: Tool, params: unknown, context: ToolContext) => Promise<void> | void;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Called after tool execution
|
|
176
|
+
*/
|
|
177
|
+
afterExecute?: (tool: Tool, result: ToolResult, context: ToolContext) => Promise<void> | void;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Called on tool execution error
|
|
181
|
+
*/
|
|
182
|
+
onError?: (tool: Tool, error: Error, context: ToolContext) => Promise<void> | void;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Tool factory for creating tool instances
|
|
187
|
+
*/
|
|
188
|
+
export interface ToolFactory {
|
|
189
|
+
/**
|
|
190
|
+
* Create a tool instance
|
|
191
|
+
*/
|
|
192
|
+
create(config: Record<string, unknown>): Tool;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get the tool schema
|
|
196
|
+
*/
|
|
197
|
+
getSchema(): ToolSchema;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Tool schema for documentation and validation
|
|
202
|
+
*/
|
|
203
|
+
export interface ToolSchema {
|
|
204
|
+
readonly name: string;
|
|
205
|
+
readonly description: string;
|
|
206
|
+
readonly parameters: Record<string, ParameterSchema>;
|
|
207
|
+
readonly returns: ParameterSchema;
|
|
208
|
+
readonly examples?: ToolExample[];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Parameter schema
|
|
213
|
+
*/
|
|
214
|
+
export interface ParameterSchema {
|
|
215
|
+
readonly type: string;
|
|
216
|
+
readonly description: string;
|
|
217
|
+
readonly required: boolean;
|
|
218
|
+
readonly default?: unknown;
|
|
219
|
+
readonly enum?: unknown[];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Tool usage example
|
|
224
|
+
*/
|
|
225
|
+
export interface ToolExample {
|
|
226
|
+
readonly description: string;
|
|
227
|
+
readonly parameters: Record<string, unknown>;
|
|
228
|
+
readonly expectedOutput?: unknown;
|
|
229
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web search tool implementation - TypeScript WebSearchTools and DuckDuckGoTools
|
|
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
|
+
* Search result item
|
|
11
|
+
*/
|
|
12
|
+
interface SearchResult {
|
|
13
|
+
title: string;
|
|
14
|
+
url: string;
|
|
15
|
+
snippet?: string;
|
|
16
|
+
source?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface WebSearchResponse {
|
|
20
|
+
query: string;
|
|
21
|
+
results: SearchResult[];
|
|
22
|
+
backend?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface NewsResult {
|
|
26
|
+
title: string;
|
|
27
|
+
url: string;
|
|
28
|
+
snippet?: string;
|
|
29
|
+
date?: string;
|
|
30
|
+
source?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface NewsSearchResponse {
|
|
34
|
+
query: string;
|
|
35
|
+
results: NewsResult[];
|
|
36
|
+
backend?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parameters for web search
|
|
41
|
+
*/
|
|
42
|
+
const WebSearchParameters = z.object({
|
|
43
|
+
query: z.string().describe('The search query'),
|
|
44
|
+
max_results: z.number().min(1).max(20).optional().default(5).describe('Maximum number of results to return'),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* DuckDuckGo search tool using DuckDuckGo's HTML interface
|
|
49
|
+
*/
|
|
50
|
+
export class DuckDuckGoSearchTool extends BaseTool<typeof WebSearchParameters, WebSearchResponse> {
|
|
51
|
+
private modifier?: string;
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
config?: Partial<Omit<BaseToolConfig<typeof WebSearchParameters>, 'parameters'>> & {
|
|
55
|
+
modifier?: string;
|
|
56
|
+
}
|
|
57
|
+
) {
|
|
58
|
+
super({
|
|
59
|
+
name: config?.name ?? 'duckduckgo.search',
|
|
60
|
+
description: config?.description ?? 'Search the web using DuckDuckGo',
|
|
61
|
+
parameters: WebSearchParameters,
|
|
62
|
+
category: config?.category ?? ToolCategory.WEB,
|
|
63
|
+
permissions: {
|
|
64
|
+
allowNetwork: true,
|
|
65
|
+
maxExecutionTimeMs: 30000,
|
|
66
|
+
...config?.permissions,
|
|
67
|
+
},
|
|
68
|
+
...config,
|
|
69
|
+
});
|
|
70
|
+
this.modifier = config?.modifier;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected async performExecute(
|
|
74
|
+
params: z.infer<typeof WebSearchParameters>,
|
|
75
|
+
_context: ToolContext
|
|
76
|
+
): Promise<WebSearchResponse> {
|
|
77
|
+
const searchQuery = this.modifier ? `${this.modifier} ${params.query}` : params.query;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Use DuckDuckGo's instant answer API
|
|
81
|
+
const response = await fetch(
|
|
82
|
+
`https://duckduckgo.com/html/?q=${encodeURIComponent(searchQuery)}`,
|
|
83
|
+
{
|
|
84
|
+
headers: {
|
|
85
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(`DuckDuckGo search failed: ${response.status}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const html = await response.text();
|
|
95
|
+
const results = this.parseResults(html, params.max_results);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
query: searchQuery,
|
|
99
|
+
results,
|
|
100
|
+
backend: 'duckduckgo',
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
// Fallback to returning empty results with error info
|
|
104
|
+
return {
|
|
105
|
+
query: searchQuery,
|
|
106
|
+
results: [],
|
|
107
|
+
backend: 'duckduckgo',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private parseResults(html: string, maxResults: number): SearchResult[] {
|
|
113
|
+
const results: SearchResult[] = [];
|
|
114
|
+
// Simple regex-based parsing for DuckDuckGo HTML results
|
|
115
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
116
|
+
|
|
117
|
+
let match;
|
|
118
|
+
const titles: string[] = [];
|
|
119
|
+
const urls: string[] = [];
|
|
120
|
+
|
|
121
|
+
while ((match = resultRegex.exec(html)) !== null && urls.length < maxResults) {
|
|
122
|
+
const url = match[1];
|
|
123
|
+
const title = match[2].replace(/<[^>]*>/g, ''); // Strip HTML tags
|
|
124
|
+
if (url && !url.startsWith('/')) {
|
|
125
|
+
urls.push(url);
|
|
126
|
+
titles.push(title);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < urls.length; i++) {
|
|
131
|
+
results.push({
|
|
132
|
+
title: titles[i] || 'Untitled',
|
|
133
|
+
url: urls[i],
|
|
134
|
+
source: 'DuckDuckGo',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return results;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* DuckDuckGo news search tool
|
|
144
|
+
*/
|
|
145
|
+
export class DuckDuckGoNewsTool extends BaseTool<typeof WebSearchParameters, NewsSearchResponse> {
|
|
146
|
+
constructor(config?: Partial<Omit<BaseToolConfig<typeof WebSearchParameters>, 'parameters'>>) {
|
|
147
|
+
super({
|
|
148
|
+
name: config?.name ?? 'duckduckgo.news',
|
|
149
|
+
description: config?.description ?? 'Search for news using DuckDuckGo',
|
|
150
|
+
parameters: WebSearchParameters,
|
|
151
|
+
category: config?.category ?? ToolCategory.WEB,
|
|
152
|
+
permissions: {
|
|
153
|
+
allowNetwork: true,
|
|
154
|
+
maxExecutionTimeMs: 30000,
|
|
155
|
+
...config?.permissions,
|
|
156
|
+
},
|
|
157
|
+
...config,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
protected async performExecute(
|
|
162
|
+
params: z.infer<typeof WebSearchParameters>,
|
|
163
|
+
_context: ToolContext
|
|
164
|
+
): Promise<NewsSearchResponse> {
|
|
165
|
+
try {
|
|
166
|
+
const response = await fetch(
|
|
167
|
+
`https://duckduckgo.com/html/?q=${encodeURIComponent(params.query + ' news')}`,
|
|
168
|
+
{
|
|
169
|
+
headers: {
|
|
170
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (!response.ok) {
|
|
176
|
+
throw new Error(`DuckDuckGo news search failed: ${response.status}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const html = await response.text();
|
|
180
|
+
const results = this.parseNewsResults(html, params.max_results);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
query: params.query,
|
|
184
|
+
results,
|
|
185
|
+
backend: 'duckduckgo',
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
query: params.query,
|
|
190
|
+
results: [],
|
|
191
|
+
backend: 'duckduckgo',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private parseNewsResults(html: string, maxResults: number): NewsResult[] {
|
|
197
|
+
const results: NewsResult[] = [];
|
|
198
|
+
// Similar parsing logic for news
|
|
199
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
200
|
+
|
|
201
|
+
let match;
|
|
202
|
+
while ((match = resultRegex.exec(html)) !== null && results.length < maxResults) {
|
|
203
|
+
const url = match[1];
|
|
204
|
+
const title = match[2].replace(/<[^>]*>/g, '');
|
|
205
|
+
if (url && !url.startsWith('/')) {
|
|
206
|
+
results.push({
|
|
207
|
+
title: title || 'Untitled',
|
|
208
|
+
url: url,
|
|
209
|
+
source: 'DuckDuckGo News',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Generic web search tool that can use different backends
|
|
220
|
+
*/
|
|
221
|
+
const GenericWebSearchParameters = z.object({
|
|
222
|
+
query: z.string().describe('The search query'),
|
|
223
|
+
max_results: z.number().min(1).max(20).optional().default(5).describe('Maximum number of results to return'),
|
|
224
|
+
backend: z.enum(['duckduckgo', 'bing', 'google']).optional().default('duckduckgo').describe('Search backend to use'),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export class WebSearchTool extends BaseTool<typeof GenericWebSearchParameters, WebSearchResponse> {
|
|
228
|
+
private modifier?: string;
|
|
229
|
+
|
|
230
|
+
constructor(
|
|
231
|
+
config?: Partial<Omit<BaseToolConfig<typeof GenericWebSearchParameters>, 'parameters'>> & {
|
|
232
|
+
modifier?: string;
|
|
233
|
+
}
|
|
234
|
+
) {
|
|
235
|
+
super({
|
|
236
|
+
name: config?.name ?? 'web.search',
|
|
237
|
+
description: config?.description ?? 'Search the web using various search engines',
|
|
238
|
+
parameters: GenericWebSearchParameters,
|
|
239
|
+
category: config?.category ?? ToolCategory.WEB,
|
|
240
|
+
permissions: {
|
|
241
|
+
allowNetwork: true,
|
|
242
|
+
maxExecutionTimeMs: 30000,
|
|
243
|
+
...config?.permissions,
|
|
244
|
+
},
|
|
245
|
+
...config,
|
|
246
|
+
});
|
|
247
|
+
this.modifier = config?.modifier;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
protected async performExecute(
|
|
251
|
+
params: z.infer<typeof GenericWebSearchParameters>,
|
|
252
|
+
_context: ToolContext
|
|
253
|
+
): Promise<WebSearchResponse> {
|
|
254
|
+
const searchQuery = this.modifier ? `${this.modifier} ${params.query}` : params.query;
|
|
255
|
+
|
|
256
|
+
// For now, only DuckDuckGo is implemented without API keys
|
|
257
|
+
// Other backends would require API keys
|
|
258
|
+
switch (params.backend) {
|
|
259
|
+
case 'duckduckgo':
|
|
260
|
+
default:
|
|
261
|
+
return this.searchDuckDuckGo(searchQuery, params.max_results);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private async searchDuckDuckGo(query: string, maxResults: number): Promise<WebSearchResponse> {
|
|
266
|
+
try {
|
|
267
|
+
const response = await fetch(
|
|
268
|
+
`https://duckduckgo.com/html/?q=${encodeURIComponent(query)}`,
|
|
269
|
+
{
|
|
270
|
+
headers: {
|
|
271
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (!response.ok) {
|
|
277
|
+
throw new Error(`Search failed: ${response.status}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const html = await response.text();
|
|
281
|
+
const results = this.parseResults(html, maxResults);
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
query,
|
|
285
|
+
results,
|
|
286
|
+
backend: 'duckduckgo',
|
|
287
|
+
};
|
|
288
|
+
} catch (error) {
|
|
289
|
+
return {
|
|
290
|
+
query,
|
|
291
|
+
results: [],
|
|
292
|
+
backend: 'duckduckgo',
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private parseResults(html: string, maxResults: number): SearchResult[] {
|
|
298
|
+
const results: SearchResult[] = [];
|
|
299
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
300
|
+
|
|
301
|
+
let match;
|
|
302
|
+
while ((match = resultRegex.exec(html)) !== null && results.length < maxResults) {
|
|
303
|
+
const url = match[1];
|
|
304
|
+
const title = match[2].replace(/<[^>]*>/g, '');
|
|
305
|
+
if (url && !url.startsWith('/')) {
|
|
306
|
+
results.push({
|
|
307
|
+
title: title || 'Untitled',
|
|
308
|
+
url: url,
|
|
309
|
+
source: 'DuckDuckGo',
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return results;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Web search toolkit
|
|
320
|
+
*/
|
|
321
|
+
export class WebSearchToolkit {
|
|
322
|
+
static createDuckDuckGo(options?: { modifier?: string; enableNews?: boolean }): Array<DuckDuckGoSearchTool | DuckDuckGoNewsTool> {
|
|
323
|
+
const tools: Array<DuckDuckGoSearchTool | DuckDuckGoNewsTool> = [
|
|
324
|
+
new DuckDuckGoSearchTool({ modifier: options?.modifier }),
|
|
325
|
+
];
|
|
326
|
+
if (options?.enableNews !== false) {
|
|
327
|
+
tools.push(new DuckDuckGoNewsTool());
|
|
328
|
+
}
|
|
329
|
+
return tools;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
static createGeneric(options?: { modifier?: string }): Array<WebSearchTool> {
|
|
333
|
+
return [new WebSearchTool({ modifier: options?.modifier })];
|
|
334
|
+
}
|
|
335
|
+
}
|