@winspan/claude-forge 0.7.3 → 0.7.5
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/dist/ai-gateway/index.d.ts +3 -0
- package/dist/ai-gateway/index.d.ts.map +1 -1
- package/dist/ai-gateway/index.js +41 -11
- package/dist/ai-gateway/index.js.map +1 -1
- package/dist/ai-gateway/model-selector.d.ts +12 -0
- package/dist/ai-gateway/model-selector.d.ts.map +1 -1
- package/dist/ai-gateway/model-selector.js +20 -0
- package/dist/ai-gateway/model-selector.js.map +1 -1
- package/dist/ai-gateway/rate-limiter.d.ts +17 -4
- package/dist/ai-gateway/rate-limiter.d.ts.map +1 -1
- package/dist/ai-gateway/rate-limiter.js +60 -22
- package/dist/ai-gateway/rate-limiter.js.map +1 -1
- package/dist/ai-gateway/response-cache.d.ts +8 -3
- package/dist/ai-gateway/response-cache.d.ts.map +1 -1
- package/dist/ai-gateway/response-cache.js +37 -5
- package/dist/ai-gateway/response-cache.js.map +1 -1
- package/dist/autopilot/task-completion-detector.d.ts +32 -0
- package/dist/autopilot/task-completion-detector.d.ts.map +1 -0
- package/dist/autopilot/task-completion-detector.js +103 -0
- package/dist/autopilot/task-completion-detector.js.map +1 -0
- package/dist/daemon/handlers/context-builder.d.ts +8 -7
- package/dist/daemon/handlers/context-builder.d.ts.map +1 -1
- package/dist/daemon/handlers/context-builder.js +16 -20
- package/dist/daemon/handlers/context-builder.js.map +1 -1
- package/dist/daemon/handlers/orchestration-context.d.ts +6 -9
- package/dist/daemon/handlers/orchestration-context.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/01-failure-signal.d.ts +1 -0
- package/dist/daemon/handlers/stages/01-failure-signal.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/01-failure-signal.js +2 -2
- package/dist/daemon/handlers/stages/01-failure-signal.js.map +1 -1
- package/dist/daemon/handlers/stages/02-active-intervention.d.ts +1 -0
- package/dist/daemon/handlers/stages/02-active-intervention.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/02-active-intervention.js +1 -0
- package/dist/daemon/handlers/stages/02-active-intervention.js.map +1 -1
- package/dist/daemon/handlers/stages/03-init-prompt.d.ts +1 -0
- package/dist/daemon/handlers/stages/03-init-prompt.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/03-init-prompt.js +2 -2
- package/dist/daemon/handlers/stages/03-init-prompt.js.map +1 -1
- package/dist/daemon/handlers/stages/04-skill-suggestions.d.ts +1 -0
- package/dist/daemon/handlers/stages/04-skill-suggestions.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/04-skill-suggestions.js +2 -1
- package/dist/daemon/handlers/stages/04-skill-suggestions.js.map +1 -1
- package/dist/daemon/handlers/stages/05-conv-config.d.ts +1 -0
- package/dist/daemon/handlers/stages/05-conv-config.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/05-conv-config.js +1 -0
- package/dist/daemon/handlers/stages/05-conv-config.js.map +1 -1
- package/dist/daemon/handlers/stages/06-engine-check.d.ts +1 -0
- package/dist/daemon/handlers/stages/06-engine-check.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/06-engine-check.js +1 -0
- package/dist/daemon/handlers/stages/06-engine-check.js.map +1 -1
- package/dist/daemon/handlers/stages/07-pipeline-reply.d.ts +1 -0
- package/dist/daemon/handlers/stages/07-pipeline-reply.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/07-pipeline-reply.js +1 -2
- package/dist/daemon/handlers/stages/07-pipeline-reply.js.map +1 -1
- package/dist/daemon/handlers/stages/08-esc-interrupt.d.ts +1 -0
- package/dist/daemon/handlers/stages/08-esc-interrupt.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/08-esc-interrupt.js +1 -0
- package/dist/daemon/handlers/stages/08-esc-interrupt.js.map +1 -1
- package/dist/daemon/handlers/stages/09-pipeline-active.d.ts +1 -0
- package/dist/daemon/handlers/stages/09-pipeline-active.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/09-pipeline-active.js +1 -0
- package/dist/daemon/handlers/stages/09-pipeline-active.js.map +1 -1
- package/dist/daemon/handlers/stages/10-cooldown.d.ts +1 -1
- package/dist/daemon/handlers/stages/10-cooldown.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/10-cooldown.js +1 -39
- package/dist/daemon/handlers/stages/10-cooldown.js.map +1 -1
- package/dist/daemon/handlers/stages/11-intent-analysis.d.ts +1 -0
- package/dist/daemon/handlers/stages/11-intent-analysis.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/11-intent-analysis.js +4 -3
- package/dist/daemon/handlers/stages/11-intent-analysis.js.map +1 -1
- package/dist/daemon/handlers/stages/12-strategy-advice.d.ts +1 -0
- package/dist/daemon/handlers/stages/12-strategy-advice.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/12-strategy-advice.js +1 -0
- package/dist/daemon/handlers/stages/12-strategy-advice.js.map +1 -1
- package/dist/daemon/handlers/stages/13-template-route.d.ts +1 -0
- package/dist/daemon/handlers/stages/13-template-route.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/13-template-route.js +1 -0
- package/dist/daemon/handlers/stages/13-template-route.js.map +1 -1
- package/dist/daemon/handlers/stages/14-plan-resume.d.ts +1 -0
- package/dist/daemon/handlers/stages/14-plan-resume.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/14-plan-resume.js +1 -0
- package/dist/daemon/handlers/stages/14-plan-resume.js.map +1 -1
- package/dist/daemon/handlers/stages/15-plan-enforcement.d.ts +1 -0
- package/dist/daemon/handlers/stages/15-plan-enforcement.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/15-plan-enforcement.js +1 -0
- package/dist/daemon/handlers/stages/15-plan-enforcement.js.map +1 -1
- package/dist/daemon/handlers/stages/16-intervention-level.d.ts +1 -0
- package/dist/daemon/handlers/stages/16-intervention-level.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/16-intervention-level.js +1 -0
- package/dist/daemon/handlers/stages/16-intervention-level.js.map +1 -1
- package/dist/daemon/handlers/stages/17-simple-task.d.ts +1 -0
- package/dist/daemon/handlers/stages/17-simple-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/17-simple-task.js +2 -1
- package/dist/daemon/handlers/stages/17-simple-task.js.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.d.ts +1 -0
- package/dist/daemon/handlers/stages/18-complex-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.js +4 -4
- package/dist/daemon/handlers/stages/18-complex-task.js.map +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.d.ts +1 -0
- package/dist/daemon/handlers/stages/19-moderate-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.js +3 -2
- package/dist/daemon/handlers/stages/19-moderate-task.js.map +1 -1
- package/dist/daemon/handlers/stages/stage-interface.d.ts +3 -0
- package/dist/daemon/handlers/stages/stage-interface.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/stage-scheduler.d.ts +27 -0
- package/dist/daemon/handlers/stages/stage-scheduler.d.ts.map +1 -0
- package/dist/daemon/handlers/stages/stage-scheduler.js +105 -0
- package/dist/daemon/handlers/stages/stage-scheduler.js.map +1 -0
- package/dist/daemon/handlers/user-prompt-handler.d.ts +3 -12
- package/dist/daemon/handlers/user-prompt-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt-handler.js +73 -141
- package/dist/daemon/handlers/user-prompt-handler.js.map +1 -1
- package/dist/pipeline/analyzer.d.ts +2 -0
- package/dist/pipeline/analyzer.d.ts.map +1 -1
- package/dist/pipeline/analyzer.js +16 -2
- package/dist/pipeline/analyzer.js.map +1 -1
- package/dist/pipeline/index.d.ts +4 -5
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +49 -28
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/phase-manager.d.ts +26 -1
- package/dist/pipeline/phase-manager.d.ts.map +1 -1
- package/dist/pipeline/phase-manager.js +84 -4
- package/dist/pipeline/phase-manager.js.map +1 -1
- package/dist/pipeline/store.js +3 -3
- package/dist/pipeline/store.js.map +1 -1
- package/dist/utils/claude-api.d.ts.map +1 -1
- package/dist/utils/claude-api.js +4 -2
- package/dist/utils/claude-api.js.map +1 -1
- package/package.json +1 -1
|
@@ -25,6 +25,9 @@ export declare class AIGateway implements AIProvider {
|
|
|
25
25
|
private noCacheLabels;
|
|
26
26
|
private modelSelector;
|
|
27
27
|
private cacheTtlMs;
|
|
28
|
+
private maxConcurrent;
|
|
29
|
+
private degradationCount;
|
|
30
|
+
private totalCompletions;
|
|
28
31
|
constructor(inner: AIProvider, opts?: GatewayOptions);
|
|
29
32
|
complete(prompt: string, label?: string, options?: CompletionOptions): Promise<string>;
|
|
30
33
|
getStats(): Readonly<import("../ai-provider/types.js").ApiCallStats>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAO7E,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,kCAAkC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B;IAC1B,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAOD;;;GAGG;AACH,qBAAa,SAAU,YAAW,UAAU;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAO7E,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,kCAAkC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B;IAC1B,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAOD;;;GAGG;AACH,qBAAa,SAAU,YAAW,UAAU;IAU9B,OAAO,CAAC,KAAK;IATzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,gBAAgB,CAAK;gBAET,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,cAAmB;IAY1D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAoE5F,QAAQ;IAIR,WAAW,IAAI,MAAM;CAStB"}
|
package/dist/ai-gateway/index.js
CHANGED
|
@@ -18,9 +18,13 @@ export class AIGateway {
|
|
|
18
18
|
noCacheLabels;
|
|
19
19
|
modelSelector;
|
|
20
20
|
cacheTtlMs;
|
|
21
|
+
maxConcurrent;
|
|
22
|
+
degradationCount = 0;
|
|
23
|
+
totalCompletions = 0;
|
|
21
24
|
constructor(inner, opts = {}) {
|
|
22
25
|
this.inner = inner;
|
|
23
|
-
this.
|
|
26
|
+
this.maxConcurrent = opts.maxConcurrent ?? 3;
|
|
27
|
+
this.rateLimiter = new RateLimiter(this.maxConcurrent, opts.minIntervalMs ?? 200);
|
|
24
28
|
this.cache = new ResponseCache();
|
|
25
29
|
this.cacheTtlMs = opts.cacheTtlMs ?? 300_000;
|
|
26
30
|
this.noCacheLabels = new Set([
|
|
@@ -32,7 +36,7 @@ export class AIGateway {
|
|
|
32
36
|
async complete(prompt, label, options) {
|
|
33
37
|
// 可缓存的调用先查缓存
|
|
34
38
|
const cacheable = label ? !this.noCacheLabels.has(label) : false;
|
|
35
|
-
//
|
|
39
|
+
// 先确定主模型(用于缓存键)
|
|
36
40
|
const resolvedModel = this.modelSelector ? this.modelSelector.selectModel(label) : options?.model;
|
|
37
41
|
if (cacheable) {
|
|
38
42
|
const cached = this.cache.get(prompt, label, resolvedModel);
|
|
@@ -43,19 +47,42 @@ export class AIGateway {
|
|
|
43
47
|
}
|
|
44
48
|
// 限流
|
|
45
49
|
await this.rateLimiter.acquire();
|
|
50
|
+
this.totalCompletions++;
|
|
46
51
|
try {
|
|
47
|
-
//
|
|
52
|
+
// 分层模型策略 + 降级链
|
|
53
|
+
if (this.modelSelector) {
|
|
54
|
+
const chain = this.modelSelector.getFallbackChain(label);
|
|
55
|
+
const tiers = [chain.primary, ...chain.fallbacks];
|
|
56
|
+
let lastErr;
|
|
57
|
+
for (const tier of tiers) {
|
|
58
|
+
const model = this.modelSelector.modelForTier(tier);
|
|
59
|
+
const tieredOptions = { ...options, model };
|
|
60
|
+
try {
|
|
61
|
+
const result = await withRetry(() => this.inner.complete(prompt, label, tieredOptions), `AI调用(${label ?? 'unlabeled'}/${tier})`, { retryable: true, maxRetries: 2, retryDelayMs: 500, exponentialBackoff: true });
|
|
62
|
+
if (tier !== chain.primary) {
|
|
63
|
+
this.degradationCount++;
|
|
64
|
+
logger.info(`[AI Gateway] 降级成功:${chain.primary} → ${tier}(label=${label ?? '-'})`);
|
|
65
|
+
}
|
|
66
|
+
if (cacheable) {
|
|
67
|
+
this.cache.set(prompt, label, result, this.cacheTtlMs, model);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
lastErr = err;
|
|
73
|
+
if (tier !== tiers[tiers.length - 1]) {
|
|
74
|
+
logger.warn(`[AI Gateway] ${tier} 调用失败,降级到下一层(label=${label ?? '-'}):${err}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw lastErr;
|
|
79
|
+
}
|
|
80
|
+
// 无分层策略:原有逻辑
|
|
48
81
|
const tieredOptions = {
|
|
49
82
|
...options,
|
|
50
83
|
...(resolvedModel ? { model: resolvedModel } : {}),
|
|
51
84
|
};
|
|
52
|
-
|
|
53
|
-
const result = await withRetry(() => this.inner.complete(prompt, label, tieredOptions), `AI调用(${label ?? 'unlabeled'})`, {
|
|
54
|
-
retryable: true,
|
|
55
|
-
maxRetries: 3,
|
|
56
|
-
retryDelayMs: 1000,
|
|
57
|
-
exponentialBackoff: true,
|
|
58
|
-
});
|
|
85
|
+
const result = await withRetry(() => this.inner.complete(prompt, label, tieredOptions), `AI调用(${label ?? 'unlabeled'})`, { retryable: true, maxRetries: 3, retryDelayMs: 1000, exponentialBackoff: true });
|
|
59
86
|
if (cacheable) {
|
|
60
87
|
this.cache.set(prompt, label, result, this.cacheTtlMs, resolvedModel);
|
|
61
88
|
}
|
|
@@ -72,7 +99,10 @@ export class AIGateway {
|
|
|
72
99
|
const inner = this.inner.formatStats();
|
|
73
100
|
const hitRate = (this.cache.hitRate * 100).toFixed(1);
|
|
74
101
|
const limiter = this.rateLimiter.stats;
|
|
75
|
-
|
|
102
|
+
const degradationRate = this.totalCompletions === 0
|
|
103
|
+
? '0.0'
|
|
104
|
+
: ((this.degradationCount / this.totalCompletions) * 100).toFixed(1);
|
|
105
|
+
return `${inner}\n[AI Gateway] 缓存命中率: ${hitRate}% | 缓存条目: ${this.cache.size} | 并发: ${limiter.active}/${this.maxConcurrent} | 累计等待: ${limiter.totalWaitMs}ms | 降级率: ${degradationRate}%`;
|
|
76
106
|
}
|
|
77
107
|
}
|
|
78
108
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai-gateway/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAa,MAAM,2BAA2B,CAAC;AAcjE,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC;IACtC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAC3C,QAAQ,EAAE,MAAM,EAAE,MAAM;CACzB,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,OAAO,SAAS;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai-gateway/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type ModelTier = 'haiku' | 'sonnet' | 'opus';
|
|
2
|
+
export interface FallbackChain {
|
|
3
|
+
primary: ModelTier;
|
|
4
|
+
fallbacks: ModelTier[];
|
|
5
|
+
}
|
|
2
6
|
export declare class ModelSelector {
|
|
3
7
|
private tierToModel;
|
|
4
8
|
constructor(models?: {
|
|
@@ -8,5 +12,13 @@ export declare class ModelSelector {
|
|
|
8
12
|
});
|
|
9
13
|
selectTier(label?: string): ModelTier;
|
|
10
14
|
selectModel(label?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* 返回降级链:primary 失败时依次尝试 fallbacks。
|
|
17
|
+
* opus → sonnet → haiku
|
|
18
|
+
* sonnet → haiku
|
|
19
|
+
* haiku → (无降级)
|
|
20
|
+
*/
|
|
21
|
+
getFallbackChain(label?: string): FallbackChain;
|
|
22
|
+
modelForTier(tier: ModelTier): string;
|
|
11
23
|
}
|
|
12
24
|
//# sourceMappingURL=model-selector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/model-selector.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/model-selector.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,SAAS,CAAC;IACnB,SAAS,EAAE,SAAS,EAAE,CAAC;CACxB;AAkED,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAA4B;gBAEnC,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAQvE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS;IAKrC,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IAInC;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa;IAY/C,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM;CAGtC"}
|
|
@@ -75,5 +75,25 @@ export class ModelSelector {
|
|
|
75
75
|
selectModel(label) {
|
|
76
76
|
return this.tierToModel[this.selectTier(label)];
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* 返回降级链:primary 失败时依次尝试 fallbacks。
|
|
80
|
+
* opus → sonnet → haiku
|
|
81
|
+
* sonnet → haiku
|
|
82
|
+
* haiku → (无降级)
|
|
83
|
+
*/
|
|
84
|
+
getFallbackChain(label) {
|
|
85
|
+
const primary = this.selectTier(label);
|
|
86
|
+
switch (primary) {
|
|
87
|
+
case 'opus':
|
|
88
|
+
return { primary: 'opus', fallbacks: ['sonnet', 'haiku'] };
|
|
89
|
+
case 'sonnet':
|
|
90
|
+
return { primary: 'sonnet', fallbacks: ['haiku'] };
|
|
91
|
+
case 'haiku':
|
|
92
|
+
return { primary: 'haiku', fallbacks: [] };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
modelForTier(tier) {
|
|
96
|
+
return this.tierToModel[tier];
|
|
97
|
+
}
|
|
78
98
|
}
|
|
79
99
|
//# sourceMappingURL=model-selector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../src/ai-gateway/model-selector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../src/ai-gateway/model-selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 全局 AI
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 全局 AI 调用限流器(事件驱动队列模型)
|
|
3
|
+
*
|
|
4
|
+
* 设计:
|
|
5
|
+
* - 并发槽位控制:最多 maxConcurrent 个并发 AI 调用
|
|
6
|
+
* - 最小间隔控制:两次调用之间最小间隔 minIntervalMs
|
|
7
|
+
* - FIFO 等待队列:release() 直接唤醒下一个等待者,消除 setTimeout 轮询
|
|
5
8
|
*/
|
|
6
9
|
export declare class RateLimiter {
|
|
7
10
|
private maxConcurrent;
|
|
8
11
|
private minIntervalMs;
|
|
9
12
|
private activeCount;
|
|
10
|
-
private queue;
|
|
11
13
|
private lastCallAt;
|
|
14
|
+
/** FIFO 等待队列:每个元素是一个 resolve 回调 */
|
|
15
|
+
private queue;
|
|
16
|
+
private drainTimer;
|
|
17
|
+
private totalWaitMs;
|
|
12
18
|
constructor(maxConcurrent?: number, minIntervalMs?: number);
|
|
13
19
|
acquire(): Promise<void>;
|
|
14
20
|
release(): void;
|
|
15
21
|
get stats(): {
|
|
16
22
|
active: number;
|
|
17
23
|
queued: number;
|
|
24
|
+
totalWaitMs: number;
|
|
18
25
|
};
|
|
26
|
+
private canAcquireNow;
|
|
27
|
+
/**
|
|
28
|
+
* 尝试唤醒队列头部的等待者。
|
|
29
|
+
* 若间隔未满足,延迟到间隔结束后再次尝试。
|
|
30
|
+
*/
|
|
31
|
+
private drainQueue;
|
|
19
32
|
}
|
|
20
33
|
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/rate-limiter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,qBAAa,WAAW;IASpB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,aAAa;IATvB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAK;IACvB,mCAAmC;IACnC,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,WAAW,CAAK;gBAGd,aAAa,SAAI,EACjB,aAAa,SAAM;IAGvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB9B,OAAO,IAAI,IAAI;IAKf,IAAI,KAAK,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAEnE;IAED,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;CAkBnB"}
|
|
@@ -1,45 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 全局 AI
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 全局 AI 调用限流器(事件驱动队列模型)
|
|
3
|
+
*
|
|
4
|
+
* 设计:
|
|
5
|
+
* - 并发槽位控制:最多 maxConcurrent 个并发 AI 调用
|
|
6
|
+
* - 最小间隔控制:两次调用之间最小间隔 minIntervalMs
|
|
7
|
+
* - FIFO 等待队列:release() 直接唤醒下一个等待者,消除 setTimeout 轮询
|
|
5
8
|
*/
|
|
6
9
|
export class RateLimiter {
|
|
7
10
|
maxConcurrent;
|
|
8
11
|
minIntervalMs;
|
|
9
12
|
activeCount = 0;
|
|
10
|
-
queue = [];
|
|
11
13
|
lastCallAt = 0;
|
|
14
|
+
/** FIFO 等待队列:每个元素是一个 resolve 回调 */
|
|
15
|
+
queue = [];
|
|
16
|
+
drainTimer = null;
|
|
17
|
+
totalWaitMs = 0;
|
|
12
18
|
constructor(maxConcurrent = 3, minIntervalMs = 200) {
|
|
13
19
|
this.maxConcurrent = maxConcurrent;
|
|
14
20
|
this.minIntervalMs = minIntervalMs;
|
|
15
21
|
}
|
|
16
22
|
async acquire() {
|
|
23
|
+
// 快速路径:槽位充足且间隔满足,直接获取
|
|
24
|
+
if (this.canAcquireNow()) {
|
|
25
|
+
this.activeCount++;
|
|
26
|
+
this.lastCallAt = Date.now();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// 慢速路径:入队等待
|
|
30
|
+
const enqueuedAt = Date.now();
|
|
17
31
|
return new Promise((resolve) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
const delay = Math.max(this.minIntervalMs - sinceLastCall, this.activeCount >= this.maxConcurrent ? 50 : 0);
|
|
28
|
-
setTimeout(tryAcquire, delay);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
tryAcquire();
|
|
32
|
+
this.queue.push(() => {
|
|
33
|
+
this.totalWaitMs += Date.now() - enqueuedAt;
|
|
34
|
+
this.activeCount++;
|
|
35
|
+
this.lastCallAt = Date.now();
|
|
36
|
+
resolve();
|
|
37
|
+
});
|
|
38
|
+
this.drainQueue();
|
|
32
39
|
});
|
|
33
40
|
}
|
|
34
41
|
release() {
|
|
35
42
|
this.activeCount = Math.max(0, this.activeCount - 1);
|
|
36
|
-
|
|
43
|
+
this.drainQueue();
|
|
44
|
+
}
|
|
45
|
+
get stats() {
|
|
46
|
+
return { active: this.activeCount, queued: this.queue.length, totalWaitMs: this.totalWaitMs };
|
|
47
|
+
}
|
|
48
|
+
canAcquireNow() {
|
|
49
|
+
// 允许首批并发请求快速获取(填满并发槽位);
|
|
50
|
+
// 当存在已激活请求时,不对新进入的并发请求施加间隔限制。
|
|
51
|
+
if (this.activeCount > 0) {
|
|
52
|
+
return this.activeCount < this.maxConcurrent;
|
|
53
|
+
}
|
|
54
|
+
const sinceLastCall = Date.now() - this.lastCallAt;
|
|
55
|
+
return this.activeCount < this.maxConcurrent && sinceLastCall >= this.minIntervalMs;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 尝试唤醒队列头部的等待者。
|
|
59
|
+
* 若间隔未满足,延迟到间隔结束后再次尝试。
|
|
60
|
+
*/
|
|
61
|
+
drainQueue() {
|
|
62
|
+
if (this.queue.length === 0)
|
|
63
|
+
return;
|
|
64
|
+
if (this.activeCount >= this.maxConcurrent)
|
|
65
|
+
return;
|
|
66
|
+
const sinceLastCall = Date.now() - this.lastCallAt;
|
|
67
|
+
if (sinceLastCall >= this.minIntervalMs) {
|
|
37
68
|
const next = this.queue.shift();
|
|
38
69
|
next?.();
|
|
39
70
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
71
|
+
else {
|
|
72
|
+
// 间隔未满足:等到间隔结束后再唤醒(同一时刻仅保留一个定时器)
|
|
73
|
+
const delay = this.minIntervalMs - sinceLastCall;
|
|
74
|
+
if (this.drainTimer)
|
|
75
|
+
return;
|
|
76
|
+
this.drainTimer = setTimeout(() => {
|
|
77
|
+
this.drainTimer = null;
|
|
78
|
+
this.drainQueue();
|
|
79
|
+
}, delay);
|
|
80
|
+
}
|
|
43
81
|
}
|
|
44
82
|
}
|
|
45
83
|
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/ai-gateway/rate-limiter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/ai-gateway/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI
|
|
3
|
-
* key = SHA256(
|
|
4
|
-
* TTL
|
|
2
|
+
* AI 响应内存缓存(带容量治理)
|
|
3
|
+
* key = SHA256(model + label + prompt)
|
|
4
|
+
* - TTL 过期淘汰
|
|
5
|
+
* - maxEntries 容量上限(近似 LRU)
|
|
5
6
|
*/
|
|
6
7
|
export declare class ResponseCache {
|
|
8
|
+
private maxEntries;
|
|
7
9
|
private cache;
|
|
8
10
|
private hits;
|
|
9
11
|
private misses;
|
|
12
|
+
constructor(maxEntries?: number);
|
|
10
13
|
get(prompt: string, label?: string, model?: string): string | undefined;
|
|
11
14
|
set(prompt: string, label: string | undefined, value: string, ttlMs?: number, model?: string): void;
|
|
12
15
|
invalidate(): void;
|
|
13
16
|
get hitRate(): number;
|
|
14
17
|
get size(): number;
|
|
18
|
+
private evictExpired;
|
|
19
|
+
private evictOverflow;
|
|
15
20
|
private makeKey;
|
|
16
21
|
}
|
|
17
22
|
//# sourceMappingURL=response-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/response-cache.ts"],"names":[],"mappings":"AAOA
|
|
1
|
+
{"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/ai-gateway/response-cache.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,qBAAa,aAAa;IAKZ,OAAO,CAAC,UAAU;IAJ9B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;gBAEC,UAAU,SAAM;IAEpC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAqBvE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAepG,UAAU,IAAI,IAAI;IAIlB,IAAI,OAAO,IAAI,MAAM,CAGpB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,OAAO;CAGhB"}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
/**
|
|
3
|
-
* AI
|
|
4
|
-
* key = SHA256(
|
|
5
|
-
* TTL
|
|
3
|
+
* AI 响应内存缓存(带容量治理)
|
|
4
|
+
* key = SHA256(model + label + prompt)
|
|
5
|
+
* - TTL 过期淘汰
|
|
6
|
+
* - maxEntries 容量上限(近似 LRU)
|
|
6
7
|
*/
|
|
7
8
|
export class ResponseCache {
|
|
9
|
+
maxEntries;
|
|
8
10
|
cache = new Map();
|
|
9
11
|
hits = 0;
|
|
10
12
|
misses = 0;
|
|
13
|
+
constructor(maxEntries = 500) {
|
|
14
|
+
this.maxEntries = maxEntries;
|
|
15
|
+
}
|
|
11
16
|
get(prompt, label, model) {
|
|
12
17
|
const key = this.makeKey(prompt, label, model);
|
|
13
18
|
const entry = this.cache.get(key);
|
|
@@ -20,12 +25,23 @@ export class ResponseCache {
|
|
|
20
25
|
this.misses++;
|
|
21
26
|
return undefined;
|
|
22
27
|
}
|
|
28
|
+
// 近似 LRU:命中时刷新插入顺序
|
|
29
|
+
this.cache.delete(key);
|
|
30
|
+
this.cache.set(key, entry);
|
|
23
31
|
this.hits++;
|
|
24
32
|
return entry.value;
|
|
25
33
|
}
|
|
26
34
|
set(prompt, label, value, ttlMs = 300_000, model) {
|
|
27
35
|
const key = this.makeKey(prompt, label, model);
|
|
28
|
-
|
|
36
|
+
const entry = { value, expiresAt: Date.now() + ttlMs };
|
|
37
|
+
// 先清理已过期条目
|
|
38
|
+
this.evictExpired();
|
|
39
|
+
// 更新同 key 时,先删后插(保持最新顺序)
|
|
40
|
+
if (this.cache.has(key))
|
|
41
|
+
this.cache.delete(key);
|
|
42
|
+
this.cache.set(key, entry);
|
|
43
|
+
// 超出容量:淘汰最旧条目(Map 头部)
|
|
44
|
+
this.evictOverflow();
|
|
29
45
|
}
|
|
30
46
|
invalidate() {
|
|
31
47
|
this.cache.clear();
|
|
@@ -37,8 +53,24 @@ export class ResponseCache {
|
|
|
37
53
|
get size() {
|
|
38
54
|
return this.cache.size;
|
|
39
55
|
}
|
|
56
|
+
evictExpired() {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
for (const [k, entry] of this.cache.entries()) {
|
|
59
|
+
if (entry.expiresAt <= now) {
|
|
60
|
+
this.cache.delete(k);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
evictOverflow() {
|
|
65
|
+
while (this.cache.size > this.maxEntries) {
|
|
66
|
+
const oldestKey = this.cache.keys().next().value;
|
|
67
|
+
if (!oldestKey)
|
|
68
|
+
break;
|
|
69
|
+
this.cache.delete(oldestKey);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
40
72
|
makeKey(prompt, label, model) {
|
|
41
|
-
return createHash('sha256').update(`${model ?? ''}::${label ?? ''}::${prompt}`).digest('hex')
|
|
73
|
+
return createHash('sha256').update(`${model ?? ''}::${label ?? ''}::${prompt}`).digest('hex');
|
|
42
74
|
}
|
|
43
75
|
}
|
|
44
76
|
//# sourceMappingURL=response-cache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-cache.js","sourceRoot":"","sources":["../../src/ai-gateway/response-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAOpC
|
|
1
|
+
{"version":3,"file":"response-cache.js","sourceRoot":"","sources":["../../src/ai-gateway/response-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AIProvider } from '../ai-provider/types.js';
|
|
2
|
+
import type { PipelinePhase } from '../pipeline/types.js';
|
|
3
|
+
export interface TaskCompletionContext {
|
|
4
|
+
requirement: string;
|
|
5
|
+
currentPhase: PipelinePhase;
|
|
6
|
+
/** 最近 5 次工具调用名称 */
|
|
7
|
+
recentTools: string[];
|
|
8
|
+
toolCallCount: number;
|
|
9
|
+
/** 当前阶段已完成任务数 */
|
|
10
|
+
completedTaskCount: number;
|
|
11
|
+
/** 当前阶段总任务数 */
|
|
12
|
+
totalTaskCount: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CompletionDetectionResult {
|
|
15
|
+
isComplete: boolean;
|
|
16
|
+
confidence: 'high' | 'medium' | 'low';
|
|
17
|
+
reason: string;
|
|
18
|
+
suggestedAction: 'advance' | 'wait' | 'ask_user';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* LLM 语义任务完成检测器
|
|
22
|
+
*
|
|
23
|
+
* 使用 Haiku 快速判断当前阶段是否完成,替代基于工具调用次数的启发式阈值。
|
|
24
|
+
* 保留阈值逻辑作为兜底,防止 LLM 误判导致无限循环。
|
|
25
|
+
*/
|
|
26
|
+
export declare class TaskCompletionDetector {
|
|
27
|
+
private aiProvider;
|
|
28
|
+
constructor(aiProvider: AIProvider);
|
|
29
|
+
detect(ctx: TaskCompletionContext): Promise<CompletionDetectionResult>;
|
|
30
|
+
private parseResponse;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=task-completion-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-completion-detector.d.ts","sourceRoot":"","sources":["../../src/autopilot/task-completion-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,aAAa,CAAC;IAC5B,mBAAmB;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;CAClD;AA6CD;;;;;GAKG;AACH,qBAAa,sBAAsB;IACrB,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAEpC,MAAM,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA0B5E,OAAO,CAAC,aAAa;CAgCtB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
const PHASE_DESCRIPTIONS = {
|
|
3
|
+
analyze: '需求分析(输出功能点列表、技术方案、风险点)',
|
|
4
|
+
design: '架构设计(输出目录结构、核心接口定义、设计文档)',
|
|
5
|
+
profile: '性能分析(输出 profiling 结果、基准数据、瓶颈定位)',
|
|
6
|
+
code: '编码实现(按设计方案编写源代码文件)',
|
|
7
|
+
test: '测试编写(编写测试文件并运行通过)',
|
|
8
|
+
review: '代码审查(安全性、规范性、性能检查并修复)',
|
|
9
|
+
done: '已完成',
|
|
10
|
+
};
|
|
11
|
+
const COMPLETION_PROMPT = (ctx) => `你是一个软件开发流水线的阶段完成度评估器。
|
|
12
|
+
|
|
13
|
+
## 当前上下文
|
|
14
|
+
|
|
15
|
+
- **需求**:${ctx.requirement}
|
|
16
|
+
- **当前阶段**:${ctx.currentPhase}(${PHASE_DESCRIPTIONS[ctx.currentPhase]})
|
|
17
|
+
- **任务进度**:${ctx.completedTaskCount}/${ctx.totalTaskCount} 个任务已完成
|
|
18
|
+
- **工具调用总数**:${ctx.toolCallCount}
|
|
19
|
+
- **最近工具调用**:${ctx.recentTools.join(', ') || '(无)'}
|
|
20
|
+
|
|
21
|
+
## 评估任务
|
|
22
|
+
|
|
23
|
+
根据以上信息,判断当前阶段是否已经完成,可以推进到下一阶段。
|
|
24
|
+
|
|
25
|
+
## 评估标准
|
|
26
|
+
|
|
27
|
+
- analyze 阶段:已有探索(Read/Glob/Grep)+ 输出(Write/Edit),或明确说明分析完成
|
|
28
|
+
- design 阶段:已创建设计文档或目录结构(Write/Edit 包含 design/architecture/README)
|
|
29
|
+
- profile 阶段:已执行 profiling 命令(Bash)或写入性能报告
|
|
30
|
+
- code 阶段:已创建源代码文件(Write .ts/.js/.py/.go 等)
|
|
31
|
+
- test 阶段:已写入测试文件(Write/Edit 包含 test/spec)且已运行测试(Bash vitest/jest)
|
|
32
|
+
- review 阶段:已有代码修改(Edit)
|
|
33
|
+
|
|
34
|
+
## 输出格式
|
|
35
|
+
|
|
36
|
+
严格输出 JSON,不要有任何其他文字:
|
|
37
|
+
{
|
|
38
|
+
"isComplete": true/false,
|
|
39
|
+
"confidence": "high"/"medium"/"low",
|
|
40
|
+
"reason": "简短说明(中文,不超过 50 字)",
|
|
41
|
+
"suggestedAction": "advance"/"wait"/"ask_user"
|
|
42
|
+
}`;
|
|
43
|
+
/**
|
|
44
|
+
* LLM 语义任务完成检测器
|
|
45
|
+
*
|
|
46
|
+
* 使用 Haiku 快速判断当前阶段是否完成,替代基于工具调用次数的启发式阈值。
|
|
47
|
+
* 保留阈值逻辑作为兜底,防止 LLM 误判导致无限循环。
|
|
48
|
+
*/
|
|
49
|
+
export class TaskCompletionDetector {
|
|
50
|
+
aiProvider;
|
|
51
|
+
constructor(aiProvider) {
|
|
52
|
+
this.aiProvider = aiProvider;
|
|
53
|
+
}
|
|
54
|
+
async detect(ctx) {
|
|
55
|
+
try {
|
|
56
|
+
const prompt = COMPLETION_PROMPT(ctx);
|
|
57
|
+
const response = await this.aiProvider.complete(prompt, 'task-completion-detector', {
|
|
58
|
+
maxTokens: 200,
|
|
59
|
+
temperature: 0,
|
|
60
|
+
});
|
|
61
|
+
const result = this.parseResponse(response);
|
|
62
|
+
if (result) {
|
|
63
|
+
logger.info(`[任务完成检测] ${ctx.currentPhase} 阶段:${result.isComplete ? '完成' : '未完成'}(${result.confidence})- ${result.reason}`);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
logger.warn(`[任务完成检测] LLM 调用失败,降级到保守策略:${err}`);
|
|
69
|
+
}
|
|
70
|
+
// 降级:保守策略(未完成)
|
|
71
|
+
return {
|
|
72
|
+
isComplete: false,
|
|
73
|
+
confidence: 'low',
|
|
74
|
+
reason: 'LLM 检测失败,保守判断为未完成',
|
|
75
|
+
suggestedAction: 'wait',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
parseResponse(response) {
|
|
79
|
+
try {
|
|
80
|
+
// 提取 JSON(可能被 markdown 代码块包裹)
|
|
81
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
82
|
+
if (!jsonMatch)
|
|
83
|
+
return null;
|
|
84
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
85
|
+
if (typeof parsed.isComplete !== 'boolean' ||
|
|
86
|
+
!['high', 'medium', 'low'].includes(parsed.confidence) ||
|
|
87
|
+
typeof parsed.reason !== 'string' ||
|
|
88
|
+
!['advance', 'wait', 'ask_user'].includes(parsed.suggestedAction)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
isComplete: parsed.isComplete,
|
|
93
|
+
confidence: parsed.confidence,
|
|
94
|
+
reason: parsed.reason,
|
|
95
|
+
suggestedAction: parsed.suggestedAction,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=task-completion-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-completion-detector.js","sourceRoot":"","sources":["../../src/autopilot/task-completion-detector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAqB5C,MAAM,kBAAkB,GAAkC;IACxD,OAAO,EAAE,wBAAwB;IACjC,MAAM,EAAE,0BAA0B;IAClC,OAAO,EAAE,iCAAiC;IAC1C,IAAI,EAAE,oBAAoB;IAC1B,IAAI,EAAE,mBAAmB;IACzB,MAAM,EAAE,uBAAuB;IAC/B,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,GAA0B,EAAU,EAAE,CAAC;;;;WAIvD,GAAG,CAAC,WAAW;aACb,GAAG,CAAC,YAAY,IAAI,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC;aACxD,GAAG,CAAC,kBAAkB,IAAI,GAAG,CAAC,cAAc;eAC1C,GAAG,CAAC,aAAa;eACjB,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK;;;;;;;;;;;;;;;;;;;;;;;EAuBhD,CAAC;AAEH;;;;;GAKG;AACH,MAAM,OAAO,sBAAsB;IACb;IAApB,YAAoB,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAE9C,KAAK,CAAC,MAAM,CAAC,GAA0B;QACrC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,0BAA0B,EAAE;gBAClF,SAAS,EAAE,GAAG;gBACd,WAAW,EAAE,CAAC;aACf,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,YAAY,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,UAAU,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC3H,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,6BAA6B,GAAG,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,eAAe;QACf,OAAO;YACL,UAAU,EAAE,KAAK;YACjB,UAAU,EAAE,KAAK;YACjB,MAAM,EAAE,mBAAmB;YAC3B,eAAe,EAAE,MAAM;SACxB,CAAC;IACJ,CAAC;IAEO,aAAa,CAAC,QAAgB;QACpC,IAAI,CAAC;YACH,8BAA8B;YAC9B,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAChD,IAAI,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC;YAE5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAKrC,CAAC;YAEF,IACE,OAAO,MAAM,CAAC,UAAU,KAAK,SAAS;gBACtC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAoB,CAAC;gBAChE,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;gBACjC,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAyB,CAAC,EAC3E,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO;gBACL,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,UAAU,EAAE,MAAM,CAAC,UAAuC;gBAC1D,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,eAAe,EAAE,MAAM,CAAC,eAAkD;aAC3E,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
|