Route tool ranker fallback through executor
This commit is contained in:
+1
-1
@@ -74,7 +74,7 @@
|
|||||||
- [x] Убрать дублирующий ручной `tool-rank-audit.ts`, если stage полностью заменит его.
|
- [x] Убрать дублирующий ручной `tool-rank-audit.ts`, если stage полностью заменит его.
|
||||||
- [x] Сохранить status UX: `🧩 Выбираю подходящие инструменты...`.
|
- [x] Сохранить status UX: `🧩 Выбираю подходящие инструменты...`.
|
||||||
- [x] Гарантировать `clearStatus()` после ranker success/failure.
|
- [x] Гарантировать `clearStatus()` после ranker success/failure.
|
||||||
- [ ] Добавить fallback через `PipelineFallbackExecutor`: main model, all tools, no tools.
|
- [x] Добавить fallback через `PipelineFallbackExecutor`: main model, all tools, no tools.
|
||||||
- [x] Добавить tests на fallback ranker policy.
|
- [x] Добавить tests на fallback ranker policy.
|
||||||
|
|
||||||
## 6. Сделать model_call и tool_loop физически отдельными stages
|
## 6. Сделать model_call и tool_loop физически отдельными stages
|
||||||
|
|||||||
@@ -1,23 +1,56 @@
|
|||||||
import {ToolRankerFallbackPolicy} from "../common/policies.js";
|
import {ToolRankerFallbackPolicy} from "../common/policies.js";
|
||||||
|
import {decidePipelineFallback, type PipelineFallbackDecision} from "./user-request-pipeline/fallback-executor.js";
|
||||||
|
|
||||||
export type ToolRankerFallbackSelection = {
|
export type ToolRankerFallbackSelection = {
|
||||||
toolNames: string[];
|
toolNames: string[];
|
||||||
usedRanker: boolean;
|
usedRanker: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resolveToolRankerFallbackSelection(params: {
|
export type ToolRankerFallbackDecision = PipelineFallbackDecision & ToolRankerFallbackSelection;
|
||||||
|
|
||||||
|
function fallbackActionForPolicy(policy: ToolRankerFallbackPolicy) {
|
||||||
|
return policy === ToolRankerFallbackPolicy.MAIN_MODEL
|
||||||
|
? "use_alternate_target"
|
||||||
|
: "continue_without_stage";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decideToolRankerFallback(params: {
|
||||||
fallbackPolicy: ToolRankerFallbackPolicy;
|
fallbackPolicy: ToolRankerFallbackPolicy;
|
||||||
availableToolNames: readonly string[];
|
availableToolNames: readonly string[];
|
||||||
}): ToolRankerFallbackSelection {
|
reason: "unavailable" | "failed";
|
||||||
if (params.fallbackPolicy === ToolRankerFallbackPolicy.NO_TOOLS) {
|
}): ToolRankerFallbackDecision {
|
||||||
|
const action = fallbackActionForPolicy(params.fallbackPolicy);
|
||||||
|
const decision = decidePipelineFallback({
|
||||||
|
stage: "tool_rank",
|
||||||
|
reason: params.reason,
|
||||||
|
policies: [{
|
||||||
|
stage: "tool_rank",
|
||||||
|
onUnavailable: action,
|
||||||
|
onFailed: action,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toolNames: [],
|
...decision,
|
||||||
|
toolNames: params.fallbackPolicy === ToolRankerFallbackPolicy.NO_TOOLS
|
||||||
|
? []
|
||||||
|
: [...params.availableToolNames],
|
||||||
usedRanker: false,
|
usedRanker: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveToolRankerFallbackSelection(params: {
|
||||||
|
fallbackPolicy: ToolRankerFallbackPolicy;
|
||||||
|
availableToolNames: readonly string[];
|
||||||
|
}): ToolRankerFallbackSelection {
|
||||||
|
const decision = decideToolRankerFallback({
|
||||||
|
fallbackPolicy: params.fallbackPolicy,
|
||||||
|
availableToolNames: params.availableToolNames,
|
||||||
|
reason: "failed",
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toolNames: [...params.availableToolNames],
|
toolNames: decision.toolNames,
|
||||||
usedRanker: false,
|
usedRanker: decision.usedRanker,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import test from "node:test";
|
|||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
const {ToolRankerFallbackPolicy} = await import("../dist/common/policies.js");
|
const {ToolRankerFallbackPolicy} = await import("../dist/common/policies.js");
|
||||||
const {resolveToolRankerFallbackSelection} = await import("../dist/ai/tool-ranker-fallback.js");
|
const {
|
||||||
|
decideToolRankerFallback,
|
||||||
|
resolveToolRankerFallbackSelection,
|
||||||
|
} = await import("../dist/ai/tool-ranker-fallback.js");
|
||||||
|
|
||||||
const availableToolNames = ["read_file", "search_files"];
|
const availableToolNames = ["read_file", "search_files"];
|
||||||
|
|
||||||
@@ -32,6 +35,26 @@ test("tool ranker fallback returns all tools when policy is ALL_TOOLS", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("tool ranker fallback decision uses executor semantics", () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
decideToolRankerFallback({
|
||||||
|
fallbackPolicy: ToolRankerFallbackPolicy.MAIN_MODEL,
|
||||||
|
availableToolNames,
|
||||||
|
reason: "failed",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
stage: "tool_rank",
|
||||||
|
reason: "failed",
|
||||||
|
action: "use_alternate_target",
|
||||||
|
shouldContinue: true,
|
||||||
|
shouldNotifyUser: false,
|
||||||
|
shouldFailRequest: false,
|
||||||
|
toolNames: ["read_file", "search_files"],
|
||||||
|
usedRanker: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("tool ranker fallback keeps all tools when policy is MAIN_MODEL", () => {
|
test("tool ranker fallback keeps all tools when policy is MAIN_MODEL", () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
resolveToolRankerFallbackSelection({
|
resolveToolRankerFallbackSelection({
|
||||||
|
|||||||
Reference in New Issue
Block a user