autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
100 lines (99 loc) • 3.97 kB
JavaScript
/**
* DynamicComposer — 运行时动态工具组合
*
* 将已有的原子工具按 sequential / parallel / conditional 策略组合成复合工具。
* 组合结果注册为临时工具(通过 TemporaryToolRegistry)。
*
* 与 PipelineStrategy 的区别:
* - PipelineStrategy 是 Agent 执行策略(Agent 层)
* - DynamicComposer 是工具组合(Tool 层),产出物是单个工具
*/
import Logger from '#infra/logging/Logger.js';
/* ────────────────────── Class ────────────────────── */
export class DynamicComposer {
constructor(registry) {
this.
}
/**
* 验证组合 spec 的可行性(所有工具是否存在)
*/
validate(spec) {
const missing = [];
for (const step of spec.steps) {
if (!this.
missing.push(step.tool);
}
}
return { valid: missing.length === 0, missingTools: missing };
}
/**
* 构建组合工具
*/
compose(spec) {
// 验证
const { valid, missingTools } = this.validate(spec);
if (!valid) {
return {
success: false,
error: `Missing tools: ${missingTools.join(', ')}`,
};
}
if (spec.steps.length === 0) {
return {
success: false,
error: 'Composition must have at least one step',
};
}
const registry = this.
const logger = this.
// 根据策略构建 handler
const handler = spec.mergeStrategy === 'parallel'
? this.
: this.
return { success: true, handler };
}
/* ── Internal ── */
return async (params, context) => {
let prevResult = params;
for (const step of spec.steps) {
const args = typeof step.args === 'function' ? step.args(prevResult) : { ...step.args, ...params };
logger.debug(`DynamicComposer [${spec.name}]: executing step "${step.tool}"`);
const result = await registry.execute(step.tool, args, context);
if (step.extractKey && typeof result === 'object' && result !== null) {
prevResult = result[step.extractKey];
}
else {
prevResult = result;
}
}
return prevResult;
};
}
return async (params, context) => {
logger.debug(`DynamicComposer [${spec.name}]: executing ${spec.steps.length} steps in parallel`);
const promises = spec.steps.map(async (step) => {
const args = typeof step.args === 'function' ? step.args(params) : { ...step.args, ...params };
const result = await registry.execute(step.tool, args, context);
if (step.extractKey && typeof result === 'object' && result !== null) {
return { tool: step.tool, result: result[step.extractKey] };
}
return { tool: step.tool, result };
});
const results = await Promise.allSettled(promises);
const merged = {};
for (const r of results) {
if (r.status === 'fulfilled') {
merged[r.value.tool] = r.value.result;
}
else {
merged[`error_${Object.keys(merged).length}`] = r.reason?.message ?? 'Unknown error';
}
}
return merged;
};
}
}