From fb7341d82b2176ded84990e073d572f34e9d3170 Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Wed, 28 Oct 2026 04:54:57 +0200 Subject: [PATCH] Keep native Bedrock implementation in standalone traces --- scripts/smoke-provider-runtime.mjs | 21 +++++++++++++-------- src/agent/custom-providers.ts | 7 +++++-- tests/provider-runtime-payload.test.ts | 5 +++-- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/scripts/smoke-provider-runtime.mjs b/scripts/smoke-provider-runtime.mjs index 47c12c6..7cafc1d 100644 --- a/scripts/smoke-provider-runtime.mjs +++ b/scripts/smoke-provider-runtime.mjs @@ -1,7 +1,7 @@ /** - * OFFLINE native Pi Google initialization gate (no generation or provider calls). - * Unlike /api/models, this traverses Pi's lazy adapter and constructs GoogleGenAI. - * The real onPayload hook throws a unique sentinel BEFORE generateContentStream. + * OFFLINE native Pi Google/Bedrock initialization gate (no generation or provider calls). + * Unlike /api/models, this traverses Pi's lazy adapters and constructs real SDK clients. + * The real onPayload hook throws a unique sentinel BEFORE SDK request dispatch. * All file modules must resolve inside the payload, including nested dependencies. * * node scripts/smoke-provider-runtime.mjs [--standalone ] [--runtime ] @@ -81,17 +81,22 @@ async function probe(standalone) { const entry = pathToFileURL(path.join(root, "node_modules/@earendil-works/pi-ai/dist/index.js")); const { createModels } = await import(entry.href); const models = createModels(); - for (const [name, factory] of [["google", "googleProvider"], ["opencode", "opencodeProvider"], ["opencode-go", "opencodeGoProvider"]]) { + for (const [name, factory, api, label] of [ + ["google", "googleProvider", "google-generative-ai", "Google"], + ["opencode", "opencodeProvider", "google-generative-ai", "Google"], + ["opencode-go", "opencodeGoProvider", "google-generative-ai", "Google"], + ["amazon-bedrock", "amazonBedrockProvider", "bedrock-converse-stream", "Bedrock"], + ]) { const provider = (await import(new URL(`providers/${name}.js`, entry).href))[factory](); models.setProvider(provider); - const model = provider.getModels().find((candidate) => candidate.api === "google-generative-ai"); - assert.ok(model, `${name}: missing Google-backed catalog model`); + const model = provider.getModels().find((candidate) => candidate.api === api); + assert.ok(model, `${name}: missing ${api} catalog model`); const sentinel = `LH_OFFLINE_ON_PAYLOAD_${randomUUID()}`; let reached = 0; const result = await models.streamSimple(model, { messages: [{ role: "user", content: "Offline initialization only", timestamp: 0 }] }, { apiKey: "dummy-offline-provider-gate-not-a-key", onPayload(payload, selected) { - assert.equal(selected.api, "google-generative-ai"); + assert.equal(selected.api, api); assert.ok(payload && typeof payload === "object"); reached++; throw new Error(sentinel); @@ -101,7 +106,7 @@ async function probe(standalone) { assert.equal(result.stopReason, "error"); assert.equal(result.errorMessage, sentinel, `${name}: unexpected initialization error`); noNetwork(); - console.log(`[smoke-provider-runtime] ${name}: native lazy Google adapter reached onPayload; generation stopped`); + console.log(`[smoke-provider-runtime] ${name}: native lazy ${label} adapter reached onPayload; generation stopped`); } // Exercise the public lazy OAuth surface offline. toAuth evaluates the // real flow module and its imports but neither logs in nor refreshes. diff --git a/src/agent/custom-providers.ts b/src/agent/custom-providers.ts index 3966ea1..5c82773 100644 --- a/src/agent/custom-providers.ts +++ b/src/agent/custom-providers.ts @@ -8,7 +8,7 @@ import { openAICodexResponsesApi } from "@earendil-works/pi-ai/api/openai-codex- import { azureOpenAIResponsesApi } from "@earendil-works/pi-ai/api/azure-openai-responses.lazy"; import { googleGenerativeAIApi } from "@earendil-works/pi-ai/api/google-generative-ai.lazy"; import { googleVertexApi } from "@earendil-works/pi-ai/api/google-vertex.lazy"; -import { bedrockConverseStreamApi } from "@earendil-works/pi-ai/api/bedrock-converse-stream.lazy"; +import { lazyApi } from "@earendil-works/pi-ai/api/lazy"; import { mistralConversationsApi } from "@earendil-works/pi-ai/api/mistral-conversations.lazy"; import { piMessagesApi } from "@earendil-works/pi-ai/api/pi-messages.lazy"; import { resolveCustomValue, type CustomProviderConfig } from "../core/custom-providers.ts"; @@ -21,7 +21,10 @@ const apis: Record = { "azure-openai-responses": azureOpenAIResponsesApi(), "google-generative-ai": googleGenerativeAIApi(), "google-vertex": googleVertexApi(), - "bedrock-converse-stream": bedrockConverseStreamApi(), + // Pi's Bedrock wrapper intentionally hides its Node-only import from + // bundlers. Give this server's standalone tracer a literal public import; + // Pi still owns both lazy forwarding and the unchanged native transport. + "bedrock-converse-stream": lazyApi(() => import("@earendil-works/pi-ai/api/bedrock-converse-stream")), "mistral-conversations": mistralConversationsApi(), "pi-messages": piMessagesApi(), }; diff --git a/tests/provider-runtime-payload.test.ts b/tests/provider-runtime-payload.test.ts index dec3213..85c9b8b 100644 --- a/tests/provider-runtime-payload.test.ts +++ b/tests/provider-runtime-payload.test.ts @@ -1,4 +1,4 @@ -/** Real installed Pi/Google SDK only; no provider stubs, keys, or generation. */ +/** Real installed Pi/Google/Bedrock SDKs only; no provider stubs, keys, or generation. */ import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import * as fs from "node:fs"; @@ -12,11 +12,12 @@ const checkout = fileURLToPath(new URL("..", import.meta.url)); const run = (payload: string) => spawnSync(process.execPath, [script, "--standalone", payload], { encoding: "utf8", timeout: 40_000 }); describe("offline native Pi Google initialization gate", () => { - it("reaches the sentinel for direct Google and Google-backed OpenCode adapters using the real SDK", () => { + it("reaches the sentinel for Google, Google-backed OpenCode and Bedrock adapters using the real SDKs", () => { // This is an unbundled positive control, NOT proof of standalone completeness. const result = run(checkout); assert.equal(result.status, 0, String(result.stderr)); for (const provider of ["google", "opencode", "opencode-go"]) assert.ok(String(result.stdout).includes(`${provider}: native lazy Google adapter reached onPayload`)); + assert.ok(String(result.stdout).includes("amazon-bedrock: native lazy Bedrock adapter reached onPayload")); assert.match(String(result.stdout), /zero network attempts/); }); it("rejects a missing Pi entry instead of treating any error as success", () => {