From a0ac97c05829bff1970cf14808b740497f4eb1b4 Mon Sep 17 00:00:00 2001 From: nikechan-x-worker Date: Tue, 23 Jun 2026 15:15:40 +0200 Subject: [PATCH 1/4] Update AI SDK models --- package-lock.json | 329 +++++---------- package.json | 24 +- .../features/constants/aiModels.test.ts | 23 +- src/features/constants/aiModels.ts | 375 +++++++++++++++--- 4 files changed, 447 insertions(+), 304 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d749efe..ea681a31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,17 +8,17 @@ "name": "aituber-kit", "version": "0.1.0", "dependencies": { - "@ai-sdk/anthropic": "^3.0.2", - "@ai-sdk/azure": "^3.0.2", - "@ai-sdk/cohere": "^3.0.2", - "@ai-sdk/deepseek": "^2.0.2", - "@ai-sdk/fireworks": "^2.0.29", - "@ai-sdk/google": "^3.0.2", - "@ai-sdk/mistral": "^3.0.2", - "@ai-sdk/openai": "^3.0.2", - "@ai-sdk/openai-compatible": "^2.0.2", - "@ai-sdk/perplexity": "^3.0.17", - "@ai-sdk/xai": "^3.0.3", + "@ai-sdk/anthropic": "^3.0.85", + "@ai-sdk/azure": "^3.0.77", + "@ai-sdk/cohere": "^3.0.39", + "@ai-sdk/deepseek": "^2.0.39", + "@ai-sdk/fireworks": "^2.0.57", + "@ai-sdk/google": "^3.0.83", + "@ai-sdk/mistral": "^3.0.40", + "@ai-sdk/openai": "^3.0.74", + "@ai-sdk/openai-compatible": "^2.0.51", + "@ai-sdk/perplexity": "^3.0.36", + "@ai-sdk/xai": "^3.0.96", "@anthropic-ai/sdk": "^0.20.8", "@azure/identity": "^4.13.0", "@charcoal-ui/icons": "^5.1.0", @@ -35,7 +35,7 @@ "@pixiv/three-vrm": "^3.4.4", "@supabase/supabase-js": "^2.89.0", "@vercel/analytics": "^1.6.1", - "ai": "^6.0.6", + "ai": "^6.0.208", "axios": "^1.6.8", "canvas": "^3.2.0", "face-api.js": "^0.22.2", @@ -144,13 +144,12 @@ "license": "MIT" }, "node_modules/@ai-sdk/anthropic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.4.tgz", - "integrity": "sha512-0TdiAHsgws15ZlWeMNa7rR6mIkcYrnU94atMgDvGNCUQuwuypDspeCcKbvohGpPmDRiec3/Ws/7Q3DjhZdtM9A==", - "license": "Apache-2.0", + "version": "3.0.85", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.85.tgz", + "integrity": "sha512-fNeDB644l5wbRNQU0FnI+F7UTtOenMnPtACfMPUJaS2zJfuBlseEa1TMg+otHkETZgaJB+6Na51NQEv0+m7czw==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -160,14 +159,14 @@ } }, "node_modules/@ai-sdk/azure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-3.0.2.tgz", - "integrity": "sha512-R0cMZ+czZHwyiU6vzmzjnHZ0pWvKyoTgTrkbN0DPBJ4nUw5mzC48OBNZLKkI0jwEE0o0TWwPEJsi4vSATy7G8w==", - "license": "Apache-2.0", + "version": "3.0.77", + "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-3.0.77.tgz", + "integrity": "sha512-LsCkguEGxawm4hCQlqywjJ5KUVBpQU83Y88yYD/9oWCf8Yq00Up8U1nAQcGEE0M41GbEtT9PQSsCA5ySCicUAg==", "dependencies": { - "@ai-sdk/openai": "3.0.2", - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/deepseek": "2.0.39", + "@ai-sdk/openai": "3.0.74", + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -177,13 +176,12 @@ } }, "node_modules/@ai-sdk/cohere": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/cohere/-/cohere-3.0.2.tgz", - "integrity": "sha512-FQI5m9QrSTeQBKfAFgz01F/mlpLNwF/TQJ77g7fL9AwwBTUIS+7RWuMwCo33F4aVsRMmDh7JgN39FMSHpV6NgA==", - "license": "Apache-2.0", + "version": "3.0.39", + "resolved": "https://registry.npmjs.org/@ai-sdk/cohere/-/cohere-3.0.39.tgz", + "integrity": "sha512-BLj9ZCwuoXJDRP6UbgM6ufvkcCVRnbS1SyA5j6pgt6exnlfLdo6UmpCX5T99/YdPvySUX8agvjTPC5EZw5mJ3A==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -193,13 +191,12 @@ } }, "node_modules/@ai-sdk/deepseek": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/deepseek/-/deepseek-2.0.2.tgz", - "integrity": "sha512-HznAFPbhOw/RUknLhSnYz3Ng78KBfouXlFRBL7WBw/jDbDKQIRl0vCux/DjjikbRBIl8EYewuPW7GnIEYMX5qA==", - "license": "Apache-2.0", + "version": "2.0.39", + "resolved": "https://registry.npmjs.org/@ai-sdk/deepseek/-/deepseek-2.0.39.tgz", + "integrity": "sha512-3wUq1cvqSWkRadCSqcDXBLpOwUAe+JqfWQZS0fK0sWiqDeTIEzsse+r7xcN2x5XpJjefT0d7FQTa3u2lZ/yxbg==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -212,7 +209,6 @@ "version": "2.0.57", "resolved": "https://registry.npmjs.org/@ai-sdk/fireworks/-/fireworks-2.0.57.tgz", "integrity": "sha512-G7ZNDg31Tl81RTR0dfBhNBMpkPBB1ufGP46o7TYX/FSoz6IqGnRuA23SAzaVJkQ3ag22onsvpNHRPRwYWX6OBg==", - "license": "Apache-2.0", "dependencies": { "@ai-sdk/openai-compatible": "2.0.51", "@ai-sdk/provider": "3.0.10", @@ -225,51 +221,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/fireworks/node_modules/@ai-sdk/openai-compatible": { - "version": "2.0.51", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-2.0.51.tgz", - "integrity": "sha512-A6qfyaVs4lxmRxRux6U3ViOa8mMbsSd0OaHghpei2MpiBT6791J4zFH5MN7kaW1tLfQ246rSC2DVTMOavsycjQ==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.10", - "@ai-sdk/provider-utils": "4.0.30" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/fireworks/node_modules/@ai-sdk/provider": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz", - "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/fireworks/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.30", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.30.tgz", - "integrity": "sha512-VO7I+vPffqI5sMnPoUq5DCSqKIgQIk/naJWRdQVpz2ma2zoprC/lqiJiUEl2s6DfvTD76TbhD3q39ROjlA6rGw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.10", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@ai-sdk/gateway": { "version": "3.0.133", "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.133.tgz", @@ -286,41 +237,13 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz", - "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.30", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.30.tgz", - "integrity": "sha512-VO7I+vPffqI5sMnPoUq5DCSqKIgQIk/naJWRdQVpz2ma2zoprC/lqiJiUEl2s6DfvTD76TbhD3q39ROjlA6rGw==", - "dependencies": { - "@ai-sdk/provider": "3.0.10", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@ai-sdk/google": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-3.0.2.tgz", - "integrity": "sha512-KyV4AR8fBKVCABfav3zGn/PY7cMDMt9m7yYhH+FJ7jLfBrEVdjT4sM0ojPFRHYUelXHl42oOAgpy3GWkeG6vtw==", - "license": "Apache-2.0", + "version": "3.0.83", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-3.0.83.tgz", + "integrity": "sha512-Pz7aCX0dy+5x+r4K/37HbLZNaPtPL4q2NduzJW64VffLv5sI9Nb478wAd7PlH2r2asiypJsz/Jerf9draTciUA==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -330,13 +253,12 @@ } }, "node_modules/@ai-sdk/mistral": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/mistral/-/mistral-3.0.2.tgz", - "integrity": "sha512-I2wPbhh3uzSqPA/OdU+PNOT5RZMhnJz86wHhMq8KEDSQEo6kDt82X5OlpL3LFdG0wlPYfZZnWhKpDFn7lcy/2Q==", - "license": "Apache-2.0", + "version": "3.0.40", + "resolved": "https://registry.npmjs.org/@ai-sdk/mistral/-/mistral-3.0.40.tgz", + "integrity": "sha512-HzCV9jFsb04kpL/N+G7SCjFKJA0Q33p4Hc+1quYGGD8Ta37Pe5bzk9AjxaS8OYd//jmcny16yKhJ+e+h+P0AJg==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -346,13 +268,12 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.2.tgz", - "integrity": "sha512-GONwavgSWtcWO+t9+GpGK8l7nIYh+zNtCL/NYDSeHxHiw6ksQS9XMRWrZyE5NpJ0EXNxSAWCHIDmb1WvTqhq9Q==", - "license": "Apache-2.0", + "version": "3.0.74", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.74.tgz", + "integrity": "sha512-LPDBWd2WCv0GQs29K2pHcNrGx24hm4D8QEP386HwUAUPr1URho6bNVXHNmIv0FxaW+xDkLpNMTen+mFCUBp2LA==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -362,13 +283,12 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-2.0.2.tgz", - "integrity": "sha512-9HFztZv9FBC7UxdLaA+gGKKEQjVNnI3nRmc6mRDPW5z5czvVKMrRgoXqjsYGbv+3giOXsVlRITHsrPswGn6O1w==", - "license": "Apache-2.0", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-2.0.51.tgz", + "integrity": "sha512-A6qfyaVs4lxmRxRux6U3ViOa8mMbsSd0OaHghpei2MpiBT6791J4zFH5MN7kaW1tLfQ246rSC2DVTMOavsycjQ==", "dependencies": { - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -378,42 +298,12 @@ } }, "node_modules/@ai-sdk/perplexity": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@ai-sdk/perplexity/-/perplexity-3.0.17.tgz", - "integrity": "sha512-0YpJkSF0c4tjF+6J6TNx5hqiStTJuHRSODdm8oc3mC0WULu1Y5UXbYel04diBWxdqx/Hb2tpU+YWwEGElEY1tQ==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.7", - "@ai-sdk/provider-utils": "4.0.13" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/perplexity/node_modules/@ai-sdk/provider": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.7.tgz", - "integrity": "sha512-VkPLrutM6VdA924/mG8OS+5frbVTcu6e046D2bgDo00tehBANR1QBJ/mPcZ9tXMFOsVcm6SQArOregxePzTFPw==", - "license": "Apache-2.0", + "version": "3.0.36", + "resolved": "https://registry.npmjs.org/@ai-sdk/perplexity/-/perplexity-3.0.36.tgz", + "integrity": "sha512-dtospD+FNzUseO19Knxt7CdhFKHD/FhDJjBAOYmQN/KFHg0rOp0fuVFOJdOY5ZRIOrZeiG3XoQFVGuek4++FFw==", "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/perplexity/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.13.tgz", - "integrity": "sha512-HHG72BN4d+OWTcq2NwTxOm/2qvk1duYsnhCDtsbYwn/h/4zeqURu1S0+Cn0nY2Ysq9a9HGKvrYuMn9bgFhR2Og==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.7", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -423,10 +313,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.1.tgz", - "integrity": "sha512-2lR4w7mr9XrydzxBSjir4N6YMGdXD+Np1Sh0RXABh7tWdNFFwIeRI1Q+SaYZMbfL8Pg8RRLcrxQm51yxTLhokg==", - "license": "Apache-2.0", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz", + "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==", "dependencies": { "json-schema": "^0.4.0" }, @@ -435,14 +324,13 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.2.tgz", - "integrity": "sha512-KaykkuRBdF/ffpI5bwpL4aSCmO/99p8/ci+VeHwJO8tmvXtiVAb99QeyvvvXmL61e9Zrvv4GBGoajW19xdjkVQ==", - "license": "Apache-2.0", + "version": "4.0.30", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.30.tgz", + "integrity": "sha512-VO7I+vPffqI5sMnPoUq5DCSqKIgQIk/naJWRdQVpz2ma2zoprC/lqiJiUEl2s6DfvTD76TbhD3q39ROjlA6rGw==", "dependencies": { - "@ai-sdk/provider": "3.0.1", + "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" + "eventsource-parser": "^3.0.8" }, "engines": { "node": ">=18" @@ -496,17 +384,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/provider-utils-v6/node_modules/@ai-sdk/provider": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz", - "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@ai-sdk/provider-v5": { "name": "@ai-sdk/provider", "version": "2.0.3", @@ -532,14 +409,13 @@ } }, "node_modules/@ai-sdk/xai": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/xai/-/xai-3.0.3.tgz", - "integrity": "sha512-Ts0RQ2yNlgX/mIj+1cvJvh/GYPZnXLe8Lzr4VFn9QNRjWJmRCQ1TSGymdmSB2dRHyBQU10acuem/bkkxFmL8vQ==", - "license": "Apache-2.0", + "version": "3.0.96", + "resolved": "https://registry.npmjs.org/@ai-sdk/xai/-/xai-3.0.96.tgz", + "integrity": "sha512-a24jD29cQ3YocP7B4NUvNTb9s5CYR1ao2yU/QncT7QDwwOp4x/lq+6W+BdXGf1VucG7A0dx9jtyIOm6PE4JPyg==", "dependencies": { - "@ai-sdk/openai-compatible": "2.0.2", - "@ai-sdk/provider": "3.0.1", - "@ai-sdk/provider-utils": "4.0.2" + "@ai-sdk/openai-compatible": "2.0.51", + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" }, "engines": { "node": ">=18" @@ -5862,6 +5738,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5901,6 +5778,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5921,6 +5799,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5941,6 +5820,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5961,6 +5841,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5981,6 +5862,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6001,6 +5883,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6021,6 +5904,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6041,6 +5925,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6061,6 +5946,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6081,6 +5967,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6101,6 +5988,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6121,6 +6009,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6141,6 +6030,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6158,6 +6048,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, "license": "Apache-2.0", "optional": true, "bin": { @@ -9174,33 +9065,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/ai/node_modules/@ai-sdk/provider": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz", - "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/ai/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.30", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.30.tgz", - "integrity": "sha512-VO7I+vPffqI5sMnPoUq5DCSqKIgQIk/naJWRdQVpz2ma2zoprC/lqiJiUEl2s6DfvTD76TbhD3q39ROjlA6rGw==", - "dependencies": { - "@ai-sdk/provider": "3.0.10", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -10010,7 +9874,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -12967,7 +12831,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -14641,7 +14505,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14705,7 +14569,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -14790,7 +14654,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -17521,7 +17385,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -17535,7 +17399,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -18959,6 +18823,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -21913,7 +21778,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" diff --git a/package.json b/package.json index d5202742..653ed76b 100644 --- a/package.json +++ b/package.json @@ -30,17 +30,17 @@ "deploy:cloudflare": "node scripts/build-cloudflare.js --deploy" }, "dependencies": { - "@ai-sdk/anthropic": "^3.0.2", - "@ai-sdk/azure": "^3.0.2", - "@ai-sdk/cohere": "^3.0.2", - "@ai-sdk/deepseek": "^2.0.2", - "@ai-sdk/fireworks": "^2.0.29", - "@ai-sdk/google": "^3.0.2", - "@ai-sdk/mistral": "^3.0.2", - "@ai-sdk/openai": "^3.0.2", - "@ai-sdk/openai-compatible": "^2.0.2", - "@ai-sdk/perplexity": "^3.0.17", - "@ai-sdk/xai": "^3.0.3", + "@ai-sdk/anthropic": "^3.0.85", + "@ai-sdk/azure": "^3.0.77", + "@ai-sdk/cohere": "^3.0.39", + "@ai-sdk/deepseek": "^2.0.39", + "@ai-sdk/fireworks": "^2.0.57", + "@ai-sdk/google": "^3.0.83", + "@ai-sdk/mistral": "^3.0.40", + "@ai-sdk/openai": "^3.0.74", + "@ai-sdk/openai-compatible": "^2.0.51", + "@ai-sdk/perplexity": "^3.0.36", + "@ai-sdk/xai": "^3.0.96", "@anthropic-ai/sdk": "^0.20.8", "@azure/identity": "^4.13.0", "@charcoal-ui/icons": "^5.1.0", @@ -57,7 +57,7 @@ "@pixiv/three-vrm": "^3.4.4", "@supabase/supabase-js": "^2.89.0", "@vercel/analytics": "^1.6.1", - "ai": "^6.0.6", + "ai": "^6.0.208", "axios": "^1.6.8", "canvas": "^3.2.0", "face-api.js": "^0.22.2", diff --git a/src/__tests__/features/constants/aiModels.test.ts b/src/__tests__/features/constants/aiModels.test.ts index 08f811b9..0fff6f72 100644 --- a/src/__tests__/features/constants/aiModels.test.ts +++ b/src/__tests__/features/constants/aiModels.test.ts @@ -78,8 +78,8 @@ describe('aiModels', () => { }) describe('getDefaultModel', () => { - it('should return gpt-4.1-mini for openai', () => { - expect(getDefaultModel('openai')).toBe('gpt-4.1-mini') + it('should return gpt-5.4-mini for openai', () => { + expect(getDefaultModel('openai')).toBe('gpt-5.4-mini') }) it('should return claude-sonnet-4-6 for anthropic', () => { @@ -116,8 +116,10 @@ describe('aiModels', () => { }) describe('getSpecificDefaultModel', () => { - it('should return tts-1 for openaiAudio', () => { - expect(getSpecificDefaultModel('openaiAudio')).toBe('tts-1') + it('should return gpt-4o-mini-audio-preview for openaiAudio', () => { + expect(getSpecificDefaultModel('openaiAudio')).toBe( + 'gpt-4o-mini-audio-preview' + ) }) it('should return gpt-realtime for openaiRealtime', () => { @@ -125,7 +127,7 @@ describe('aiModels', () => { }) it('should also work for regular AIService', () => { - expect(getSpecificDefaultModel('openai')).toBe('gpt-4.1-mini') + expect(getSpecificDefaultModel('openai')).toBe('gpt-5.4-mini') }) }) @@ -144,10 +146,13 @@ describe('aiModels', () => { expect(models).toEqual(allModels) }) - it('should return all models for google (all are multimodal)', () => { + it('should return subset for google (Gemma and AQA are not multimodal)', () => { const models = getMultiModalModels('google') const allModels = getModels('google') - expect(models).toEqual(allModels) + expect(models.length).toBeLessThan(allModels.length) + expect(models).toContain('gemini-2.5-flash') + expect(models).not.toContain('aqa') + expect(models).not.toContain('gemma-3-27b-it') }) it('should return subset for xai (some are not multimodal)', () => { @@ -305,8 +310,8 @@ describe('aiModels', () => { it('getOpenAIAudioModels should return correct models', () => { const models = getOpenAIAudioModels() expect(models).toEqual([...openAIAudioModels]) - expect(models).toContain('tts-1') - expect(models).toContain('tts-1-hd') + expect(models).toContain('gpt-4o-audio-preview') + expect(models).toContain('gpt-4o-mini-audio-preview') }) it('getOpenAIWhisperModels should return correct models', () => { diff --git a/src/features/constants/aiModels.ts b/src/features/constants/aiModels.ts index 8aa01938..9fa607fe 100644 --- a/src/features/constants/aiModels.ts +++ b/src/features/constants/aiModels.ts @@ -26,93 +26,230 @@ interface ModelInfo { */ const modelDefinitions: Record = { openai: [ + { + name: 'gpt-5.5', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.5-2026-04-23', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gpt-5.4-pro', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { + name: 'gpt-5.4-pro-2026-03-05', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gpt-5.4', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5.3-chat-latest', + name: 'gpt-5.4-2026-03-05', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.4-mini', + multiModal: true, + isDefault: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.4-mini-2026-03-17', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.4-nano', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.4-nano-2026-03-17', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { name: 'gpt-5.3-chat-latest', multiModal: true }, + { + name: 'gpt-5.3-codex', + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gpt-5.2-pro', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5.2-chat-latest', + name: 'gpt-5.2-pro-2025-12-11', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { name: 'gpt-5.2-chat-latest', multiModal: true }, { name: 'gpt-5.2', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5.1-codex-mini', + name: 'gpt-5.2-2025-12-11', multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.2-codex', + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'gpt-5.1-codex-mini', reasoningEfforts: ['none', 'minimal', 'low', 'medium', 'high'], }, { name: 'gpt-5.1-codex', - multiModal: true, reasoningEfforts: ['none', 'minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5.1-chat-latest', - multiModal: true, + name: 'gpt-5.1-codex-max', reasoningEfforts: ['none', 'minimal', 'low', 'medium', 'high'], }, + { name: 'gpt-5.1-chat-latest', multiModal: true }, { name: 'gpt-5.1', multiModal: true, reasoningEfforts: ['none', 'minimal', 'low', 'medium', 'high'], }, + { + name: 'gpt-5.1-2025-11-13', + multiModal: true, + reasoningEfforts: ['none', 'minimal', 'low', 'medium', 'high'], + }, { name: 'gpt-5-pro', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { + name: 'gpt-5-pro-2025-10-06', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gpt-5', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { + name: 'gpt-5-2025-08-07', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gpt-5-mini', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5-nano', + name: 'gpt-5-mini-2025-08-07', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5-codex', + name: 'gpt-5-nano', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, { - name: 'gpt-5-chat-latest', + name: 'gpt-5-nano-2025-08-07', multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { + name: 'gpt-5-codex', + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { name: 'gpt-5-chat-latest', multiModal: true }, { name: 'gpt-4.1', multiModal: true }, - { name: 'gpt-4.1-mini', multiModal: true, isDefault: true }, + { name: 'gpt-4.1-2025-04-14', multiModal: true }, + { name: 'gpt-4.1-mini', multiModal: true }, + { name: 'gpt-4.1-mini-2025-04-14', multiModal: true }, { name: 'gpt-4.1-nano', multiModal: true }, + { name: 'gpt-4.1-nano-2025-04-14', multiModal: true }, { name: 'gpt-4o', multiModal: true }, + { name: 'gpt-4o-2024-05-13', multiModal: true }, + { name: 'gpt-4o-2024-08-06', multiModal: true }, + { name: 'gpt-4o-2024-11-20', multiModal: true }, { name: 'gpt-4o-mini', multiModal: true }, + { name: 'gpt-4o-mini-2024-07-18', multiModal: true }, + { name: 'gpt-3.5-turbo' }, + { name: 'gpt-3.5-turbo-0125' }, + { name: 'gpt-3.5-turbo-1106' }, + { + name: 'o4-mini', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o4-mini-2025-04-16', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o3', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o3-2025-04-16', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o3-mini', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o3-mini-2025-01-31', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o1', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, + { + name: 'o1-2024-12-17', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, ], anthropic: [ + { + name: 'claude-fable-5', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'claude-opus-4-8', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'claude-opus-4-7', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, { name: 'claude-opus-4-6', multiModal: true, @@ -132,43 +269,90 @@ const modelDefinitions: Record = { reasoningEfforts: ['low', 'medium', 'high'], reasoningTokenBudget: true, }, + { + name: 'claude-opus-4-5-20251101', + multiModal: true, + reasoningEfforts: ['low', 'medium', 'high'], + reasoningTokenBudget: true, + }, { name: 'claude-opus-4-1', multiModal: true, reasoningEfforts: [], reasoningTokenBudget: true, }, + { + name: 'claude-opus-4-1-20250805', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, { name: 'claude-opus-4-0', multiModal: true, reasoningEfforts: [], reasoningTokenBudget: true, }, + { + name: 'claude-opus-4-20250514', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, { name: 'claude-sonnet-4-5', multiModal: true, reasoningEfforts: [], reasoningTokenBudget: true, }, + { + name: 'claude-sonnet-4-5-20250929', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, { name: 'claude-sonnet-4-0', multiModal: true, reasoningEfforts: [], reasoningTokenBudget: true, }, + { + name: 'claude-sonnet-4-20250514', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, { name: 'claude-haiku-4-5', multiModal: true, reasoningEfforts: [], reasoningTokenBudget: true, }, + { + name: 'claude-haiku-4-5-20251001', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { name: 'claude-3-haiku-20240307', multiModal: true }, ], google: [ + { + name: 'gemini-3.5-flash', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gemini-3.1-pro-preview', multiModal: true, reasoningEfforts: ['low', 'medium', 'high'], }, + { + name: 'gemini-3.1-pro-preview-customtools', + multiModal: true, + reasoningEfforts: ['low', 'medium', 'high'], + }, { name: 'gemini-3.1-flash-image-preview', multiModal: true, @@ -179,6 +363,11 @@ const modelDefinitions: Record = { multiModal: true, reasoningEfforts: ['minimal', 'low', 'medium', 'high'], }, + { + name: 'gemini-3.1-flash-tts-preview', + multiModal: true, + reasoningEfforts: ['minimal', 'low', 'medium', 'high'], + }, { name: 'gemini-3-pro-preview', multiModal: true, @@ -207,6 +396,12 @@ const modelDefinitions: Record = { reasoningEfforts: [], reasoningTokenBudget: true, }, + { + name: 'gemini-2.5-flash-image', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, { name: 'gemini-2.5-flash-lite', multiModal: true, @@ -214,16 +409,61 @@ const modelDefinitions: Record = { reasoningTokenBudget: true, }, { - name: 'gemini-2.5-flash-lite-preview-06-17', + name: 'gemini-2.5-flash-preview-tts', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'gemini-2.5-pro-preview-tts', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'gemini-2.5-flash-native-audio-latest', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'gemini-2.5-flash-native-audio-preview-09-2025', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'gemini-2.5-flash-native-audio-preview-12-2025', + multiModal: true, + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'gemini-2.5-computer-use-preview-10-2025', multiModal: true, reasoningEfforts: [], reasoningTokenBudget: true, }, { name: 'gemini-2.0-flash', multiModal: true }, + { name: 'gemini-2.0-flash-001', multiModal: true }, + { name: 'gemini-2.0-flash-lite', multiModal: true }, + { name: 'gemini-2.0-flash-lite-001', multiModal: true }, + { name: 'gemini-pro-latest', multiModal: true }, + { name: 'gemini-flash-latest', multiModal: true }, + { name: 'gemini-flash-lite-latest', multiModal: true }, + { name: 'deep-research-pro-preview-12-2025', multiModal: true }, + { name: 'nano-banana-pro-preview', multiModal: true }, + { name: 'aqa' }, + { name: 'gemini-robotics-er-1.5-preview', multiModal: true }, + { name: 'gemma-3-1b-it' }, + { name: 'gemma-3-4b-it' }, + { name: 'gemma-3n-e4b-it' }, + { name: 'gemma-3n-e2b-it' }, + { name: 'gemma-3-12b-it' }, + { name: 'gemma-3-27b-it' }, ], azure: [], xai: [ - { name: 'grok-4-1', multiModal: true, reasoningEfforts: ['low', 'high'] }, { name: 'grok-4-1-fast-reasoning', reasoningEfforts: ['low', 'high'], @@ -231,6 +471,9 @@ const modelDefinitions: Record = { { name: 'grok-4-1-fast-non-reasoning' }, { name: 'grok-4-fast-non-reasoning' }, { name: 'grok-4-fast-reasoning', reasoningEfforts: ['low', 'high'] }, + { name: 'grok-4.20-0309-non-reasoning' }, + { name: 'grok-4.20-0309-reasoning', reasoningEfforts: ['low', 'high'] }, + { name: 'grok-4.20-multi-agent-0309', reasoningEfforts: ['low', 'high'] }, { name: 'grok-code-fast-1' }, { name: 'grok-4', @@ -266,29 +509,24 @@ const modelDefinitions: Record = { }, ], groq: [ - { name: 'gemma2-9b-it' }, { name: 'llama-3.1-8b-instant' }, { name: 'llama-3.3-70b-versatile', isDefault: true }, - { name: 'meta-llama/llama-guard-4-12b' }, - { name: 'deepseek-r1-distill-llama-70b' }, - { name: 'meta-llama/llama-4-maverick-17b-128e-instruct' }, + { + name: 'openai/gpt-oss-120b', + reasoningEfforts: ['low', 'medium', 'high'], + }, + { name: 'openai/gpt-oss-20b', reasoningEfforts: ['low', 'medium', 'high'] }, + { name: 'groq/compound' }, + { name: 'groq/compound-mini' }, { name: 'meta-llama/llama-4-scout-17b-16e-instruct', multiModal: true }, { name: 'meta-llama/llama-prompt-guard-2-22m' }, { name: 'meta-llama/llama-prompt-guard-2-86m' }, - { name: 'moonshotai/kimi-k2-instruct-0905' }, - { name: 'qwen/qwen3-32b', reasoningEfforts: [] }, - { name: 'llama-guard-3-8b' }, - { name: 'llama3-70b-8192' }, - { name: 'llama3-8b-8192' }, - { name: 'mixtral-8x7b-32768' }, - { name: 'qwen-qwq-32b' }, - { name: 'qwen-2.5-32b' }, - { name: 'deepseek-r1-distill-qwen-32b' }, - { name: 'openai/gpt-oss-20b', reasoningEfforts: ['low', 'medium', 'high'] }, { - name: 'openai/gpt-oss-120b', + name: 'openai/gpt-oss-safeguard-20b', reasoningEfforts: ['low', 'medium', 'high'], }, + { name: 'qwen/qwen3-32b', reasoningEfforts: [] }, + { name: 'qwen/qwen3.6-27b', reasoningEfforts: [] }, ], cohere: [ { name: 'command-a-03-2025', isDefault: true }, @@ -297,6 +535,7 @@ const modelDefinitions: Record = { reasoningEfforts: [], reasoningTokenBudget: true, }, + { name: 'command-a-vision-07-2025', multiModal: true }, { name: 'command-r7b-12-2024' }, { name: 'command-r-plus-04-2024' }, { name: 'command-r-plus' }, @@ -309,22 +548,40 @@ const modelDefinitions: Record = { { name: 'command-light-nightly' }, ], mistralai: [ - { name: 'pixtral-large-latest', multiModal: true }, + { name: 'ministral-3b-latest' }, + { name: 'ministral-8b-latest' }, + { name: 'ministral-14b-latest' }, { name: 'mistral-large-latest', isDefault: true }, { name: 'mistral-medium-latest' }, + { name: 'mistral-medium-3' }, + { name: 'mistral-large-2512' }, { name: 'mistral-medium-2508' }, { name: 'mistral-medium-2505' }, + { name: 'mistral-small-2506' }, + { name: 'pixtral-large-latest', multiModal: true }, + { name: 'mistral-medium-3.5' }, { name: 'mistral-small-latest' }, - { name: 'magistral-small-2507' }, - { name: 'magistral-medium-2507' }, - { name: 'magistral-small-2506' }, - { name: 'magistral-medium-2506' }, - { name: 'ministral-3b-latest' }, - { name: 'ministral-8b-latest' }, - { name: 'pixtral-12b-2409', multiModal: true }, - { name: 'open-mistral-7b' }, - { name: 'open-mixtral-8x7b' }, - { name: 'open-mixtral-8x22b' }, + { name: 'mistral-small-2603' }, + { + name: 'magistral-medium-latest', + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'magistral-small-latest', + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'magistral-medium-2509', + reasoningEfforts: [], + reasoningTokenBudget: true, + }, + { + name: 'magistral-small-2509', + reasoningEfforts: [], + reasoningTokenBudget: true, + }, ], perplexity: [ { name: 'sonar-deep-research' }, @@ -334,16 +591,14 @@ const modelDefinitions: Record = { { name: 'sonar' }, ], fireworks: [ - { name: 'accounts/fireworks/models/firefunction-v1' }, - { name: 'accounts/fireworks/models/deepseek-r1' }, { name: 'accounts/fireworks/models/deepseek-v3' }, + { name: 'accounts/fireworks/models/llama-v3p3-70b-instruct' }, + { name: 'accounts/fireworks/models/llama-v3p2-3b-instruct' }, { name: 'accounts/fireworks/models/llama-v3p1-405b-instruct' }, { name: 'accounts/fireworks/models/llama-v3p1-8b-instruct' }, - { name: 'accounts/fireworks/models/llama-v3p2-3b-instruct' }, - { name: 'accounts/fireworks/models/llama-v3p3-70b-instruct' }, { name: 'accounts/fireworks/models/mixtral-8x7b-instruct' }, - { name: 'accounts/fireworks/models/mixtral-8x7b-instruct-hf' }, { name: 'accounts/fireworks/models/mixtral-8x22b-instruct' }, + { name: 'accounts/fireworks/models/mixtral-8x7b-instruct-hf' }, { name: 'accounts/fireworks/models/qwen2p5-coder-32b-instruct' }, { name: 'accounts/fireworks/models/qwen2p5-72b-instruct' }, { name: 'accounts/fireworks/models/qwen-qwq-32b-preview' }, @@ -401,7 +656,7 @@ export const defaultModels: Record< models.find((model) => model.isDefault)?.name || '', ]) ), - openaiAudio: 'tts-1', + openaiAudio: 'gpt-4o-mini-audio-preview', openaiRealtime: 'gpt-realtime', } as Record @@ -465,9 +720,11 @@ export const openAIRealtimeModels = [ * OpenAIのオーディオAPIモードで使用するモデル一覧 */ export const openAIAudioModels = [ - 'tts-1', - 'tts-1-hd', - 'gpt-4o-mini-tts', + 'gpt-4o-audio-preview', + 'gpt-4o-audio-preview-2024-12-17', + 'gpt-4o-audio-preview-2025-06-03', + 'gpt-4o-mini-audio-preview', + 'gpt-4o-mini-audio-preview-2024-12-17', ] as const /** @@ -492,7 +749,10 @@ export function getOpenAIAudioModels(): string[] { export const openAIWhisperModels = [ 'whisper-1', 'gpt-4o-transcribe', + 'gpt-4o-transcribe-diarize', 'gpt-4o-mini-transcribe', + 'gpt-4o-mini-transcribe-2025-03-20', + 'gpt-4o-mini-transcribe-2025-12-15', ] as const /** @@ -505,7 +765,15 @@ export function getOpenAIWhisperModels(): string[] { /** * OpenAIのTTS(音声合成)用モデル一覧 */ -export const openAITTSModels = ['tts-1', 'tts-1-hd', 'gpt-4o-mini-tts'] as const +export const openAITTSModels = [ + 'tts-1', + 'tts-1-1106', + 'tts-1-hd', + 'tts-1-hd-1106', + 'gpt-4o-mini-tts', + 'gpt-4o-mini-tts-2025-03-20', + 'gpt-4o-mini-tts-2025-12-15', +] as const /** * OpenAIのTTSモデル一覧を取得する @@ -580,17 +848,22 @@ export function isMultiModalAvailable( } export const googleSearchGroundingModels = [ + 'gemini-3.5-flash', 'gemini-3.1-pro-preview', - 'gemini-3.1-flash-image-preview', + 'gemini-3.1-pro-preview-customtools', 'gemini-3.1-flash-lite-preview', 'gemini-3-pro-preview', - 'gemini-3-pro-image-preview', 'gemini-3-flash-preview', 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite', - 'gemini-2.5-flash-lite-preview-06-17', 'gemini-2.0-flash', + 'gemini-2.0-flash-001', + 'gemini-2.0-flash-lite', + 'gemini-2.0-flash-lite-001', + 'gemini-pro-latest', + 'gemini-flash-latest', + 'gemini-flash-lite-latest', ] as const /** From 40e38c03753c349c201f129ba09e5438f6cc9796 Mon Sep 17 00:00:00 2001 From: nikechan-x-worker Date: Tue, 23 Jun 2026 15:15:53 +0200 Subject: [PATCH 2/4] Improve quick start provider setup --- locales/ja/translation.json | 4 +- src/components/settings/quickStart.tsx | 791 +++++++++++++++++++++++-- 2 files changed, 730 insertions(+), 65 deletions(-) diff --git a/locales/ja/translation.json b/locales/ja/translation.json index 7175729f..4ae1e80e 100644 --- a/locales/ja/translation.json +++ b/locales/ja/translation.json @@ -246,11 +246,11 @@ "QuickStartBasicsDescription": "表示名と言語など、最初に決める項目です。", "QuickStartDetailedDisplaySettings": "見た目や背景を詳しく設定", "QuickStartAIConversationTitle": "2. AIと会話", - "QuickStartAIConversationDescription": "迷ったらAIサービスとモデルだけ設定すれば大丈夫です。", + "QuickStartAIConversationDescription": "迷ったらAPIキー、AIサービス、モデルだけ設定すれば大丈夫です。", "QuickStartDetailedAISettings": "AIを詳しく設定", "QuickStartDetailedMemorySettings": "記憶を詳しく設定", "QuickStartVoiceTitle": "3. 声", - "QuickStartVoiceDescription": "キャラクターがどの音声エンジンで話すかを選びます。", + "QuickStartVoiceDescription": "音声エンジンと、発話に必要なAPIキーや話者を設定します。", "QuickStartDetailedVoiceSettings": "声を詳しく設定", "QuickStartSpeechInputSettings": "マイク入力を設定", "QuickStartOptionalFeaturesTitle": "4. 必要になったら使う設定", diff --git a/src/components/settings/quickStart.tsx b/src/components/settings/quickStart.tsx index 79594225..23a52c5e 100644 --- a/src/components/settings/quickStart.tsx +++ b/src/components/settings/quickStart.tsx @@ -1,15 +1,57 @@ import { useTranslation } from 'react-i18next' import i18n from 'i18next' -import { ReactNode } from 'react' +import { ReactNode, useEffect, useState } from 'react' +import { Listbox } from '@headlessui/react' import settingsStore from '@/features/stores/settings' import menuStore from '@/features/stores/menu' -import { AIService, AIVoice, Language } from '@/features/constants/settings' +import { + AIService, + AIVoice, + Language, + OpenAITTSModel, + OpenAITTSVoice, +} from '@/features/constants/settings' +import { getOpenAITTSModels } from '@/features/constants/aiModels' import { ModelSelector } from './modelProvider/ModelSelector' -import { aiServiceOptions } from './modelProvider/utils/aiServiceConfigs' +import { ApiKeyInput } from './modelProvider/ApiKeyInput' +import { ServiceLogo } from './modelProvider/ServiceLogo' +import { + aiServiceOptions, + getServiceConfigByKey, +} from './modelProvider/utils/aiServiceConfigs' import { useAIServiceHandlers } from './modelProvider/hooks/useAIServiceHandlers' import { useModelProviderState } from './modelProvider/hooks/useModelProviderState' import { ToggleSwitch } from '../toggleSwitch' import { languageOptions } from '@/components/settings/languageOptions' +import speakers from '../speakers.json' + +type SpeakerOption = { + id: number | string + speaker: string +} + +const inputClassName = + 'w-full rounded-lg bg-white px-4 py-2 hover:bg-white-hover' + +const selectButtonClassName = + 'flex w-full cursor-pointer items-center justify-between rounded-lg border border-gray-300 bg-white px-4 py-2 text-left shadow-sm transition hover:bg-white-hover focus:border-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/20' + +const choiceControlClassName = 'w-full max-w-full sm:w-80' + +const quickGridClassName = 'mt-4 grid gap-4 sm:grid-cols-2' + +const openAITTSVoiceOptions: OpenAITTSVoice[] = [ + 'alloy', + 'ash', + 'ballad', + 'coral', + 'echo', + 'fable', + 'onyx', + 'nova', + 'sage', + 'shimmer', +] type QuickStartDestination = | 'based' @@ -27,15 +69,62 @@ const QuickStart = () => { const modelState = useModelProviderState() const { handleAIServiceChange, updateMultiModalModeForModel } = useAIServiceHandlers() + const serviceConfig = + getServiceConfigByKey(t)[modelState.selectAIService as AIService] + const selectedServiceOption = aiServiceOptions.find( + (option) => option.value === modelState.selectAIService + ) const characterName = settingsStore((s) => s.characterName) const userDisplayName = settingsStore((s) => s.userDisplayName) const selectLanguage = settingsStore((s) => s.selectLanguage) const selectVoice = settingsStore((s) => s.selectVoice) - const maxTokens = settingsStore((s) => s.maxTokens) - const maxPastMessages = settingsStore((s) => s.maxPastMessages) const showAssistantText = settingsStore((s) => s.showAssistantText) const showCharacterName = settingsStore((s) => s.showCharacterName) + const koeiromapKey = settingsStore((s) => s.koeiromapKey) + const googleTtsType = settingsStore((s) => s.googleTtsType) + const voicevoxSpeaker = settingsStore((s) => s.voicevoxSpeaker) + const voicevoxServerUrl = settingsStore((s) => s.voicevoxServerUrl) + const aivisSpeechSpeaker = settingsStore((s) => s.aivisSpeechSpeaker) + const aivisSpeechServerUrl = settingsStore((s) => s.aivisSpeechServerUrl) + const aivisCloudApiKey = settingsStore((s) => s.aivisCloudApiKey) + const aivisCloudModelUuid = settingsStore((s) => s.aivisCloudModelUuid) + const aivisCloudStyleId = settingsStore((s) => s.aivisCloudStyleId) + const aivisCloudStyleName = settingsStore((s) => s.aivisCloudStyleName) + const aivisCloudUseStyleName = settingsStore((s) => s.aivisCloudUseStyleName) + const stylebertvits2ServerUrl = settingsStore( + (s) => s.stylebertvits2ServerUrl + ) + const stylebertvits2ApiKey = settingsStore((s) => s.stylebertvits2ApiKey) + const stylebertvits2ModelId = settingsStore((s) => s.stylebertvits2ModelId) + const stylebertvits2Style = settingsStore((s) => s.stylebertvits2Style) + const gsviTtsServerUrl = settingsStore((s) => s.gsviTtsServerUrl) + const gsviTtsModelId = settingsStore((s) => s.gsviTtsModelId) + const elevenlabsApiKey = settingsStore((s) => s.elevenlabsApiKey) + const elevenlabsVoiceId = settingsStore((s) => s.elevenlabsVoiceId) + const cartesiaApiKey = settingsStore((s) => s.cartesiaApiKey) + const cartesiaVoiceId = settingsStore((s) => s.cartesiaVoiceId) + const openaiTTSVoice = settingsStore((s) => s.openaiTTSVoice) + const openaiTTSModel = settingsStore((s) => s.openaiTTSModel) + const azureTTSKey = settingsStore((s) => s.azureTTSKey) + const azureTTSEndpoint = settingsStore((s) => s.azureTTSEndpoint) + const [aivisSpeakers, setAivisSpeakers] = useState([]) + + useEffect(() => { + if (selectVoice !== 'aivis_speech') return + + const fetchAivisSpeakers = async () => { + try { + const response = await fetch('/speakers_aivis.json') + const data = (await response.json()) as SpeakerOption[] + setAivisSpeakers(data) + } catch (error) { + console.error('Failed to fetch AIVIS speakers:', error) + } + } + + fetchAivisSpeakers() + }, [selectVoice]) const goTo = (tab: QuickStartDestination) => { menuStore.setState({ @@ -44,6 +133,588 @@ const QuickStart = () => { }) } + const getSelectedServiceApiKey = () => { + switch (modelState.selectAIService) { + case 'openai': + return modelState.openaiKey + case 'anthropic': + return modelState.anthropicKey + case 'google': + return modelState.googleKey + case 'azure': + return modelState.azureKey + case 'xai': + return modelState.xaiKey + case 'groq': + return modelState.groqKey + case 'cohere': + return modelState.cohereKey + case 'mistralai': + return modelState.mistralaiKey + case 'perplexity': + return modelState.perplexityKey + case 'fireworks': + return modelState.fireworksKey + case 'deepseek': + return modelState.deepseekKey + case 'openrouter': + return modelState.openrouterKey + case 'dify': + return modelState.difyKey + default: + return '' + } + } + + const updateSelectedServiceApiKey = (value: string) => { + switch (modelState.selectAIService) { + case 'openai': + settingsStore.setState({ openaiKey: value }) + break + case 'anthropic': + settingsStore.setState({ anthropicKey: value }) + break + case 'google': + settingsStore.setState({ googleKey: value }) + break + case 'azure': + settingsStore.setState({ azureKey: value }) + break + case 'xai': + settingsStore.setState({ xaiKey: value }) + break + case 'groq': + settingsStore.setState({ groqKey: value }) + break + case 'cohere': + settingsStore.setState({ cohereKey: value }) + break + case 'mistralai': + settingsStore.setState({ mistralaiKey: value }) + break + case 'perplexity': + settingsStore.setState({ perplexityKey: value }) + break + case 'fireworks': + settingsStore.setState({ fireworksKey: value }) + break + case 'deepseek': + settingsStore.setState({ deepseekKey: value }) + break + case 'openrouter': + settingsStore.setState({ openrouterKey: value }) + break + case 'dify': + settingsStore.setState({ difyKey: value }) + break + } + } + + const handleModelChange = (model: string) => { + settingsStore.setState({ selectAIModel: model }) + updateMultiModalModeForModel(modelState.selectAIService as AIService, model) + } + + const renderQuickAIServiceSettings = () => { + switch (modelState.selectAIService) { + case 'azure': + return ( +
+ + + settingsStore.setState({ azureEndpoint: e.target.value }) + } + placeholder="https://resource.openai.azure.com/openai/deployments/deployment/chat/completions?api-version=2024-10-21" + /> + +
+ ) + case 'dify': + return ( +
+ + + settingsStore.setState({ difyUrl: e.target.value }) + } + placeholder="https://api.dify.ai/v1/chat-messages" + /> + +
+ ) + case 'lmstudio': + case 'ollama': + return ( +
+ + + settingsStore.setState({ localLlmUrl: e.target.value }) + } + placeholder={ + modelState.selectAIService === 'ollama' + ? 'http://localhost:11434' + : 'http://localhost:1234/v1' + } + /> + + + handleModelChange(e.target.value)} + placeholder="model-name" + /> + +
+ ) + case 'openrouter': + return ( +
+ + handleModelChange(e.target.value)} + placeholder="openai/gpt-4o" + /> + +
+ ) + case 'custom-api': + return ( +
+ + + settingsStore.setState({ customApiUrl: e.target.value }) + } + placeholder="https://example.com/api/chat" + /> + +
+ ) + default: + return ( +
+ + settingsStore.setState({ customModel: !modelState.customModel }) + } + /> +
+ ) + } + } + + const renderQuickVoiceSettings = () => { + switch (selectVoice) { + case 'koeiromap': + return ( +
+ + + settingsStore.setState({ koeiromapKey: e.target.value }) + } + placeholder="..." + /> + +
+ ) + case 'voicevox': + return ( +
+ + + settingsStore.setState({ voicevoxServerUrl: e.target.value }) + } + placeholder="http://localhost:50021" + /> + + + + +
+ ) + case 'google': + return ( +
+ + + settingsStore.setState({ googleTtsType: e.target.value }) + } + placeholder="ja-JP-Neural2-B" + /> + +
+ ) + case 'stylebertvits2': + return ( +
+ + + settingsStore.setState({ + stylebertvits2ServerUrl: e.target.value, + }) + } + placeholder="http://localhost:5000" + /> + + + + settingsStore.setState({ + stylebertvits2ApiKey: e.target.value, + }) + } + placeholder="..." + /> + + + + settingsStore.setState({ + stylebertvits2ModelId: e.target.value, + }) + } + placeholder="0" + /> + + + + settingsStore.setState({ + stylebertvits2Style: e.target.value, + }) + } + placeholder="Neutral" + /> + +
+ ) + case 'aivis_speech': + return ( +
+ + + settingsStore.setState({ + aivisSpeechServerUrl: e.target.value, + }) + } + placeholder="http://localhost:10101" + /> + + + + +
+ ) + case 'aivis_cloud_api': + return ( +
+ + + settingsStore.setState({ + aivisCloudApiKey: e.target.value, + }) + } + placeholder="..." + /> + + + + settingsStore.setState({ + aivisCloudModelUuid: e.target.value, + }) + } + placeholder="..." + /> + +
+ + settingsStore.setState({ + aivisCloudUseStyleName: value, + }) + } + /> +
+ {aivisCloudUseStyleName ? ( + + + settingsStore.setState({ + aivisCloudStyleName: e.target.value, + }) + } + placeholder={t('StyleNamePlaceholder')} + /> + + ) : ( + + + settingsStore.setState({ + aivisCloudStyleId: Number(e.target.value), + }) + } + placeholder="0" + /> + + )} +
+ ) + case 'gsvitts': + return ( +
+ + + settingsStore.setState({ gsviTtsServerUrl: e.target.value }) + } + placeholder="http://127.0.0.1:5000/tts" + /> + + + + settingsStore.setState({ gsviTtsModelId: e.target.value }) + } + placeholder="0" + /> + +
+ ) + case 'elevenlabs': + return ( +
+ + + settingsStore.setState({ elevenlabsApiKey: e.target.value }) + } + placeholder="..." + /> + + + + settingsStore.setState({ elevenlabsVoiceId: e.target.value }) + } + placeholder="..." + /> + +
+ ) + case 'cartesia': + return ( +
+ + + settingsStore.setState({ cartesiaApiKey: e.target.value }) + } + placeholder="..." + /> + + + + settingsStore.setState({ cartesiaVoiceId: e.target.value }) + } + placeholder="..." + /> + +
+ ) + case 'openai': + return ( +
+ + + settingsStore.setState({ openaiKey: e.target.value }) + } + placeholder="sk-..." + /> + + + + + + + +
+ ) + case 'azure': + return ( +
+ + + settingsStore.setState({ azureTTSKey: e.target.value }) + } + placeholder="..." + /> + + + + settingsStore.setState({ azureTTSEndpoint: e.target.value }) + } + placeholder="https://resource.openai.azure.com/openai/deployments/deployment/audio/speech?api-version=2024-05-01-preview" + /> + + + + +
+ ) + } + } + return (
@@ -130,68 +801,61 @@ const QuickStart = () => { description={t('QuickStartAIConversationDescription')} > - +
+ + + + + {selectedServiceOption?.label} + + + + + + {aiServiceOptions.map((option) => ( + + `relative cursor-pointer select-none px-4 py-2 ${ + active ? 'bg-white-hover' : '' + }` + } + > + {({ selected }) => ( +
+ + + {option.label} + +
+ )} +
+ ))} +
+
+
-
- { - settingsStore.setState({ selectAIModel: model }) - updateMultiModalModeForModel( - modelState.selectAIService as AIService, - model - ) - }} - onCustomModelToggle={() => - settingsStore.setState({ customModel: !modelState.customModel }) - } + {serviceConfig?.keyLabel && ( + -
- {modelState.selectAIService !== 'dify' && ( -
- - { - const value = parseInt(e.target.value) - if (!Number.isNaN(value) && value >= 1) { - settingsStore.setState({ maxTokens: value }) - } - }} - /> - - - { - const value = parseInt(e.target.value) - if (!Number.isNaN(value) && value >= 1 && value <= 9999) { - settingsStore.setState({ maxPastMessages: value }) - } - }} - /> - -
)} + {renderQuickAIServiceSettings()}
{ selectVoice: e.target.value as AIVoice, }) } - className="w-full rounded-lg bg-white px-4 py-2 hover:bg-white-hover sm:w-col-span-2" + className={`${inputClassName} ${choiceControlClassName}`} > @@ -231,6 +895,7 @@ const QuickStart = () => { + {renderQuickVoiceSettings()}
Date: Tue, 23 Jun 2026 15:28:47 +0200 Subject: [PATCH 3/4] Update audio mode e2e expectation --- tests/e2e/settings-modes.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/e2e/settings-modes.spec.ts b/tests/e2e/settings-modes.spec.ts index 526abfdb..53dd9edd 100644 --- a/tests/e2e/settings-modes.spec.ts +++ b/tests/e2e/settings-modes.spec.ts @@ -9,6 +9,8 @@ import { readPersistedSetting, } from './helpers/app' +const OPENAI_AUDIO_MODE_MODEL = 'gpt-4o-mini-audio-preview' + async function blockExternalRequests(page: Page) { await page.route('**/*', (route) => { const url = new URL(route.request().url()) @@ -208,13 +210,13 @@ test('enforces mode exclusions for real-time API and audio modes from settings U await clickElement(page.getByTestId('audio-mode-toggle')) await expectPersistedSetting(page, 'audioMode', true) await expectPersistedSetting(page, 'realtimeAPIMode', false) - await expectPersistedSetting(page, 'selectAIModel', 'tts-1') + await expectPersistedSetting(page, 'selectAIModel', OPENAI_AUDIO_MODE_MODEL) await clickElement(page.getByTestId('audio-mode-toggle')) await expectPersistedSetting(page, 'audioMode', false) await expect .poll(() => readPersistedSetting(page, 'selectAIModel')) - .not.toBe('tts-1') + .not.toBe(OPENAI_AUDIO_MODE_MODEL) await openSettingsTab(page, 'idle') await switchAfter(page, 'Idle Mode').click() From 0d9aa189a8d80fcf980a77130ec569409c33cec4 Mon Sep 17 00:00:00 2001 From: nikechan-x-worker Date: Tue, 23 Jun 2026 15:45:29 +0200 Subject: [PATCH 4/4] Address CodeRabbit quick start feedback --- locales/ja/translation.json | 4 +- src/components/settings/modelProvider.tsx | 4 +- .../settings/modelProvider/ServiceLogo.tsx | 5 +- src/components/settings/quickStart.tsx | 59 +++++++++++++------ 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/locales/ja/translation.json b/locales/ja/translation.json index 4ae1e80e..bf03dfd3 100644 --- a/locales/ja/translation.json +++ b/locales/ja/translation.json @@ -246,11 +246,11 @@ "QuickStartBasicsDescription": "表示名と言語など、最初に決める項目です。", "QuickStartDetailedDisplaySettings": "見た目や背景を詳しく設定", "QuickStartAIConversationTitle": "2. AIと会話", - "QuickStartAIConversationDescription": "迷ったらAPIキー、AIサービス、モデルだけ設定すれば大丈夫です。", + "QuickStartAIConversationDescription": "AIサービスに応じて、APIキー、エンドポイント、URL、モデルなど必要な項目だけを設定します。", "QuickStartDetailedAISettings": "AIを詳しく設定", "QuickStartDetailedMemorySettings": "記憶を詳しく設定", "QuickStartVoiceTitle": "3. 声", - "QuickStartVoiceDescription": "音声エンジンと、発話に必要なAPIキーや話者を設定します。", + "QuickStartVoiceDescription": "音声エンジンに応じて、APIキー、エンドポイント、話者、モデル、スタイルなど発話に必要な項目を設定します。", "QuickStartDetailedVoiceSettings": "声を詳しく設定", "QuickStartSpeechInputSettings": "マイク入力を設定", "QuickStartOptionalFeaturesTitle": "4. 必要になったら使う設定", diff --git a/src/components/settings/modelProvider.tsx b/src/components/settings/modelProvider.tsx index 54137150..89745d94 100644 --- a/src/components/settings/modelProvider.tsx +++ b/src/components/settings/modelProvider.tsx @@ -355,7 +355,7 @@ const ModelProvider = () => { >
- + {selectedServiceOption?.label} @@ -371,7 +371,7 @@ const ModelProvider = () => { > {({ selected }) => (
- + diff --git a/src/components/settings/modelProvider/ServiceLogo.tsx b/src/components/settings/modelProvider/ServiceLogo.tsx index 6dc2b7f1..afbc6d3f 100644 --- a/src/components/settings/modelProvider/ServiceLogo.tsx +++ b/src/components/settings/modelProvider/ServiceLogo.tsx @@ -1,6 +1,7 @@ import Image from 'next/image' +import { AIService } from '@/features/constants/settings' -const aiServiceLogos = { +const aiServiceLogos: Record = { openai: '/images/ai-logos/openai.svg', anthropic: '/images/ai-logos/anthropic.svg', google: '/images/ai-logos/google.svg', @@ -20,7 +21,7 @@ const aiServiceLogos = { } interface ServiceLogoProps { - service: keyof typeof aiServiceLogos + service: AIService } export const ServiceLogo = ({ service }: ServiceLogoProps) => { diff --git a/src/components/settings/quickStart.tsx b/src/components/settings/quickStart.tsx index 23a52c5e..dcb2f3ff 100644 --- a/src/components/settings/quickStart.tsx +++ b/src/components/settings/quickStart.tsx @@ -12,18 +12,18 @@ import { OpenAITTSVoice, } from '@/features/constants/settings' import { getOpenAITTSModels } from '@/features/constants/aiModels' -import { ModelSelector } from './modelProvider/ModelSelector' -import { ApiKeyInput } from './modelProvider/ApiKeyInput' -import { ServiceLogo } from './modelProvider/ServiceLogo' +import { ModelSelector } from '@/components/settings/modelProvider/ModelSelector' +import { ApiKeyInput } from '@/components/settings/modelProvider/ApiKeyInput' +import { ServiceLogo } from '@/components/settings/modelProvider/ServiceLogo' import { aiServiceOptions, getServiceConfigByKey, -} from './modelProvider/utils/aiServiceConfigs' -import { useAIServiceHandlers } from './modelProvider/hooks/useAIServiceHandlers' -import { useModelProviderState } from './modelProvider/hooks/useModelProviderState' -import { ToggleSwitch } from '../toggleSwitch' +} from '@/components/settings/modelProvider/utils/aiServiceConfigs' +import { useAIServiceHandlers } from '@/components/settings/modelProvider/hooks/useAIServiceHandlers' +import { useModelProviderState } from '@/components/settings/modelProvider/hooks/useModelProviderState' +import { ToggleSwitch } from '@/components/toggleSwitch' import { languageOptions } from '@/components/settings/languageOptions' -import speakers from '../speakers.json' +import speakers from '@/components/speakers.json' type SpeakerOption = { id: number | string @@ -31,10 +31,10 @@ type SpeakerOption = { } const inputClassName = - 'w-full rounded-lg bg-white px-4 py-2 hover:bg-white-hover' + 'theme-surface-control w-full rounded-lg border px-4 py-2 text-theme-default outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/20' const selectButtonClassName = - 'flex w-full cursor-pointer items-center justify-between rounded-lg border border-gray-300 bg-white px-4 py-2 text-left shadow-sm transition hover:bg-white-hover focus:border-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/20' + 'theme-surface-control flex w-full cursor-pointer items-center justify-between rounded-lg border px-4 py-2 text-left shadow-sm transition hover:border-primary/40 focus:border-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/20' const choiceControlClassName = 'w-full max-w-full sm:w-80' @@ -64,6 +64,21 @@ type QuickStartDestination = | 'idle' | 'other' +const isSpeakerOption = (value: unknown): value is SpeakerOption => { + if (typeof value !== 'object' || value === null) return false + + const speakerOption = value as { + id?: unknown + speaker?: unknown + } + + return ( + (typeof speakerOption.id === 'number' || + typeof speakerOption.id === 'string') && + typeof speakerOption.speaker === 'string' + ) +} + const QuickStart = () => { const { t } = useTranslation() const modelState = useModelProviderState() @@ -116,10 +131,17 @@ const QuickStart = () => { const fetchAivisSpeakers = async () => { try { const response = await fetch('/speakers_aivis.json') - const data = (await response.json()) as SpeakerOption[] - setAivisSpeakers(data) + if (!response.ok) { + throw new Error(`HTTP ${response.status}`) + } + + const data: unknown = await response.json() + setAivisSpeakers( + Array.isArray(data) ? data.filter(isSpeakerOption) : [] + ) } catch (error) { console.error('Failed to fetch AIVIS speakers:', error) + setAivisSpeakers([]) } } @@ -531,11 +553,12 @@ const QuickStart = () => { className={inputClassName} type="number" value={aivisCloudStyleId} - onChange={(e) => + onChange={(e) => { + const styleId = Number.parseInt(e.target.value, 10) settingsStore.setState({ - aivisCloudStyleId: Number(e.target.value), + aivisCloudStyleId: Number.isNaN(styleId) ? 0 : styleId, }) - } + }} placeholder="0" /> @@ -808,14 +831,14 @@ const QuickStart = () => {
- + {selectedServiceOption?.label} - + {aiServiceOptions.map((option) => ( { > {({ selected }) => (
- +