fix(agent): decouple agent streams from compat

closes #6851
This commit is contained in:
Mario Zechner
2026-07-20 17:54:17 +02:00
parent 3a40794ea1
commit 1235c0ec64
29 changed files with 219 additions and 105 deletions
+13
View File
@@ -0,0 +1,13 @@
import { Agent } from "@earendil-works/pi-agent-core";
import { createModels } from "@earendil-works/pi-ai";
import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";
const models = createModels();
models.setProvider(anthropicProvider());
const model = models.getModel("anthropic", "claude-sonnet-4-5");
if (!model) throw new Error("Anthropic smoke-test model not found");
export const agent = new Agent({
initialState: { model },
streamFunction: models.streamSimple.bind(models),
});
+2 -2
View File
@@ -1,5 +1,5 @@
import { createAssistantMessageEventStream, Type } from "@earendil-works/pi-ai";
import { complete, getModel, getProviders } from "@earendil-works/pi-ai/compat";
import { complete, getModel, getProviders, streamSimple } from "@earendil-works/pi-ai/compat";
import {
Agent,
bashExecutionToText,
@@ -24,7 +24,7 @@ const model = getModel("google", "gemini-2.5-flash");
const schema = Type.Object({ prompt: Type.String() });
const stream = createAssistantMessageEventStream();
const agent = new Agent({ initialState: { model } });
const agent = new Agent({ initialState: { model }, streamFunction: streamSimple });
agent.steer({ role: "user", content: [{ type: "text", text: "queued" }], timestamp: 0 });
const repo = new InMemorySessionRepo();
const result = getOrThrow(ok({ value: 1 }));
+58
View File
@@ -4,6 +4,7 @@ import { dirname, join, resolve } from "node:path";
import { build } from "esbuild";
const outputPath = join(tmpdir(), "pi-browser-smoke.js");
const agentTreeshakeOutputPath = join(tmpdir(), "pi-agent-treeshake-smoke.js");
const errorLogPath = join(tmpdir(), "pi-browser-smoke-errors.log");
const generatedCatalogDataDir = join(process.cwd(), "packages/ai/src/providers/data");
@@ -23,6 +24,22 @@ const generatedCatalogDataPlugin = {
},
};
function normalizePath(path) {
return path.replaceAll("\\", "/");
}
function findInput(inputs, suffix) {
return Object.keys(inputs).find((input) => {
const normalized = normalizePath(input);
return normalized === suffix || normalized.endsWith(`/${suffix}`);
});
}
function includesNodePackage(inputs, packageName) {
const marker = `node_modules/${packageName}/`;
return Object.keys(inputs).some((input) => normalizePath(input).includes(marker));
}
try {
await build({
entryPoints: ["scripts/browser-smoke-entry.ts"],
@@ -33,6 +50,47 @@ try {
outfile: outputPath,
plugins: [generatedCatalogDataPlugin],
});
const agentTreeshakeBuild = await build({
entryPoints: ["scripts/agent-treeshake-smoke-entry.ts"],
bundle: true,
platform: "browser",
format: "esm",
logLevel: "silent",
metafile: true,
outfile: agentTreeshakeOutputPath,
plugins: [generatedCatalogDataPlugin],
write: false,
});
const inputs = agentTreeshakeBuild.metafile.inputs;
for (const forbiddenInput of [
"packages/ai/src/compat.ts",
"packages/ai/src/models.generated.ts",
"packages/ai/src/providers/all.ts",
]) {
const includedInput = findInput(inputs, forbiddenInput);
if (includedInput) {
throw new Error(`Agent selective-provider bundle unexpectedly includes ${includedInput}`);
}
}
const aiSdkPackages = [
"@anthropic-ai/sdk",
"@aws-sdk/client-bedrock-runtime",
"@google/genai",
"@mistralai/mistralai",
"openai",
];
const includedAiSdkPackages = aiSdkPackages.filter((packageName) => includesNodePackage(inputs, packageName));
if (
includedAiSdkPackages.length !== 1 ||
includedAiSdkPackages[0] !== "@anthropic-ai/sdk"
) {
throw new Error(
`Agent selective-provider bundle SDKs: expected only @anthropic-ai/sdk, found ${includedAiSdkPackages.join(", ") || "none"}`,
);
}
process.exit(0);
} catch (error) {
let detailedErrors = "";