68 lines
3.1 KiB
TypeScript
68 lines
3.1 KiB
TypeScript
import { DynamicStructuredTool } from '@langchain/core/tools';
|
|
import { z } from 'zod';
|
|
import type { FastifyBaseLogger } from 'fastify';
|
|
import type { WebExploreSubagent } from '../../harness/subagents/web-explore/index.js';
|
|
import type { SubagentContext } from '../../harness/subagents/base-subagent.js';
|
|
import type { HarnessEvent } from '../../harness/harness-events.js';
|
|
|
|
export interface WebExploreAgentToolConfig {
|
|
webExploreSubagent: WebExploreSubagent;
|
|
context: SubagentContext;
|
|
logger: FastifyBaseLogger;
|
|
}
|
|
|
|
/**
|
|
* Creates a LangChain tool that delegates to the web-explore subagent.
|
|
* The subagent decides whether to use web search or arXiv based on the instruction.
|
|
*/
|
|
export function createWebExploreAgentTool(config: WebExploreAgentToolConfig): DynamicStructuredTool & { streamFunc: (args: { instruction: string }, signal?: AbortSignal) => AsyncGenerator<HarnessEvent, string> } {
|
|
const { webExploreSubagent, context, logger } = config;
|
|
|
|
async function* streamFunc({ instruction }: { instruction: string }, signal?: AbortSignal): AsyncGenerator<HarnessEvent, string> {
|
|
logger.info({ instruction: instruction.substring(0, 100) }, 'Streaming web-explore subagent');
|
|
const gen = webExploreSubagent.streamEvents(context, instruction, signal);
|
|
let step: IteratorResult<HarnessEvent, string>;
|
|
while (!(step = await gen.next()).done) {
|
|
yield step.value;
|
|
}
|
|
return step.value;
|
|
}
|
|
|
|
const tool = new DynamicStructuredTool({
|
|
name: 'web_explore',
|
|
description: `Search the EXTERNAL web or academic databases and return a summarized answer.
|
|
|
|
Use this tool ONLY for external, public information:
|
|
- Current events, news, or real-time information
|
|
- External documentation, tutorials, or how-to guides for third-party libraries/tools
|
|
- Academic papers, research findings, or scientific topics
|
|
- Any topic requiring external sources
|
|
|
|
NEVER use this tool for:
|
|
- Questions about the Dexorder platform itself (workspace tools, chartState, indicators, strategies)
|
|
- Internal API usage (workspace_patch, workspace_read, etc.) — consult the system prompt instead
|
|
- Anything that can be answered from the context already available
|
|
|
|
The subagent will search the web (or arXiv for academic queries), fetch relevant content, and return a markdown summary with cited sources.`,
|
|
schema: z.object({
|
|
instruction: z.string().describe(
|
|
'What to search for and summarize. Be specific — include the topic, what aspects matter, ' +
|
|
'and any context that helps narrow the search (e.g. "recent papers on momentum factor in equities" ' +
|
|
'or "how to configure rate limiting in Fastify").'
|
|
),
|
|
}),
|
|
func: async ({ instruction }: { instruction: string }): Promise<string> => {
|
|
logger.info({ instruction: instruction.substring(0, 100) }, 'Delegating to web-explore subagent');
|
|
|
|
try {
|
|
return await webExploreSubagent.execute(context, instruction);
|
|
} catch (error) {
|
|
logger.error({ error, errorMessage: (error as Error)?.message }, 'Web explore subagent failed');
|
|
throw error;
|
|
}
|
|
},
|
|
});
|
|
|
|
return Object.assign(tool, { streamFunc });
|
|
}
|