mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 02:34:15 -05:00
fix(deps): remove deprecated uuid install path by replacing vertex-sdk with local client (#1771)
* fix(deps): remove deprecated uuid install path * fix(api): address PR #1232 review — type the local Vertex client surface Resolves the blocker raised by @Vasanthdev2004, @gnanam1990, and @jatmn: the in-repo AnthropicVertex replacement compiled under bun (no type-check) but added 4 `tsc --noEmit` errors that the upstream typed SDK did not. - Declare `messages`/`beta` as typed class fields (BaseAnthropic doesn't, but the upstream @anthropic-ai/vertex-sdk client did), so typed consumers — client.ts `new AnthropicVertex(...)` and the SDK calling `.messages` — keep the resource surface. (vertexClient.ts:145/146, test:53) - Widen the header-merge helpers to accept the base client's request header type (HeadersLike), and handle the NullableHeaders shape it actually passes so the merge stays correct, not just type-clean. (vertexClient.ts:182) Also drops the now-stale `@anthropic-ai/vertex-sdk` entries left behind by the dependency removal: - scripts/externals.ts INTENTIONALLY_BUNDLED (P3) - knip.json ignoreDependencies Testing: `tsc --noEmit` clean; vertex/client/gemini tests 51 pass; smoke green (INTENTIONALLY_BUNDLED back in sync, 57 entries); knip clean. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(api): address CodeRabbit review on PR #1232 — auth precedence + coverage - Security (vertexClient.ts): merge resolved Google auth headers LAST so a caller-supplied Authorization / x-goog-user-project can't override the Vertex credential and send the wrong token upstream. Other request headers still pass through unchanged. - Tests (vertexClient.test.ts): add focused regression coverage for the previously-unguarded routing/auth branches — * streaming → :streamRawPredict path (+ model stripped, stream preserved) * count_tokens → count-tokens:rawPredict path rewrite * auth-header precedence: caller Authorization does NOT override the Vertex token (guards the fix above + exercises the NullableHeaders merge branch). Testing: tsc clean; vertexClient tests 5 pass; full src/services/api 840 pass; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(api): validate and encode Vertex model before building the URL CodeRabbit follow-up on PR #1232: `model` was interpolated straight into the Vertex endpoint path, so a missing/non-string model would silently route to `.../models/undefined:rawPredict` instead of failing fast. Now throw a clear error on a missing/empty model and encodeURIComponent the value before building the path. Adds a focused test for the missing-model case. Testing: tsc clean; vertexClient tests 6 pass; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(api): address remaining review items from PR #1232 - [P2] Fix count_tokens method guard: apply method==='post' to both paths - [P3] Remove unused accessToken option from AnthropicVertex - [P3] Narrow batches type on messages/beta resources with Omit * test: add count_tokens?beta=true routing regression test --------- Co-authored-by: Kevin Codex <kevin@gitlawb.com> Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
This commit is contained in:
co-authored by
Kevin Codex
OpenClaude
Gravirei
parent
3eb57c6d13
commit
9c0d5c61e2
@@ -10,7 +10,6 @@
|
||||
"@anthropic-ai/foundry-sdk": "0.2.3",
|
||||
"@anthropic-ai/sandbox-runtime": "0.0.55",
|
||||
"@anthropic-ai/sdk": "0.94.0",
|
||||
"@anthropic-ai/vertex-sdk": "0.16.0",
|
||||
"@commander-js/extra-typings": "12.1.0",
|
||||
"@grpc/grpc-js": "^1.14.3",
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
@@ -37,7 +36,7 @@
|
||||
"figures": "6.1.0",
|
||||
"fuse.js": "7.1.0",
|
||||
"get-east-asian-width": "1.5.0",
|
||||
"google-auth-library": "9.15.1",
|
||||
"google-auth-library": "10.6.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"ignore": "7.0.5",
|
||||
"indent-string": "5.0.0",
|
||||
@@ -79,6 +78,7 @@
|
||||
},
|
||||
},
|
||||
"overrides": {
|
||||
"google-auth-library": "10.6.2",
|
||||
"ip-address": "10.2.0",
|
||||
"lodash-es": "4.18.1",
|
||||
},
|
||||
@@ -93,8 +93,6 @@
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.94.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-OVlCttk5MyeTGtrWX5+F3MJOfEMDuEjK8+rm9aQMDfRPWndVMbhk37QG8WLnVbcc7huyUGngVMjT7iMN2llySA=="],
|
||||
|
||||
"@anthropic-ai/vertex-sdk": ["@anthropic-ai/vertex-sdk@0.16.0", "", { "dependencies": { "@anthropic-ai/sdk": ">=0.50.3 <1", "google-auth-library": "^9.4.2" } }, "sha512-ntxemtRkwPsjVzGQJsmBPRW38tfas6VuVlD1v6pHffDJKLPtCdaiN9KUQeraJ/F34tjxEWlsaCnl3t/orJm1Xw=="],
|
||||
|
||||
"@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="],
|
||||
|
||||
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
|
||||
@@ -525,6 +523,8 @@
|
||||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
|
||||
@@ -593,6 +593,8 @@
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||
|
||||
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||
|
||||
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
|
||||
@@ -607,6 +609,8 @@
|
||||
|
||||
"formatly": ["formatly@0.3.0", "", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="],
|
||||
|
||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
|
||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||
@@ -615,9 +619,9 @@
|
||||
|
||||
"fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="],
|
||||
|
||||
"gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
|
||||
"gaxios": ["gaxios@7.1.5", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg=="],
|
||||
|
||||
"gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
|
||||
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
@@ -631,16 +635,14 @@
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="],
|
||||
|
||||
"google-auth-library": ["google-auth-library@9.15.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="],
|
||||
"google-auth-library": ["google-auth-library@10.6.2", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw=="],
|
||||
|
||||
"google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
||||
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
@@ -737,7 +739,9 @@
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||
|
||||
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||
|
||||
"node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
|
||||
|
||||
@@ -887,8 +891,6 @@
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||
|
||||
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
|
||||
|
||||
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||
@@ -915,8 +917,6 @@
|
||||
|
||||
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
|
||||
|
||||
"uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
|
||||
@@ -927,9 +927,7 @@
|
||||
|
||||
"walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
@@ -1219,8 +1217,6 @@
|
||||
|
||||
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"knip/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
|
||||
|
||||
"knip/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
],
|
||||
"ignoreDependencies": [
|
||||
"@types/bun",
|
||||
"@anthropic-ai/foundry-sdk",
|
||||
"@anthropic-ai/vertex-sdk"
|
||||
"@anthropic-ai/foundry-sdk"
|
||||
]
|
||||
}
|
||||
|
||||
+2
-2
@@ -79,7 +79,6 @@
|
||||
"@anthropic-ai/foundry-sdk": "0.2.3",
|
||||
"@anthropic-ai/sandbox-runtime": "0.0.55",
|
||||
"@anthropic-ai/sdk": "0.94.0",
|
||||
"@anthropic-ai/vertex-sdk": "0.16.0",
|
||||
"@commander-js/extra-typings": "12.1.0",
|
||||
"@grpc/grpc-js": "^1.14.3",
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
@@ -106,7 +105,7 @@
|
||||
"figures": "6.1.0",
|
||||
"fuse.js": "7.1.0",
|
||||
"get-east-asian-width": "1.5.0",
|
||||
"google-auth-library": "9.15.1",
|
||||
"google-auth-library": "10.6.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"ignore": "7.0.5",
|
||||
"indent-string": "5.0.0",
|
||||
@@ -168,6 +167,7 @@
|
||||
},
|
||||
"overrides": {
|
||||
"ip-address": "10.2.0",
|
||||
"google-auth-library": "10.6.2",
|
||||
"lodash-es": "4.18.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ export const INTENTIONALLY_BUNDLED: string[] = [
|
||||
'@anthropic-ai/bedrock-sdk',
|
||||
'@anthropic-ai/foundry-sdk',
|
||||
'@anthropic-ai/sandbox-runtime',
|
||||
'@anthropic-ai/vertex-sdk',
|
||||
// CLI / TUI utilities
|
||||
'@alcalzone/ansi-tokenize',
|
||||
'@commander-js/extra-typings',
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
shouldUseFirstPartyAnthropicAuth,
|
||||
type ProviderOverride,
|
||||
} from './authRouting.js'
|
||||
import { AnthropicVertex } from './vertexClient.js'
|
||||
|
||||
const importRuntimeModule = new Function(
|
||||
'specifier',
|
||||
@@ -564,10 +565,7 @@ export async function getAnthropicClient({
|
||||
await refreshGcpCredentialsIfNeeded()
|
||||
}
|
||||
|
||||
const [{ AnthropicVertex }, { GoogleAuth }] = await Promise.all([
|
||||
importRuntimeModule('@anthropic-ai/vertex-sdk'),
|
||||
importRuntimeModule('google-auth-library'),
|
||||
])
|
||||
const { GoogleAuth } = await importRuntimeModule('google-auth-library')
|
||||
// TODO: Cache either GoogleAuth instance or AuthClient to improve performance
|
||||
// Currently we create a new GoogleAuth instance for every getAnthropicClient() call
|
||||
// This could cause repeated authentication flows and metadata server checks
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
import { expect, test } from 'bun:test'
|
||||
|
||||
import { AnthropicVertex } from './vertexClient.js'
|
||||
|
||||
test('routes message requests through Vertex rawPredict with auth headers', async () => {
|
||||
let capturedUrl: string | undefined
|
||||
let capturedHeaders: Headers | undefined
|
||||
let capturedBody: Record<string, unknown> | undefined
|
||||
|
||||
const client = new AnthropicVertex({
|
||||
region: 'us-east5',
|
||||
authClient: {
|
||||
getRequestHeaders: () =>
|
||||
new Headers({
|
||||
Authorization: 'Bearer vertex-token',
|
||||
'x-goog-user-project': 'vertex-project',
|
||||
}),
|
||||
},
|
||||
maxRetries: 0,
|
||||
fetch: (async (input, init) => {
|
||||
capturedUrl =
|
||||
typeof input === 'string'
|
||||
? input
|
||||
: input instanceof URL
|
||||
? input.toString()
|
||||
: input.url
|
||||
capturedHeaders = new Headers(init?.headers)
|
||||
capturedBody = JSON.parse(String(init?.body)) as Record<string, unknown>
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
id: 'msg_vertex',
|
||||
type: 'message',
|
||||
role: 'assistant',
|
||||
model: 'claude-sonnet-4-6',
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
stop_reason: 'end_turn',
|
||||
stop_sequence: null,
|
||||
usage: {
|
||||
input_tokens: 1,
|
||||
output_tokens: 1,
|
||||
},
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
)
|
||||
}) as typeof fetch,
|
||||
})
|
||||
|
||||
const response = await client.messages.create({
|
||||
model: 'claude-sonnet-4-6',
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
max_tokens: 64,
|
||||
})
|
||||
|
||||
expect(capturedUrl).toBe(
|
||||
'https://us-east5-aiplatform.googleapis.com/v1/projects/vertex-project/locations/us-east5/publishers/anthropic/models/claude-sonnet-4-6:rawPredict',
|
||||
)
|
||||
expect(capturedHeaders?.get('authorization')).toBe('Bearer vertex-token')
|
||||
expect(capturedBody?.anthropic_version).toBe('vertex-2023-10-16')
|
||||
expect(capturedBody).not.toHaveProperty('model')
|
||||
expect(response).toMatchObject({
|
||||
id: 'msg_vertex',
|
||||
role: 'assistant',
|
||||
model: 'claude-sonnet-4-6',
|
||||
})
|
||||
})
|
||||
|
||||
test('requires an explicit Vertex auth provider', () => {
|
||||
expect(() => new AnthropicVertex({ region: 'us-east5' })).toThrow(
|
||||
'A `googleAuth` or `authClient` option is required.',
|
||||
)
|
||||
})
|
||||
|
||||
// ─── Regression coverage for the remaining routing/auth branches ──────
|
||||
|
||||
type Captured = {
|
||||
url?: string
|
||||
headers?: Headers
|
||||
body?: Record<string, unknown>
|
||||
}
|
||||
|
||||
const MESSAGE_PAYLOAD = {
|
||||
id: 'msg_vertex',
|
||||
type: 'message',
|
||||
role: 'assistant',
|
||||
model: 'claude-sonnet-4-6',
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
stop_reason: 'end_turn',
|
||||
stop_sequence: null,
|
||||
usage: { input_tokens: 1, output_tokens: 1 },
|
||||
}
|
||||
|
||||
function captureRequest(capture: Captured, init: RequestInit | undefined, input: unknown): void {
|
||||
capture.url =
|
||||
typeof input === 'string'
|
||||
? input
|
||||
: input instanceof URL
|
||||
? input.toString()
|
||||
: (input as Request).url
|
||||
capture.headers = new Headers(init?.headers)
|
||||
if (init?.body) {
|
||||
capture.body = JSON.parse(String(init.body)) as Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
function jsonFetch(capture: Captured, payload: unknown): typeof fetch {
|
||||
return (async (input, init) => {
|
||||
captureRequest(capture, init, input)
|
||||
return new Response(JSON.stringify(payload), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
}) as typeof fetch
|
||||
}
|
||||
|
||||
function sseFetch(capture: Captured): typeof fetch {
|
||||
const events = [
|
||||
{ event: 'message_start', data: { type: 'message_start', message: MESSAGE_PAYLOAD } },
|
||||
{ event: 'content_block_start', data: { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } } },
|
||||
{ event: 'content_block_delta', data: { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'ok' } } },
|
||||
{ event: 'content_block_stop', data: { type: 'content_block_stop', index: 0 } },
|
||||
{ event: 'message_delta', data: { type: 'message_delta', delta: { stop_reason: 'end_turn', stop_sequence: null }, usage: { output_tokens: 1 } } },
|
||||
{ event: 'message_stop', data: { type: 'message_stop' } },
|
||||
]
|
||||
const sse = events.map(e => `event: ${e.event}\ndata: ${JSON.stringify(e.data)}\n\n`).join('')
|
||||
return (async (input, init) => {
|
||||
captureRequest(capture, init, input)
|
||||
return new Response(sse, { headers: { 'Content-Type': 'text/event-stream' } })
|
||||
}) as typeof fetch
|
||||
}
|
||||
|
||||
function makeClient(
|
||||
fetchImpl: typeof fetch,
|
||||
extraAuthHeaders?: Record<string, string>,
|
||||
): AnthropicVertex {
|
||||
return new AnthropicVertex({
|
||||
region: 'us-east5',
|
||||
authClient: {
|
||||
getRequestHeaders: () =>
|
||||
new Headers({
|
||||
Authorization: 'Bearer vertex-token',
|
||||
'x-goog-user-project': 'vertex-project',
|
||||
...extraAuthHeaders,
|
||||
}),
|
||||
},
|
||||
maxRetries: 0,
|
||||
fetch: fetchImpl,
|
||||
})
|
||||
}
|
||||
|
||||
test('routes streaming requests through Vertex streamRawPredict', async () => {
|
||||
const capture: Captured = {}
|
||||
const client = makeClient(sseFetch(capture))
|
||||
|
||||
const stream = await client.messages.create({
|
||||
model: 'claude-sonnet-4-6',
|
||||
messages: [{ role: 'user', content: 'hi' }],
|
||||
max_tokens: 64,
|
||||
stream: true,
|
||||
})
|
||||
|
||||
const events: unknown[] = []
|
||||
for await (const event of stream) {
|
||||
events.push(event)
|
||||
}
|
||||
|
||||
expect(capture.url).toBe(
|
||||
'https://us-east5-aiplatform.googleapis.com/v1/projects/vertex-project/locations/us-east5/publishers/anthropic/models/claude-sonnet-4-6:streamRawPredict',
|
||||
)
|
||||
expect(capture.body).not.toHaveProperty('model')
|
||||
expect(capture.body?.stream).toBe(true)
|
||||
expect(events.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('rewrites count_tokens to the Vertex count-tokens:rawPredict endpoint', async () => {
|
||||
const capture: Captured = {}
|
||||
const client = makeClient(jsonFetch(capture, { input_tokens: 42 }))
|
||||
|
||||
const result = await client.messages.countTokens({
|
||||
model: 'claude-sonnet-4-6',
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
})
|
||||
|
||||
expect(capture.url).toBe(
|
||||
'https://us-east5-aiplatform.googleapis.com/v1/projects/vertex-project/locations/us-east5/publishers/anthropic/models/count-tokens:rawPredict',
|
||||
)
|
||||
expect(capture.body?.anthropic_version).toBe('vertex-2023-10-16')
|
||||
expect(result).toMatchObject({ input_tokens: 42 })
|
||||
})
|
||||
|
||||
test('resolved Vertex auth headers win over caller-supplied Authorization', async () => {
|
||||
const capture: Captured = {}
|
||||
const client = makeClient(jsonFetch(capture, MESSAGE_PAYLOAD))
|
||||
|
||||
await client.messages.create(
|
||||
{
|
||||
model: 'claude-sonnet-4-6',
|
||||
messages: [{ role: 'user', content: 'hi' }],
|
||||
max_tokens: 64,
|
||||
},
|
||||
{ headers: { Authorization: 'Bearer caller-should-not-win' } },
|
||||
)
|
||||
|
||||
// The Google auth credential must not be overridable by caller headers.
|
||||
expect(capture.headers?.get('authorization')).toBe('Bearer vertex-token')
|
||||
})
|
||||
|
||||
test('rewrites count_tokens?beta=true through the same Vertex endpoint', async () => {
|
||||
const client = new AnthropicVertex({
|
||||
region: 'us-east5',
|
||||
projectId: 'vertex-project',
|
||||
authClient: { getRequestHeaders: () => new Headers({ Authorization: 'Bearer t' }) },
|
||||
maxRetries: 0,
|
||||
})
|
||||
|
||||
const options = {
|
||||
method: 'post',
|
||||
path: '/v1/messages/count_tokens?beta=true',
|
||||
body: { messages: [{ role: 'user', content: 'hi' }] },
|
||||
} as Parameters<typeof client.buildRequest>[0]
|
||||
|
||||
await client.buildRequest(options)
|
||||
|
||||
expect(options.path).toBe(
|
||||
'/projects/vertex-project/locations/us-east5/publishers/anthropic/models/count-tokens:rawPredict',
|
||||
)
|
||||
expect((options.body as Record<string, unknown>)?.anthropic_version).toBe('vertex-2023-10-16')
|
||||
})
|
||||
|
||||
test('rejects a message request with a missing/invalid model', async () => {
|
||||
const client = new AnthropicVertex({
|
||||
region: 'us-east5',
|
||||
projectId: 'vertex-project',
|
||||
authClient: { getRequestHeaders: () => new Headers() },
|
||||
maxRetries: 0,
|
||||
})
|
||||
|
||||
await expect(
|
||||
client.buildRequest({
|
||||
method: 'post',
|
||||
path: '/v1/messages',
|
||||
body: { messages: [], max_tokens: 1 },
|
||||
} as Parameters<typeof client.buildRequest>[0]),
|
||||
).rejects.toThrow('Expected `model` to be a non-empty string')
|
||||
})
|
||||
@@ -0,0 +1,286 @@
|
||||
import { BaseAnthropic, type ClientOptions } from '@anthropic-ai/sdk/client'
|
||||
import * as Resources from '@anthropic-ai/sdk/resources/index'
|
||||
|
||||
const DEFAULT_VERSION = 'vertex-2023-10-16'
|
||||
const MODEL_ENDPOINTS = new Set(['/v1/messages', '/v1/messages?beta=true'])
|
||||
|
||||
type VertexAuthHeaders = HeadersInit | Record<string, string | undefined>
|
||||
|
||||
// The base client passes its already-parsed request headers into prepareOptions
|
||||
// (HeadersLike, which includes the internal NullableHeaders wrapper). Accept
|
||||
// that shape in the merge helpers in addition to the google-auth header shapes
|
||||
// so the types line up with what the SDK actually hands us.
|
||||
type RequestHeaders = Parameters<BaseAnthropic['prepareOptions']>[0]['headers']
|
||||
|
||||
type VertexAuthClient = {
|
||||
projectId?: string | null
|
||||
getRequestHeaders: () => VertexAuthHeaders | Promise<VertexAuthHeaders>
|
||||
}
|
||||
|
||||
type VertexGoogleAuth = {
|
||||
getClient: () => VertexAuthClient | Promise<VertexAuthClient>
|
||||
}
|
||||
|
||||
type AnthropicVertexOptions = Omit<ClientOptions, 'baseURL'> & {
|
||||
authClient?: VertexAuthClient
|
||||
baseURL?: string | null
|
||||
googleAuth?: VertexGoogleAuth
|
||||
projectId?: string | null
|
||||
region?: string | null
|
||||
}
|
||||
|
||||
function readEnv(name: string): string | undefined {
|
||||
return typeof process === 'undefined' ? undefined : process.env[name]
|
||||
}
|
||||
|
||||
function isObj(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function setHeader(
|
||||
target: Headers,
|
||||
key: string,
|
||||
value: string | undefined | null,
|
||||
): void {
|
||||
if (value !== undefined && value !== null) {
|
||||
target.set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
function appendHeaders(
|
||||
target: Headers,
|
||||
source: VertexAuthHeaders | RequestHeaders,
|
||||
): void {
|
||||
if (!source) {
|
||||
return
|
||||
}
|
||||
|
||||
if (source instanceof Headers) {
|
||||
source.forEach((value, key) => target.set(key, value))
|
||||
return
|
||||
}
|
||||
|
||||
// NullableHeaders (the base client's parsed request headers):
|
||||
// { values: Headers, nulls: Set<string> } — copy values, honor explicit unsets.
|
||||
if (
|
||||
typeof source === 'object' &&
|
||||
!Array.isArray(source) &&
|
||||
'values' in source &&
|
||||
source.values instanceof Headers
|
||||
) {
|
||||
source.values.forEach((value, key) => target.set(key, value))
|
||||
const nulls = (source as { nulls?: unknown }).nulls
|
||||
if (nulls instanceof Set) {
|
||||
for (const key of nulls as Set<string>) {
|
||||
target.delete(key)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(source)) {
|
||||
for (const [key, value] of source) {
|
||||
setHeader(target, key as string, value as string | undefined | null)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
if (Array.isArray(value)) {
|
||||
for (const v of value) {
|
||||
setHeader(target, key, v as string | undefined | null)
|
||||
}
|
||||
} else {
|
||||
setHeader(target, key, value as string | undefined | null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeHeaders(
|
||||
...sources: (VertexAuthHeaders | RequestHeaders)[]
|
||||
): Headers {
|
||||
const headers = new Headers()
|
||||
for (const source of sources) {
|
||||
appendHeaders(headers, source)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
function getHeaderValue(
|
||||
source: VertexAuthHeaders,
|
||||
headerName: string,
|
||||
): string | undefined {
|
||||
if (source instanceof Headers) {
|
||||
return source.get(headerName) ?? undefined
|
||||
}
|
||||
|
||||
const normalizedName = headerName.toLowerCase()
|
||||
if (Array.isArray(source)) {
|
||||
for (const [key, value] of source) {
|
||||
if (key.toLowerCase() === normalizedName) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
if (key.toLowerCase() === normalizedName) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export class AnthropicVertex extends BaseAnthropic {
|
||||
// Re-declare the resource surface that the upstream @anthropic-ai/vertex-sdk
|
||||
// client exposed. BaseAnthropic does not declare these, so typed consumers
|
||||
// (client.ts, the SDK calling `.messages`) would otherwise lose the types.
|
||||
messages: Omit<Resources.Messages, 'batches'>
|
||||
beta: Omit<Resources.Beta, 'messages'> & { messages: Omit<Resources.Beta.Messages, 'batches'> }
|
||||
region: string
|
||||
projectId: string | null
|
||||
private readonly authClientPromise: Promise<VertexAuthClient>
|
||||
|
||||
constructor({
|
||||
authClient,
|
||||
baseURL = readEnv('ANTHROPIC_VERTEX_BASE_URL'),
|
||||
googleAuth,
|
||||
projectId = readEnv('ANTHROPIC_VERTEX_PROJECT_ID') ?? null,
|
||||
region = readEnv('CLOUD_ML_REGION') ?? null,
|
||||
...opts
|
||||
}: AnthropicVertexOptions = {}) {
|
||||
if (!region) {
|
||||
throw new Error(
|
||||
'No region was given. The client should be instantiated with the `region` option or the `CLOUD_ML_REGION` environment variable should be set.',
|
||||
)
|
||||
}
|
||||
|
||||
let resolvedBaseURL = baseURL
|
||||
if (!resolvedBaseURL) {
|
||||
switch (region) {
|
||||
case 'global':
|
||||
resolvedBaseURL = 'https://aiplatform.googleapis.com/v1'
|
||||
break
|
||||
case 'us':
|
||||
resolvedBaseURL = 'https://aiplatform.us.rep.googleapis.com/v1'
|
||||
break
|
||||
case 'eu':
|
||||
resolvedBaseURL = 'https://aiplatform.eu.rep.googleapis.com/v1'
|
||||
break
|
||||
default:
|
||||
resolvedBaseURL = `https://${region}-aiplatform.googleapis.com/v1`
|
||||
}
|
||||
}
|
||||
|
||||
super({
|
||||
baseURL: resolvedBaseURL,
|
||||
...opts,
|
||||
})
|
||||
|
||||
this.messages = makeMessagesResource(this)
|
||||
this.beta = makeBetaResource(this)
|
||||
this.region = region
|
||||
this.projectId = projectId
|
||||
|
||||
if (authClient && googleAuth) {
|
||||
throw new Error(
|
||||
'You cannot provide both `authClient` and `googleAuth`. Please provide only one of them.',
|
||||
)
|
||||
}
|
||||
|
||||
if (authClient) {
|
||||
this.authClientPromise = Promise.resolve(authClient)
|
||||
} else if (googleAuth) {
|
||||
this.authClientPromise = Promise.resolve(googleAuth.getClient())
|
||||
} else {
|
||||
throw new Error('A `googleAuth` or `authClient` option is required.')
|
||||
}
|
||||
}
|
||||
|
||||
override validateHeaders(): void {
|
||||
// Vertex auth headers are resolved asynchronously in prepareOptions.
|
||||
}
|
||||
|
||||
override async prepareOptions(
|
||||
options: Parameters<BaseAnthropic['prepareOptions']>[0],
|
||||
): Promise<void> {
|
||||
const authClient = await this.authClientPromise
|
||||
const authHeaders = await authClient.getRequestHeaders()
|
||||
const projectId =
|
||||
authClient.projectId ?? getHeaderValue(authHeaders, 'x-goog-user-project')
|
||||
|
||||
if (!this.projectId && projectId) {
|
||||
this.projectId = projectId
|
||||
}
|
||||
|
||||
// Resolved Google auth headers MUST win: merge them last so a caller-supplied
|
||||
// Authorization / x-goog-user-project can't override the Vertex credential
|
||||
// and send the wrong token upstream. Other request headers still pass through.
|
||||
options.headers = mergeHeaders(options.headers, authHeaders)
|
||||
}
|
||||
|
||||
override async buildRequest(
|
||||
options: Parameters<BaseAnthropic['buildRequest']>[0],
|
||||
): ReturnType<BaseAnthropic['buildRequest']> {
|
||||
if (isObj(options.body)) {
|
||||
options.body = { ...options.body }
|
||||
}
|
||||
|
||||
if (isObj(options.body) && !options.body['anthropic_version']) {
|
||||
options.body['anthropic_version'] = DEFAULT_VERSION
|
||||
}
|
||||
|
||||
if (MODEL_ENDPOINTS.has(options.path) && options.method === 'post') {
|
||||
if (!this.projectId) {
|
||||
throw new Error(
|
||||
'No projectId was given and it could not be resolved from credentials. The client should be instantiated with the `projectId` option or the `ANTHROPIC_VERTEX_PROJECT_ID` environment variable should be set.',
|
||||
)
|
||||
}
|
||||
|
||||
if (!isObj(options.body)) {
|
||||
throw new Error('Expected request body to be an object for post /v1/messages')
|
||||
}
|
||||
|
||||
const model = options.body['model']
|
||||
if (typeof model !== 'string' || model.trim() === '') {
|
||||
throw new Error(
|
||||
'Expected `model` to be a non-empty string for post /v1/messages',
|
||||
)
|
||||
}
|
||||
delete options.body['model']
|
||||
const stream = options.body['stream'] ?? false
|
||||
const specifier = stream ? 'streamRawPredict' : 'rawPredict'
|
||||
options.path = `/projects/${this.projectId}/locations/${this.region}/publishers/anthropic/models/${encodeURIComponent(model)}:${specifier}`
|
||||
}
|
||||
|
||||
if (
|
||||
options.method === 'post' &&
|
||||
(options.path === '/v1/messages/count_tokens' ||
|
||||
options.path === '/v1/messages/count_tokens?beta=true')
|
||||
) {
|
||||
if (!this.projectId) {
|
||||
throw new Error(
|
||||
'No projectId was given and it could not be resolved from credentials. The client should be instantiated with the `projectId` option or the `ANTHROPIC_VERTEX_PROJECT_ID` environment variable should be set.',
|
||||
)
|
||||
}
|
||||
|
||||
options.path = `/projects/${this.projectId}/locations/${this.region}/publishers/anthropic/models/count-tokens:rawPredict`
|
||||
}
|
||||
|
||||
return super.buildRequest(options)
|
||||
}
|
||||
}
|
||||
|
||||
function makeMessagesResource(client: BaseAnthropic): Resources.Messages {
|
||||
const resource = new Resources.Messages(client)
|
||||
delete (resource as Partial<Resources.Messages>).batches
|
||||
return resource
|
||||
}
|
||||
|
||||
function makeBetaResource(client: BaseAnthropic): Resources.Beta {
|
||||
const resource = new Resources.Beta(client)
|
||||
delete (resource.messages as Partial<Resources.Beta.Messages>).batches
|
||||
return resource
|
||||
}
|
||||
Reference in New Issue
Block a user