diff --git a/.env.example b/.env.example index 84b44f4..542281a 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,15 @@ # ANTHROPIC_MAX_TOKENS=4096 # ANTHROPIC_TEMPERATURE=0.2 +# Option 3: Amazon Bedrock +# LLM_PROVIDER=amazon-bedrock +# # Credentials optional if running in AWS environment with a native credential provider +# # AWS_ACCESS_KEY_ID=your-aws-access-key-id +# # AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key +# AWS_REGION=us-east-1 +# BEDROCK_MODEL=global.anthropic.claude-sonnet-4-5-20250929-v1:0 +# BEDROCK_TEMPERATURE=0.2 + # Search Provider Configuration diff --git a/package-lock.json b/package-lock.json index 421f5b3..4db4783 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,13 +9,14 @@ "version": "2.3.0", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/anthropic": "^1.0.0", - "@ai-sdk/azure": "^3.0.31", - "@ai-sdk/google": "^1.0.0", - "@ai-sdk/openai": "^1.0.0", + "@ai-sdk/amazon-bedrock": "^4.0.64", + "@ai-sdk/anthropic": "^3.0.47", + "@ai-sdk/azure": "^3.0.34", + "@ai-sdk/google": "^3.0.31", + "@ai-sdk/openai": "^3.0.33", "@ai-sdk/perplexity": "^1.0.0", "@types/micromatch": "^4.0.9", - "ai": "^4.0.0", + "ai": "^6.0.99", "chalk": "^5.3.0", "commander": "^12.0.0", "fast-glob": "^3.3.2", @@ -51,29 +52,61 @@ "node": ">=18.0.0" } }, - "node_modules/@ai-sdk/anthropic": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-1.2.12.tgz", - "integrity": "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==", + "node_modules/@ai-sdk/amazon-bedrock": { + "version": "4.0.64", + "resolved": "https://registry.npmjs.org/@ai-sdk/amazon-bedrock/-/amazon-bedrock-4.0.64.tgz", + "integrity": "sha512-foZskBdIHPGPZLfEKqkOP18JgfMy5Duik1TTduvEQ97mX3yfEzr4go1V3oU7ktry0dAL5BiNY38WwRhl+xhy9g==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8" + "@ai-sdk/anthropic": "3.0.47", + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15", + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "aws4fetch": "^1.0.20" }, "engines": { "node": ">=18" }, "peerDependencies": { - "zod": "^3.0.0" + "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/azure": { - "version": "3.0.31", - "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-3.0.31.tgz", - "integrity": "sha512-W9x6nt+yf+Ns0/Wx7U9TXHLmfu7mOUqy1b/drtVd3DvNfDudyruQM/YjM2268Q0FatSrPlA2RlnPVPGRH/4V8Q==", + "node_modules/@ai-sdk/amazon-bedrock/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/amazon-bedrock/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/anthropic": { + "version": "3.0.47", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.47.tgz", + "integrity": "sha512-E6Z3i/xvxGDxRskMMbuX9+xDK4l5LesrP2O7YQ0CcbAkYP25qTo/kYGf/AsJrLkNIY23HeO/kheUWtG1XZllDA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/openai": "3.0.30", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15" }, @@ -84,12 +117,42 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/azure/node_modules/@ai-sdk/openai": { - "version": "3.0.30", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.30.tgz", - "integrity": "sha512-YDht3t7TDyWKP+JYZp20VuYqSjyF2brHYh47GGFDUPf2wZiqNQ263ecL+quar2bP3GZ3BeQA8f0m2B7UwLPR+g==", + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", "license": "Apache-2.0", "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/azure": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-3.0.34.tgz", + "integrity": "sha512-nnOFtgvZYOa6XIeAm18i56NX77Yu4Bd+Tnbt85LGUEqwJFR54kFTlR1nm3BAJCphHrmQteJd1P3QErtyoXig8A==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/openai": "3.0.33", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15" }, @@ -129,36 +192,140 @@ "zod": "^3.25.76 || ^4.1.8" } }, + "node_modules/@ai-sdk/gateway": { + "version": "3.0.55", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.55.tgz", + "integrity": "sha512-7xMeTJnCjwRwXKVCiv4Ly4qzWvDuW3+W1WIV0X1EFu6W83d4mEhV9bFArto10MeTw40ewuDjrbrZd21mXKohkw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15", + "@vercel/oidc": "3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, "node_modules/@ai-sdk/google": { - "version": "1.2.22", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-1.2.22.tgz", - "integrity": "sha512-Ppxu3DIieF1G9pyQ5O1Z646GYR0gkC57YdBqXJ82qvCdhEhZHu0TWhmnOoeIWe2olSbuDeoOY+MfJrW8dzS3Hw==", + "version": "3.0.31", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-3.0.31.tgz", + "integrity": "sha512-RVNz8WFSIRbXbYDBE6JvlE2escWPJimBCs22LzKEYH7DNfl/X7cHNa1LFho4PsY6Ib0JmbzB8s2+i0wHs/wNCg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8" + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15" }, "engines": { "node": ">=18" }, "peerDependencies": { - "zod": "^3.0.0" + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@ai-sdk/openai": { - "version": "1.3.24", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.3.24.tgz", - "integrity": "sha512-GYXnGJTHRTZc4gJMSmFRgEQudjqd4PUN0ZjQhPwOAYH1yOAvQoG/Ikqs+HyISRbLPCrhbZnPKCNHuRU4OfpW0Q==", + "version": "3.0.33", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.33.tgz", + "integrity": "sha512-O/8SVKAiwFHkGAUfBnrLb7L2IjbpP9ySWbmOktOfa0KtzutZkmKNrJ5CtB5dj+lwuENbOuZeRsnsZdOjar7hig==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8" + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15" }, "engines": { "node": ">=18" }, "peerDependencies": { - "zod": "^3.0.0" + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@ai-sdk/perplexity": { @@ -206,59 +373,94 @@ "zod": "^3.23.8" } }, - "node_modules/@ai-sdk/react": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", - "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/ui-utils": "1.2.11", - "swr": "^2.2.5", - "throttleit": "2.1.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" + "node": ">=6.0.0" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@ai-sdk/ui-utils": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", - "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "zod-to-json-schema": "^3.24.1" + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, "engines": { - "node": ">=18" + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "zod": "^3.23.8" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@ampproject/remapping": { + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.2.tgz", + "integrity": "sha512-maTZwGsALtnAw4TJr/S6yERAosTwPduu0XhUV+SdbvRZtCOgSgk1ttL2R0XYzvkYSpvbtJocn77tBXq2AKglBw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@babel/helper-string-parser": { @@ -1469,6 +1671,83 @@ "win32" ] }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.10.tgz", + "integrity": "sha512-A4ynrsFFfSXUHicfTcRehytppFBcY3HQxEGYiyGktPIOye3Ot7fxpiy4VR42WmtGI4Wfo6OXt/c1Ky1nUFxYYQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.1.tgz", + "integrity": "sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz", + "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.1.tgz", + "integrity": "sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.1.tgz", + "integrity": "sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.1.tgz", + "integrity": "sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -1492,12 +1771,6 @@ "integrity": "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==", "license": "MIT" }, - "node_modules/@types/diff-match-patch": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", - "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2058,6 +2331,15 @@ "win32" ] }, + "node_modules/@vercel/oidc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.1.0.tgz", + "integrity": "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, "node_modules/@vitest/coverage-v8": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", @@ -2228,29 +2510,50 @@ } }, "node_modules/ai": { - "version": "4.3.19", - "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.19.tgz", - "integrity": "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==", + "version": "6.0.99", + "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.99.tgz", + "integrity": "sha512-Zg43DDJLppe22e7IWXNwpgtxR2VRFyVJSBUZNlCz2jmyzRgaHzBqINkoy6WIakyD75LOqLCQdGMOGAqTnfO3Aw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/react": "1.2.12", - "@ai-sdk/ui-utils": "1.2.11", - "@opentelemetry/api": "1.9.0", - "jsondiffpatch": "0.6.0" + "@ai-sdk/gateway": "3.0.55", + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15", + "@opentelemetry/api": "1.9.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ai/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" }, - "peerDependenciesMeta": { - "react": { - "optional": true - } + "engines": { + "node": ">=18" + } + }, + "node_modules/ai/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/ajv": { @@ -2322,6 +2625,12 @@ "node": ">=12" } }, + "node_modules/aws4fetch": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", + "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", + "license": "MIT" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2694,21 +3003,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", - "license": "Apache-2.0" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3862,23 +4156,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsondiffpatch": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", - "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", - "license": "MIT", - "dependencies": { - "@types/diff-match-patch": "^1.0.36", - "chalk": "^5.3.0", - "diff-match-patch": "^1.0.5" - }, - "bin": { - "jsondiffpatch": "bin/jsondiffpatch.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4457,16 +4734,6 @@ ], "license": "MIT" }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4944,19 +5211,6 @@ "node": ">=8" } }, - "node_modules/swr": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.4.0.tgz", - "integrity": "sha512-sUlC20T8EOt1pHmDiqueUWMmRRX03W7w5YxovWX7VR2KHEPCTMly85x05vpkP5i6Bu4h44ePSMD9Tc+G2MItFw==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -5035,18 +5289,6 @@ "node": ">=0.8" } }, - "node_modules/throttleit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", - "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -5221,9 +5463,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/tsup": { "version": "8.5.0", @@ -5449,15 +5689,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", @@ -6208,15 +6439,6 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } } } } diff --git a/package.json b/package.json index 2b6f7d2..b7c817a 100644 --- a/package.json +++ b/package.json @@ -54,13 +54,14 @@ "node": ">=18.0.0" }, "dependencies": { - "@ai-sdk/anthropic": "^1.0.0", - "@ai-sdk/azure": "^3.0.31", - "@ai-sdk/google": "^1.0.0", - "@ai-sdk/openai": "^1.0.0", + "@ai-sdk/amazon-bedrock": "^4.0.64", + "@ai-sdk/anthropic": "^3.0.47", + "@ai-sdk/azure": "^3.0.34", + "@ai-sdk/google": "^3.0.31", + "@ai-sdk/openai": "^3.0.33", "@ai-sdk/perplexity": "^1.0.0", "@types/micromatch": "^4.0.9", - "ai": "^4.0.0", + "ai": "^6.0.99", "chalk": "^5.3.0", "commander": "^12.0.0", "fast-glob": "^3.3.2", diff --git a/src/config/global-config.ts b/src/config/global-config.ts index 0d813ca..98b1a7f 100644 --- a/src/config/global-config.ts +++ b/src/config/global-config.ts @@ -47,6 +47,15 @@ const DEFAULT_GLOBAL_CONFIG_TEMPLATE = `# VectorLint Environment Configuration # GEMINI_MODEL = "gemini-2.5-pro" # GEMINI_TEMPERATURE = "0.2" +# --- Option 5: Amazon Bedrock --- +# LLM_PROVIDER = "amazon-bedrock" +# # Credentials optional if running in AWS environment with a native credential provider +# # AWS_ACCESS_KEY_ID = "your-aws-access-key-id" +# # AWS_SECRET_ACCESS_KEY = "your-aws-secret-access-key" +# AWS_REGION = "us-east-1" +# BEDROCK_MODEL = "global.anthropic.claude-sonnet-4-5-20250929-v1:0" +# BEDROCK_TEMPERATURE = "0.2" + # ============================================ # Search Provider Configuration (Optional) # Enables technical accuracy verification diff --git a/src/providers/provider-factory.ts b/src/providers/provider-factory.ts index 9feac28..fe13d2d 100644 --- a/src/providers/provider-factory.ts +++ b/src/providers/provider-factory.ts @@ -2,6 +2,7 @@ import { createOpenAI } from '@ai-sdk/openai'; import { createAzure } from '@ai-sdk/azure'; import { createAnthropic } from '@ai-sdk/anthropic'; import { createGoogleGenerativeAI } from '@ai-sdk/google'; +import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'; import type { LanguageModel } from 'ai'; import { LLMProvider } from './llm-provider'; import { VercelAIProvider, type VercelAIConfig } from './vercel-ai-provider'; @@ -19,6 +20,7 @@ export enum ProviderType { Anthropic = 'anthropic', OpenAI = 'openai', Gemini = 'gemini', + AmazonBedrock = 'amazon-bedrock', } /** @@ -78,6 +80,17 @@ export function createProvider( break; } + case ProviderType.AmazonBedrock: { + const bedrock = createAmazonBedrock({ + region: envConfig.AWS_REGION, + ...(envConfig.AWS_ACCESS_KEY_ID && { accessKeyId: envConfig.AWS_ACCESS_KEY_ID }), + ...(envConfig.AWS_SECRET_ACCESS_KEY && { secretAccessKey: envConfig.AWS_SECRET_ACCESS_KEY }), + }); + model = bedrock(envConfig.BEDROCK_MODEL) as unknown as LanguageModel; + temperature = envConfig.BEDROCK_TEMPERATURE ?? 0.2; + break; + } + default: // TypeScript should prevent this, but add runtime safety throw new Error(`Unsupported provider type: ${(envConfig as { LLM_PROVIDER: string }).LLM_PROVIDER}`); diff --git a/src/providers/vercel-ai-provider.ts b/src/providers/vercel-ai-provider.ts index 0350379..b145083 100644 --- a/src/providers/vercel-ai-provider.ts +++ b/src/providers/vercel-ai-provider.ts @@ -66,7 +66,7 @@ export class VercelAIProvider implements LLMProvider { prompt: `Input:\n\n${content}`, ...(this.config.temperature !== undefined && { temperature: this.config.temperature }), ...(this.config.maxTokens !== undefined && { maxTokens: this.config.maxTokens }), - experimental_output: Output.object({ + output: Output.object({ schema: zodSchema, }), }); @@ -74,24 +74,20 @@ export class VercelAIProvider implements LLMProvider { if (this.config.debug && result.usage) { console.log('[vectorlint] LLM response meta:', { usage: { - prompt_tokens: result.usage.promptTokens, - completion_tokens: result.usage.completionTokens, + input_tokens: result.usage.inputTokens, + output_tokens: result.usage.outputTokens, total_tokens: result.usage.totalTokens, }, finish_reason: result.finishReason, }); } - // Map Vercel AI SDK usage (promptTokens/completionTokens) - // to VectorLint TokenUsage (inputTokens/outputTokens) const usage = result.usage ? { - inputTokens: result.usage.promptTokens, - outputTokens: result.usage.completionTokens, + inputTokens: result.usage.inputTokens ?? 0, + outputTokens: result.usage.outputTokens ?? 0, } : undefined; - // experimental_output is validated by the Zod schema passed to Output.object(), - // but can be undefined/null if the LLM response doesn't match the schema - const output: unknown = result.experimental_output; + const output: unknown = result.output; if (output === undefined || output === null) { throw new Error( `LLM returned no structured output. Raw text: ${result.text?.slice(0, 500) ?? '(empty)'}` @@ -104,7 +100,6 @@ export class VercelAIProvider implements LLMProvider { } return llmResult; } catch (e: unknown) { - // Handle Vercel AI SDK's NoObjectGeneratedError with proper type narrowing if (NoObjectGeneratedError.isInstance(e)) { const rawText = e instanceof Error && 'text' in e ? String(e.text) : 'unknown'; throw new Error( diff --git a/src/schemas/env-schemas.ts b/src/schemas/env-schemas.ts index ac849f2..ce2f5a5 100644 --- a/src/schemas/env-schemas.ts +++ b/src/schemas/env-schemas.ts @@ -19,6 +19,10 @@ export const GEMINI_DEFAULT_CONFIG = { model: 'gemini-2.5-flash', }; +export const BEDROCK_DEFAULT_CONFIG = { + model: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0', +}; + // Azure OpenAI configuration schema const AZURE_OPENAI_CONFIG_SCHEMA = z.object({ AZURE_OPENAI_API_KEY: z.string().min(1), @@ -50,6 +54,15 @@ const GEMINI_CONFIG_SCHEMA = z.object({ GEMINI_TEMPERATURE: z.coerce.number().min(0).max(1).optional(), }); +// Amazon Bedrock configuration schema +const BEDROCK_CONFIG_SCHEMA = z.object({ + AWS_ACCESS_KEY_ID: z.string().min(1).optional(), + AWS_SECRET_ACCESS_KEY: z.string().min(1).optional(), + AWS_REGION: z.string().min(1), + BEDROCK_MODEL: z.string().default(BEDROCK_DEFAULT_CONFIG.model), + BEDROCK_TEMPERATURE: z.coerce.number().min(0).max(1).optional(), +}); + // Base environment schema with shared optional variables const BASE_ENV_SCHEMA = z.object({ INPUT_PRICE_PER_MILLION: z.coerce.number().positive().optional(), @@ -62,7 +75,19 @@ export const ENV_SCHEMA = z.discriminatedUnion('LLM_PROVIDER', [ z.object({ LLM_PROVIDER: z.literal(ProviderType.Anthropic) }).merge(ANTHROPIC_CONFIG_SCHEMA).merge(BASE_ENV_SCHEMA), z.object({ LLM_PROVIDER: z.literal(ProviderType.OpenAI) }).merge(OPENAI_CONFIG_SCHEMA).merge(BASE_ENV_SCHEMA), z.object({ LLM_PROVIDER: z.literal(ProviderType.Gemini) }).merge(GEMINI_CONFIG_SCHEMA).merge(BASE_ENV_SCHEMA), -]); + z.object({ LLM_PROVIDER: z.literal(ProviderType.AmazonBedrock) }).merge(BEDROCK_CONFIG_SCHEMA).merge(BASE_ENV_SCHEMA), +]).superRefine((data, ctx) => { + if (data.LLM_PROVIDER === ProviderType.AmazonBedrock) { + const hasKey = data.AWS_ACCESS_KEY_ID !== undefined; + const hasSecret = data.AWS_SECRET_ACCESS_KEY !== undefined; + if (hasKey !== hasSecret) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be provided or both be omitted', + }); + } + } +}); export const GLOBAL_CONFIG_SCHEMA = z.object({ env: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(), @@ -74,3 +99,4 @@ export type AzureOpenAIConfig = z.infer; export type AnthropicConfig = z.infer; export type OpenAIConfig = z.infer; export type GeminiConfig = z.infer; +export type BedrockConfig = z.infer; diff --git a/tests/provider-factory.test.ts b/tests/provider-factory.test.ts index 0d2abb8..133bd92 100644 --- a/tests/provider-factory.test.ts +++ b/tests/provider-factory.test.ts @@ -21,6 +21,10 @@ vi.mock('@ai-sdk/google', () => ({ createGoogleGenerativeAI: vi.fn(() => vi.fn((model: string) => ({ _type: 'google', model }))), })); +vi.mock('@ai-sdk/amazon-bedrock', () => ({ + createAmazonBedrock: vi.fn(() => vi.fn((model: string) => ({ _type: 'bedrock', model }))), +})); + describe('Provider Factory', () => { describe('Provider Instantiation', () => { it('creates VercelAIProvider for Azure OpenAI when configured', () => { @@ -70,6 +74,19 @@ describe('Provider Factory', () => { const provider = createProvider(envConfig); expect(provider).toBeInstanceOf(VercelAIProvider); }); + + it('creates VercelAIProvider for Amazon Bedrock when configured', () => { + const envConfig: EnvConfig = { + LLM_PROVIDER: ProviderType.AmazonBedrock, + AWS_REGION: 'us-west-2', + AWS_ACCESS_KEY_ID: 'test-key', + AWS_SECRET_ACCESS_KEY: 'test-secret', + BEDROCK_MODEL: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0', + }; + + const provider = createProvider(envConfig); + expect(provider).toBeInstanceOf(VercelAIProvider); + }); }); describe('Configuration Mapping', () => { @@ -109,6 +126,17 @@ describe('Provider Factory', () => { expect(() => createProvider(envConfig)).not.toThrow(); }); + it('passes Bedrock configuration correctly', () => { + const envConfig: EnvConfig = { + LLM_PROVIDER: ProviderType.AmazonBedrock, + AWS_REGION: 'eu-central-1', + BEDROCK_MODEL: 'meta.llama3-70b-instruct-v1:0', + BEDROCK_TEMPERATURE: 0.5, + }; + + expect(() => createProvider(envConfig)).not.toThrow(); + }); + it('passes debug options to provider', () => { const envConfig: EnvConfig = { LLM_PROVIDER: ProviderType.OpenAI, @@ -187,13 +215,20 @@ describe('Provider Factory', () => { GEMINI_MODEL: 'gemini-2.5-flash', }; + const bedrockConfig: EnvConfig = { + LLM_PROVIDER: ProviderType.AmazonBedrock, + AWS_REGION: 'us-east-1', + BEDROCK_MODEL: 'amazon.titan-text-express-v1', + }; + const azureProvider = createProvider(azureConfig); const anthropicProvider = createProvider(anthropicConfig); const openaiProvider = createProvider(openaiConfig); const geminiProvider = createProvider(geminiConfig); + const bedrockProvider = createProvider(bedrockConfig); // All should implement the LLMProvider interface - for (const provider of [azureProvider, anthropicProvider, openaiProvider, geminiProvider]) { + for (const provider of [azureProvider, anthropicProvider, openaiProvider, geminiProvider, bedrockProvider]) { expect(provider).toHaveProperty('runPromptStructured'); expect(typeof provider.runPromptStructured).toBe('function'); } @@ -263,6 +298,12 @@ describe('Provider Factory', () => { GEMINI_MODEL: 'gemini-2.5-flash', }; + const bedrockConfig: EnvConfig = { + LLM_PROVIDER: ProviderType.AmazonBedrock, + AWS_REGION: 'us-east-1', + BEDROCK_MODEL: 'amazon.titan-text-express-v1', + }; + const allOptions = { debug: true, showPrompt: true, @@ -273,6 +314,7 @@ describe('Provider Factory', () => { expect(() => createProvider(anthropicConfig, allOptions)).not.toThrow(); expect(() => createProvider(openaiConfig, allOptions)).not.toThrow(); expect(() => createProvider(geminiConfig, allOptions)).not.toThrow(); + expect(() => createProvider(bedrockConfig, allOptions)).not.toThrow(); }); }); @@ -337,5 +379,18 @@ describe('Provider Factory', () => { expect(() => createProvider(envConfig)).not.toThrow(); }); + + it('handles Bedrock specific fields correctly', () => { + const envConfig: EnvConfig = { + LLM_PROVIDER: ProviderType.AmazonBedrock, + AWS_REGION: 'us-east-1', + AWS_ACCESS_KEY_ID: 'test-key', + AWS_SECRET_ACCESS_KEY: 'test-secret', + BEDROCK_MODEL: 'anthropic.claude-3-opus-20240229-v1:0', + BEDROCK_TEMPERATURE: 0.8, + }; + + expect(() => createProvider(envConfig)).not.toThrow(); + }); }); }); diff --git a/tests/vercel-ai-provider.test.ts b/tests/vercel-ai-provider.test.ts index 8e0868d..d65ddfb 100644 --- a/tests/vercel-ai-provider.test.ts +++ b/tests/vercel-ai-provider.test.ts @@ -65,7 +65,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { result: 'ok' } }; + const mockResult = { output: { result: 'ok' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -115,10 +115,10 @@ describe('VercelAIProvider', () => { }; const mockResult = { - experimental_output: mockOutput, + output: mockOutput, usage: { - promptTokens: 100, - completionTokens: 50, + inputTokens: 100, + outputTokens: 50, totalTokens: 150, }, finishReason: 'stop', @@ -163,7 +163,7 @@ describe('VercelAIProvider', () => { }; const mockResult = { - experimental_output: { result: 'success' }, + output: { result: 'success' }, }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); @@ -187,7 +187,7 @@ describe('VercelAIProvider', () => { system: expect.any(String) as string, prompt: 'Input:\n\nTest content', temperature: 0.2, - experimental_output: expect.objectContaining({ + output: expect.objectContaining({ _outputType: 'object', }) as Record, }) @@ -200,7 +200,7 @@ describe('VercelAIProvider', () => { temperature: 0.7, }; - const mockResult = { experimental_output: { result: 'success' } }; + const mockResult = { output: { result: 'success' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -278,10 +278,10 @@ describe('VercelAIProvider', () => { }; const mockResult = { - experimental_output: { result: 'success' }, + output: { result: 'success' }, usage: { - promptTokens: 100, - completionTokens: 50, + inputTokens: 100, + outputTokens: 50, totalTokens: 150, }, finishReason: 'stop', @@ -306,8 +306,8 @@ describe('VercelAIProvider', () => { '[vectorlint] LLM response meta:', expect.objectContaining({ usage: expect.objectContaining({ - prompt_tokens: 100, - completion_tokens: 50, + input_tokens: 100, + output_tokens: 50, total_tokens: 150, }) as Record, }) @@ -320,7 +320,7 @@ describe('VercelAIProvider', () => { debug: false, }; - const mockResult = { experimental_output: { result: 'success' } }; + const mockResult = { output: { result: 'success' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -341,7 +341,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { result: 'success' } }; + const mockResult = { output: { result: 'success' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const buildPromptBodyForStructuredFn = vi.fn().mockReturnValue('Built system prompt'); @@ -374,7 +374,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { name: 'test' } }; + const mockResult = { output: { name: 'test' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -398,7 +398,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { score: 42 } }; + const mockResult = { output: { score: 42 } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -422,7 +422,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { requiredField: 'value' } }; + const mockResult = { output: { requiredField: 'value' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -447,7 +447,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { value: 'hello' } }; + const mockResult = { output: { value: 'hello' } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config); @@ -471,7 +471,7 @@ describe('VercelAIProvider', () => { model: MOCK_MODEL, }; - const mockResult = { experimental_output: { name: null } }; + const mockResult = { output: { name: null } }; MOCK_GENERATE_TEXT.mockResolvedValue(mockResult); const provider = new VercelAIProvider(config);