Skip to content

Commit c9dd785

Browse files
committed
fix(google): strip provider prefix from Vertex model path
buildGoogleVertexRequestUrl embedded model.id directly, so a provider-qualified id like google/gemini-3.1-pro-preview produced an invalid Vertex path segment publishers/google/models/google%2Fgemini-... (404). The sibling Generative-AI path (resolveGoogleModelPath) already strips this prefix via stripGoogleProviderPrefix; mirror it on the Vertex path. Companion to #91125, which added the same strip to the Generative-AI path.
1 parent 47dbc67 commit c9dd785

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

extensions/google/transport-stream.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,46 @@ describe("google transport stream", () => {
913913
expect(new Headers(guardedInit.headers).has("x-goog-api-key")).toBe(false);
914914
});
915915

916+
it("strips redundant google provider prefixes from Google Vertex model paths", async () => {
917+
const tempDir = await mkdtemp(path.join(os.tmpdir(), "openclaw-google-vertex-prefix-"));
918+
vi.stubEnv("HOME", path.join(tempDir, "home"));
919+
vi.stubEnv("APPDATA", "");
920+
vi.stubEnv("GOOGLE_CLOUD_PROJECT", "vertex-project");
921+
vi.stubEnv("GOOGLE_CLOUD_LOCATION", "us-central1");
922+
googleAuthGetAccessTokenMock.mockResolvedValueOnce("ya29.transport-token");
923+
const tokenFetchMock = vi.fn();
924+
guardedFetchMock.mockResolvedValueOnce(
925+
buildSseResponse([
926+
{
927+
candidates: [{ content: { parts: [{ text: "ok" }] }, finishReason: "STOP" }],
928+
},
929+
]),
930+
);
931+
932+
const streamFn = createGoogleVertexTransportStreamFn();
933+
const stream = await Promise.resolve(
934+
streamFn(
935+
buildGoogleVertexModel({ id: "google/gemini-3.1-pro-preview" }),
936+
{
937+
messages: [{ role: "user", content: "hello", timestamp: 0 }],
938+
} as Parameters<typeof streamFn>[1],
939+
{
940+
apiKey: "gcp-vertex-credentials",
941+
fetch: tokenFetchMock,
942+
} as Parameters<typeof streamFn>[2],
943+
),
944+
);
945+
await stream.result();
946+
947+
// The provider prefix must be stripped from the Vertex model path, matching
948+
// resolveGoogleModelPath; otherwise the id becomes models/google%2F... (404).
949+
const guardedCall = requireMockCall(guardedFetchMock, 0, "guarded fetch");
950+
expect(guardedCall[0]).toContain(
951+
"/publishers/google/models/gemini-3.1-pro-preview:streamGenerateContent",
952+
);
953+
expect(guardedCall[0]).not.toContain("google%2F");
954+
});
955+
916956
it("refreshes authorized_user ADC before Google Vertex requests", async () => {
917957
const tempDir = await mkdtemp(path.join(os.tmpdir(), "openclaw-google-vertex-adc-"));
918958
const credentialsPath = path.join(tempDir, "application_default_credentials.json");

extensions/google/transport-stream.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,9 @@ function buildGoogleVertexRequestUrl(
391391
): string {
392392
const project = encodeURIComponent(resolveGoogleVertexProject(options));
393393
const location = encodeURIComponent(resolveGoogleVertexLocation(options));
394-
const modelId = encodeURIComponent(model.id);
394+
// Mirror resolveGoogleModelPath: strip the google/ provider prefix so a
395+
// provider-qualified id does not become an invalid models/google%2F... path.
396+
const modelId = encodeURIComponent(stripGoogleProviderPrefix(model.id));
395397
const origin = resolveGoogleVertexBaseOrigin(model, decodeURIComponent(location));
396398
return `${origin}/${GOOGLE_VERTEX_DEFAULT_API_VERSION}/projects/${project}/locations/${location}/publishers/google/models/${modelId}:streamGenerateContent?alt=sse`;
397399
}

0 commit comments

Comments
 (0)