fix: align manual native compaction fallback
(cherry picked from commit 2d5397a52f6feaeee4136fe4e7d8271309043b75)
This commit is contained in:
@@ -1460,10 +1460,18 @@ export class SessionMaintenance {
|
||||
const candidates =
|
||||
precomputedCandidates ?? this.#getCompactionModelCandidates(this.#host.modelRegistry.getAvailable());
|
||||
const telemetry = resolveTelemetry(this.#host.agent.telemetry, this.#host.sessionId());
|
||||
let nativeCompactionFailure: { error: NativeCompactionError; provider: string } | undefined;
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const apiKey = await this.#host.modelRegistry.getApiKey(candidate, this.#host.sessionId());
|
||||
if (!apiKey) continue;
|
||||
if (
|
||||
nativeCompactionFailure &&
|
||||
(candidate.provider !== nativeCompactionFailure.provider ||
|
||||
!shouldUseProviderNativeCompaction(candidate, preparation.settings))
|
||||
) {
|
||||
throw nativeCompactionFailure.error;
|
||||
}
|
||||
|
||||
try {
|
||||
return await compact(
|
||||
@@ -1501,12 +1509,17 @@ export class SessionMaintenance {
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
if (!AIError.is(AIError.classify(error, candidate.api), AIError.Flag.AuthFailed)) {
|
||||
throw error;
|
||||
const id = AIError.classify(error instanceof NativeCompactionError ? error.cause : error, candidate.api);
|
||||
if (AIError.is(id, AIError.Flag.AuthFailed)) continue;
|
||||
if (error instanceof NativeCompactionError) {
|
||||
nativeCompactionFailure ??= { error, provider: candidate.provider };
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeCompactionFailure) throw nativeCompactionFailure.error;
|
||||
throw this.#buildCompactionAuthError();
|
||||
}
|
||||
|
||||
|
||||
@@ -373,6 +373,35 @@ describe("issue #986 compaction auth fallback", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("tries same-provider native candidates during manual compaction before crossing providers", async () => {
|
||||
const { crossProviderModel, currentModel, sameProviderModel } = await createAutoNativeFallbackSession();
|
||||
const attemptedModels: string[] = [];
|
||||
vi.spyOn(compactionModule, "compact").mockImplementation(async (preparation, model) => {
|
||||
attemptedModels.push(`${model.provider}/${model.id}`);
|
||||
if (model.provider === currentModel.provider && model.id === currentModel.id) {
|
||||
throw new compactionModule.NativeCompactionError(new Error("native manual compaction failed"));
|
||||
}
|
||||
if (model.provider === sameProviderModel.provider && model.id === sameProviderModel.id) {
|
||||
return {
|
||||
summary: "same-provider manual summary",
|
||||
shortSummary: "same-provider manual",
|
||||
firstKeptEntryId: preparation.firstKeptEntryId,
|
||||
tokensBefore: 42,
|
||||
};
|
||||
}
|
||||
throw new Error(`Unexpected compaction model ${model.provider}/${model.id}`);
|
||||
});
|
||||
|
||||
const result = await session.compact();
|
||||
|
||||
expect(result.summary).toBe("same-provider manual summary");
|
||||
expect(attemptedModels).toEqual([
|
||||
`${currentModel.provider}/${currentModel.id}`,
|
||||
`${sameProviderModel.provider}/${sameProviderModel.id}`,
|
||||
]);
|
||||
expect(attemptedModels).not.toContain(`${crossProviderModel.provider}/${crossProviderModel.id}`);
|
||||
});
|
||||
|
||||
it("falls back across providers when native compaction receives auth_unavailable", async () => {
|
||||
const { currentModel, fallbackModel } = await createSession({ fallbackModelRole: "smol" });
|
||||
const originalCompact = compactionModule.compact;
|
||||
|
||||
Reference in New Issue
Block a user