mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat(ai,agent,coding-agent): move client identity and provider attribution into pi-ai
pi-ai gets a ClientIdentity request option and createModels() default that choose the User-Agent (detailed with version, short, or custom) and provider attribution headers for OpenRouter, NVIDIA NIM, and OpenCode. Attribution is off by default in pi-ai and on by default in pi-agent-core and pi-durable (HarnessOptions.identity). pi-coding-agent sets its identity from the telemetry setting instead of adding headers itself.
This commit is contained in:
parent
adfc14461a
commit
adc5fa9f80
55 changed files with 958 additions and 465 deletions
|
|
@ -31,6 +31,8 @@ export type ChatSetup = {
|
|||
/** Live Harness settings; tests assign fields to change them between decisions. */
|
||||
readonly settings: { -readonly [K in keyof HarnessSettings]: HarnessSettings[K] };
|
||||
now: () => number;
|
||||
/** `HarnessOptions.identity`. */
|
||||
identity?: HarnessOptions["identity"];
|
||||
};
|
||||
|
||||
export function chatSetup(options: RegisterFauxProviderOptions = {}): ChatSetup {
|
||||
|
|
@ -53,6 +55,7 @@ export async function openChat(
|
|||
models: setup.models,
|
||||
registry: setup.registry,
|
||||
settings: setup.settings,
|
||||
...(setup.identity === undefined ? {} : { identity: setup.identity }),
|
||||
...(options.env === undefined
|
||||
? {}
|
||||
: { env: typeof options.env === "function" ? options.env : () => options.env as ExecutionEnv }),
|
||||
|
|
|
|||
|
|
@ -385,6 +385,24 @@ describe("generation", () => {
|
|||
await harness.close(context);
|
||||
});
|
||||
|
||||
it("attributes model requests to pi by default and merges the Harness identity over it", async () => {
|
||||
const seen: (SimpleStreamOptions["identity"] | undefined)[] = [];
|
||||
const capture = (_context: unknown, options?: SimpleStreamOptions) => {
|
||||
seen.push(options?.identity);
|
||||
return fauxAssistantMessage("ok");
|
||||
};
|
||||
for (const identity of [undefined, { userAgent: "short" as const, attribution: "acme" }]) {
|
||||
const setup = chatSetup();
|
||||
setup.identity = identity;
|
||||
setup.faux.setResponses([capture]);
|
||||
const { harness, root } = await openChat(new MemoryStorage(), setup);
|
||||
harness.resume();
|
||||
await (await root.submit({ type: "input", content: "hi" }, context)).wait(context);
|
||||
await harness.close(context);
|
||||
}
|
||||
expect(seen).toEqual([{ attribution: true }, { attribution: "acme", userAgent: "short" }]);
|
||||
});
|
||||
|
||||
// Regression coverage for #10424.
|
||||
it("keeps provider session IDs request-local across concurrent conversations", async () => {
|
||||
const setup = chatSetup();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ describe("HarnessOptions.models and settings as functions", () => {
|
|||
]);
|
||||
first.setProvider(faux.provider);
|
||||
second.setProvider(faux.provider);
|
||||
// Marks the second Models: tools see views of the host's Models that add the Harness identity.
|
||||
second.setProvider(fauxProvider({ provider: "second-only" }).provider);
|
||||
let current = first;
|
||||
const seen: Models[] = [];
|
||||
const registry = createRegistry();
|
||||
|
|
@ -97,9 +99,8 @@ describe("HarnessOptions.models and settings as functions", () => {
|
|||
);
|
||||
const { harness, root } = await open({ models: () => current }, registry);
|
||||
expect(await turn(root, "go")).toBe("done");
|
||||
expect(seen).toEqual([first, second]);
|
||||
expect(seen[0]).toBe(first);
|
||||
expect(seen[1]).toBe(second);
|
||||
expect(seen[0]!.getProvider("second-only")).toBeUndefined();
|
||||
expect(seen[1]!.getProvider("second-only")).toBeDefined();
|
||||
await harness.close(context);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
fauxAssistantMessage,
|
||||
fauxText,
|
||||
fauxToolCall,
|
||||
type Models,
|
||||
type SystemMessage,
|
||||
type ToolResultMessage,
|
||||
Type,
|
||||
|
|
@ -130,8 +131,10 @@ describe("tool round", () => {
|
|||
});
|
||||
const { harness, status } = await run(setup, [calls(["echo", {}, "c1"]), DONE]);
|
||||
expect(status).toBe("done");
|
||||
expect(seen).toEqual([setup.models, setup.models]);
|
||||
expect(seen.every((models) => models === setup.models)).toBe(true);
|
||||
// One view of the host's Models that adds the Harness identity, shared by tools and hooks.
|
||||
expect(seen).toHaveLength(2);
|
||||
expect(seen[0]).toBe(seen[1]);
|
||||
expect((seen[0] as Models).getProviders()).toEqual(setup.models.getProviders());
|
||||
await harness.close(context);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue