Files
oh-my-pi/packages/coding-agent/test/settings-stream-fn.test.ts
T
roboomp 750e20e5cc fix(advisor): inherited provider-shaping options from the session
AgentSession.#buildAdvisorRuntime constructed the advisor Agent without the provider-shaping options the SDK installs on the main agent: the streamFn wrapper that applies providers.openrouterVariant / providers.antigravityEndpoint / providers.maxInFlightRequests / model.loopGuard.*, the onPayload/onResponse/onSseEvent hooks, the shared providerSessionState map, transformProviderContext (snapcompact, secret obfuscation, image clamping), and a stable promptCacheKey. Advisor turns therefore dropped the OpenRouter sticky-routing variant suffix, used a different prompt_cache_key than the main turn, and skipped the per-session provider hooks — producing intermittent OpenRouter response-cache misses across consecutive advisor calls.

Extract the inline streamFn wrapper in sdk.ts into a shared createSettingsAwareStreamFn helper (packages/coding-agent/src/session/settings-stream-fn.ts) and pass it (plus transformProviderContext) through AgentSessionConfig as advisorStreamFn / transformProviderContext. #buildAdvisorRuntime now hands the advisor Agent the same streamFn, hooks, providerSessionState, promptCacheKey (= advisor session id), and transformProviderContext as the main turn. Adds getAdvisorAgent() accessor on AgentSession for diagnostics and parity tests.

Fixes #3639
2026-06-27 07:35:34 +00:00

88 lines
3.7 KiB
TypeScript

/**
* Contract: `createSettingsAwareStreamFn` layers session provider settings
* (`providers.openrouterVariant`, `providers.antigravityEndpoint`,
* `providers.maxInFlightRequests`, `model.loopGuard.*`) onto every call while
* letting caller-supplied options win — the same wiring the main agent and the
* advisor agent share so OpenRouter sticky-routing / response caching behaves
* the same on advisor turns (can1357/oh-my-pi#3639).
*/
import { describe, expect, it } from "bun:test";
import type { StreamFn } from "@oh-my-pi/pi-agent-core";
import type { Context, Model, SimpleStreamOptions } from "@oh-my-pi/pi-ai";
import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import { createSettingsAwareStreamFn } from "@oh-my-pi/pi-coding-agent/session/settings-stream-fn";
function captureBase(): { fn: StreamFn; calls: Array<{ options?: SimpleStreamOptions }> } {
const calls: Array<{ options?: SimpleStreamOptions }> = [];
const fn: StreamFn = (_model, _context, options) => {
calls.push({ options });
return new AssistantMessageEventStream();
};
return { fn, calls };
}
const stubModel = {} as unknown as Model;
const stubContext = { messages: [], tools: [], systemPrompt: [] } as unknown as Context;
describe("createSettingsAwareStreamFn", () => {
it("applies provider settings to the forwarded options when caller omits them", () => {
const settings = Settings.isolated({
"providers.openrouterVariant": "floor",
"providers.antigravityEndpoint": "sandbox",
"providers.maxInFlightRequests": { openrouter: 4 },
"model.loopGuard.enabled": true,
"model.loopGuard.checkAssistantContent": true,
});
const { fn: base, calls } = captureBase();
const wrapped = createSettingsAwareStreamFn(settings, base);
wrapped(stubModel, stubContext, { apiKey: "k" });
const options = calls[0]?.options;
expect(options?.openrouterVariant).toBe("floor");
expect(options?.antigravityEndpointMode).toBe("sandbox");
expect(options?.maxInFlightRequests).toEqual({ openrouter: 4 });
expect(options?.loopGuard).toEqual({ enabled: true, checkAssistantContent: true });
// caller's own option is preserved
expect(options?.apiKey).toBe("k");
});
it("treats the default openrouterVariant as absent so the base call carries no variant", () => {
const settings = Settings.isolated({ "providers.openrouterVariant": "default" });
const { fn: base, calls } = captureBase();
const wrapped = createSettingsAwareStreamFn(settings, base);
wrapped(stubModel, stubContext, undefined);
expect(calls[0]?.options?.openrouterVariant).toBeUndefined();
});
it("lets caller-supplied options override the session settings", () => {
const settings = Settings.isolated({
"providers.openrouterVariant": "floor",
"providers.antigravityEndpoint": "sandbox",
"providers.maxInFlightRequests": { openrouter: 4 },
"model.loopGuard.enabled": true,
});
const { fn: base, calls } = captureBase();
const wrapped = createSettingsAwareStreamFn(settings, base);
wrapped(stubModel, stubContext, {
openrouterVariant: "nitro",
antigravityEndpointMode: "production",
maxInFlightRequests: { openrouter: 1 },
loopGuard: { enabled: false },
});
const options = calls[0]?.options;
expect(options?.openrouterVariant).toBe("nitro");
expect(options?.antigravityEndpointMode).toBe("production");
expect(options?.maxInFlightRequests).toEqual({ openrouter: 1 });
// Loop guard merges per-field: caller wins on `enabled`, settings fill
// the rest (the inline closure the main agent used has the same shape).
expect(options?.loopGuard?.enabled).toBe(false);
expect(options?.loopGuard?.checkAssistantContent).toBe(true);
});
});