From a53bf1d6e480f431115aa7808de78c2193505d89 Mon Sep 17 00:00:00 2001 From: kurihada Date: Wed, 10 Dec 2025 16:24:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81DeepSeek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 535 +++++++++++--------------------------------- package.json | 19 +- src/core/agent.ts | 214 ++++++++---------- src/types/index.ts | 61 ++++- src/utils/config.ts | 94 ++++++-- 5 files changed, 363 insertions(+), 560 deletions(-) diff --git a/package-lock.json b/package-lock.json index e829890..3d8e739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,14 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@anthropic-ai/sdk": "^0.32.0", + "@ai-sdk/anthropic": "^2.0.54", + "@ai-sdk/deepseek": "^1.0.31", + "ai": "^5.0.108", "chalk": "^5.3.0", "commander": "^12.1.0", "inquirer": "^12.0.0", - "ora": "^8.1.0" + "ora": "^8.1.0", + "zod": "^4.1.13" }, "bin": { "ai-assist": "dist/index.js" @@ -24,35 +27,83 @@ "typescript": "^5.6.0" } }, - "node_modules/@anthropic-ai/sdk": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz", - "integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==", - "license": "MIT", + "node_modules/@ai-sdk/anthropic": { + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.54.tgz", + "integrity": "sha512-6OSFMkt5NkAchH7o0W+dI2h6yR8EPXx7Yl6txyh0gadLlkf1UU/ScyoYlkxAW8UtGju/+apvwVTdLYEQuIsVVQ==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.18" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "license": "MIT", + "node_modules/@ai-sdk/deepseek": { + "version": "1.0.31", + "resolved": "https://registry.npmjs.org/@ai-sdk/deepseek/-/deepseek-1.0.31.tgz", + "integrity": "sha512-Il7WJp8bA3CmlreYSl1YzCucGTn2e5P81IANYIIEeLtWrbK0Y9CLoOCROj8xKYyUSMKlINyGZX2uP79cKewtSg==", + "license": "Apache-2.0", "dependencies": { - "undici-types": "~5.26.4" + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.18" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" + "node_modules/@ai-sdk/gateway": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.18.tgz", + "integrity": "sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.18", + "@vercel/oidc": "3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz", + "integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.1", @@ -830,47 +881,56 @@ } } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.19.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz", "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==", + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.4" + "node_modules/@vercel/oidc": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.5.tgz", + "integrity": "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", + "node_modules/ai": { + "version": "5.0.108", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.108.tgz", + "integrity": "sha512-Jex3Lb7V41NNpuqJHKgrwoU6BCLHdI1Pg4qb4GJH4jRIDRXUBySJErHjyN4oTCwbiYCeb/8II9EnqSRPq9EifA==", + "license": "Apache-2.0", "dependencies": { - "event-target-shim": "^5.0.0" + "@ai-sdk/gateway": "2.0.18", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.18", + "@opentelemetry/api": "1.9.0" }, "engines": { - "node": ">=6.5" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" + "node": ">=18" }, - "engines": { - "node": ">= 8.0.0" + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/ansi-regex": { @@ -900,25 +960,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -991,18 +1032,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -1012,80 +1041,12 @@ "node": ">=18" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/emoji-regex": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/esbuild": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", @@ -1128,48 +1089,13 @@ "@esbuild/win32-x64": "0.27.1" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "license": "MIT", "engines": { - "node": ">=6" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" + "node": ">=18.0.0" } }, "node_modules/fsevents": { @@ -1187,15 +1113,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-east-asian-width": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", @@ -1208,43 +1125,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/get-tsconfig": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", @@ -1258,66 +1138,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/iconv-lite": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", @@ -1393,6 +1213,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/log-symbols": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", @@ -1421,36 +1247,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -1463,12 +1259,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", @@ -1478,46 +1268,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -1662,12 +1412,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -1712,33 +1456,9 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, "license": "MIT" }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -1805,6 +1525,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 0dbbfb0..4b42ae7 100644 --- a/package.json +++ b/package.json @@ -13,19 +13,28 @@ "dev": "tsx src/index.ts", "lint": "eslint src/**/*.ts" }, - "keywords": ["ai", "cli", "assistant", "claude", "terminal"], + "keywords": [ + "ai", + "cli", + "assistant", + "claude", + "terminal" + ], "author": "", "license": "MIT", "dependencies": { - "@anthropic-ai/sdk": "^0.32.0", + "@ai-sdk/anthropic": "^2.0.54", + "@ai-sdk/deepseek": "^1.0.31", + "ai": "^5.0.108", "chalk": "^5.3.0", "commander": "^12.1.0", + "inquirer": "^12.0.0", "ora": "^8.1.0", - "inquirer": "^12.0.0" + "zod": "^4.1.13" }, "devDependencies": { "@types/node": "^22.0.0", - "typescript": "^5.6.0", - "tsx": "^4.19.0" + "tsx": "^4.19.0", + "typescript": "^5.6.0" } } diff --git a/src/core/agent.ts b/src/core/agent.ts index 9a19fb3..68d3c1a 100644 --- a/src/core/agent.ts +++ b/src/core/agent.ts @@ -1,73 +1,66 @@ -import Anthropic from '@anthropic-ai/sdk'; -import type { Tool, ToolResult, Message, AgentConfig } from '../types/index.js'; +import { createAnthropic } from '@ai-sdk/anthropic'; +import { createDeepSeek } from '@ai-sdk/deepseek'; +import { generateText, streamText, type ModelMessage, type Tool as AITool, type LanguageModel } from 'ai'; +import type { Tool, ToolResult, Message, AgentConfig, ProviderType } from '../types/index.js'; +import { buildZodSchema } from '../types/index.js'; + +// Provider 工厂函数类型 +type ProviderFactory = (apiKey: string) => (model: string) => LanguageModel; + +// Provider 注册表 +const providers: Record = { + anthropic: (apiKey) => { + const client = createAnthropic({ apiKey }); + return (model) => client(model); + }, + deepseek: (apiKey) => { + const client = createDeepSeek({ apiKey }); + return (model) => client(model); + }, +}; export class Agent { - private client: Anthropic; + private getModel: (model: string) => LanguageModel; private config: AgentConfig; private tools: Map = new Map(); - private conversationHistory: Message[] = []; + private conversationHistory: ModelMessage[] = []; constructor(config: AgentConfig) { this.config = config; - this.client = new Anthropic({ - apiKey: config.apiKey, - }); + + const providerFactory = providers[config.provider]; + if (!providerFactory) { + throw new Error(`不支持的 provider: ${config.provider}`); + } + this.getModel = providerFactory(config.apiKey); } // 注册工具 - registerTool(tool: Tool): void { - this.tools.set(tool.name, tool); + registerTool(customTool: Tool): void { + this.tools.set(customTool.name, customTool); } - // 获取所有工具定义(用于 Claude API) - private getToolDefinitions(): Anthropic.Tool[] { - return Array.from(this.tools.values()).map((tool) => ({ - name: tool.name, - description: tool.description, - input_schema: { - type: 'object' as const, - properties: Object.fromEntries( - Object.entries(tool.parameters).map(([key, param]) => [ - key, - { - type: param.type, - description: param.description, - }, - ]) - ), - required: Object.entries(tool.parameters) - .filter(([, param]) => param.required) - .map(([key]) => key), - }, - })); - } + // 将自定义工具转换为 Vercel AI SDK 的工具格式 + private getVercelTools(): Record { + const vercelTools: Record = {}; - // 执行工具 - private async executeTool( - toolName: string, - input: Record - ): Promise { - const tool = this.tools.get(toolName); - if (!tool) { - return { - success: false, - output: '', - error: `Tool "${toolName}" not found`, - }; + for (const [name, customTool] of this.tools) { + const schema = buildZodSchema(customTool.parameters); + + vercelTools[name] = { + description: customTool.description, + inputSchema: schema, + execute: async (params) => { + const result = await customTool.execute(params as Record); + return result; + }, + } as AITool; } - try { - return await tool.execute(input); - } catch (error) { - return { - success: false, - output: '', - error: error instanceof Error ? error.message : String(error), - }; - } + return vercelTools; } - // 发送消息并处理响应 + // 发送消息并处理响应(流式) async chat( userMessage: string, onStream?: (text: string) => void @@ -78,84 +71,52 @@ export class Agent { content: userMessage, }); - const messages: Anthropic.MessageParam[] = this.conversationHistory.map( - (msg) => ({ - role: msg.role, - content: msg.content, - }) - ); - + const vercelTools = this.getVercelTools(); let fullResponse = ''; - // 循环处理,直到没有工具调用 - while (true) { - const response = await this.client.messages.create({ - model: this.config.model, - max_tokens: this.config.maxTokens, + if (onStream) { + // 流式模式 + const result = streamText({ + model: this.getModel(this.config.model), system: this.config.systemPrompt, - tools: this.getToolDefinitions(), - messages, - }); - - // 处理响应内容 - const textBlocks: string[] = []; - const toolUseBlocks: Anthropic.ToolUseBlock[] = []; - - for (const block of response.content) { - if (block.type === 'text') { - textBlocks.push(block.text); - if (onStream) { - onStream(block.text); + messages: this.conversationHistory, + tools: vercelTools, + maxOutputTokens: this.config.maxTokens, + onChunk: ({ chunk }) => { + if (chunk.type === 'tool-call') { + onStream(`\n[调用工具: ${chunk.toolName}]\n`); + } else if (chunk.type === 'tool-result') { + const output = (chunk as { output?: ToolResult }).output; + if (output && typeof output === 'object') { + if (output.success) { + onStream(`[结果: ${output.output}]\n`); + } else { + onStream(`[错误: ${output.error}]\n`); + } + } } - } else if (block.type === 'tool_use') { - toolUseBlocks.push(block); - } - } - - fullResponse += textBlocks.join(''); - - // 如果没有工具调用,结束循环 - if (toolUseBlocks.length === 0) { - break; - } - - // 添加 assistant 消息 - messages.push({ - role: 'assistant', - content: response.content, + }, }); - // 处理工具调用 - const toolResults: Anthropic.ToolResultBlockParam[] = []; - - for (const toolUse of toolUseBlocks) { - if (onStream) { - onStream(`\n[调用工具: ${toolUse.name}]\n`); - } - - const result = await this.executeTool( - toolUse.name, - toolUse.input as Record - ); - - if (onStream) { - onStream( - result.success ? `[结果: ${result.output}]\n` : `[错误: ${result.error}]\n` - ); - } - - toolResults.push({ - type: 'tool_result', - tool_use_id: toolUse.id, - content: result.success ? result.output : `Error: ${result.error}`, - }); + // 流式输出文本 + for await (const chunk of result.textStream) { + fullResponse += chunk; + onStream(chunk); } - // 添加工具结果 - messages.push({ - role: 'user', - content: toolResults, + // 等待完成 + await result.response; + } else { + // 非流式模式 + const result = await generateText({ + model: this.getModel(this.config.model), + system: this.config.systemPrompt, + messages: this.conversationHistory, + tools: vercelTools, + maxOutputTokens: this.config.maxTokens, }); + + fullResponse = result.text; } // 保存助手响应到历史 @@ -174,6 +135,13 @@ export class Agent { // 获取对话历史 getHistory(): Message[] { - return [...this.conversationHistory]; + return this.conversationHistory + .filter((msg): msg is ModelMessage & { role: 'user' | 'assistant' } => + msg.role === 'user' || msg.role === 'assistant' + ) + .map((msg) => ({ + role: msg.role, + content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content), + })); } } diff --git a/src/types/index.ts b/src/types/index.ts index 4fc7de2..73ecfca 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,37 +1,45 @@ +import { z } from 'zod'; + // 消息类型 export interface Message { role: 'user' | 'assistant'; content: string; } -// 工具定义 -export interface Tool { - name: string; - description: string; - parameters: Record; - execute: (params: Record) => Promise; -} - +// 工具参数定义 export interface ToolParameter { type: 'string' | 'number' | 'boolean' | 'array' | 'object'; description: string; required?: boolean; } +// 工具执行结果 export interface ToolResult { success: boolean; output: string; error?: string; } +// 工具定义(兼容 Vercel AI SDK 的 tool 格式) +export interface Tool { + name: string; + description: string; + parameters: Record; + execute: (params: Record) => Promise; +} + // 工具调用请求 export interface ToolCall { name: string; input: Record; } +// 支持的 Provider 类型 +export type ProviderType = 'anthropic' | 'deepseek'; + // Agent 配置 export interface AgentConfig { + provider: ProviderType; apiKey: string; model: string; maxTokens: number; @@ -43,3 +51,40 @@ export interface ConversationContext { messages: Message[]; workingDirectory: string; } + +// 将自定义 Tool 转换为 Vercel AI SDK 的 zod schema +export function buildZodSchema(parameters: Record): z.ZodObject> { + const schemaObj: Record = {}; + + for (const [key, param] of Object.entries(parameters)) { + let fieldSchema: z.ZodTypeAny; + + switch (param.type) { + case 'string': + fieldSchema = z.string().describe(param.description); + break; + case 'number': + fieldSchema = z.number().describe(param.description); + break; + case 'boolean': + fieldSchema = z.boolean().describe(param.description); + break; + case 'array': + fieldSchema = z.array(z.unknown()).describe(param.description); + break; + case 'object': + fieldSchema = z.record(z.string(), z.unknown()).describe(param.description); + break; + default: + fieldSchema = z.unknown().describe(param.description); + } + + if (!param.required) { + fieldSchema = fieldSchema.optional(); + } + + schemaObj[key] = fieldSchema; + } + + return z.object(schemaObj); +} diff --git a/src/utils/config.ts b/src/utils/config.ts index e74fa22..a02d0b8 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,17 +1,25 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; -import type { AgentConfig } from '../types/index.js'; +import type { AgentConfig, ProviderType } from '../types/index.js'; const CONFIG_DIR = path.join(os.homedir(), '.ai-terminal-assistant'); const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json'); interface StoredConfig { + provider?: ProviderType; apiKey?: string; + deepseekApiKey?: string; model?: string; maxTokens?: number; } +// 默认模型配置 +const DEFAULT_MODELS: Record = { + anthropic: 'claude-sonnet-4-20250514', + deepseek: 'deepseek-chat', +}; + // 默认系统提示词 const DEFAULT_SYSTEM_PROMPT = `你是一个运行在终端中的 AI 编程助手。你可以帮助用户: - 读取和写入文件 @@ -29,12 +37,14 @@ const DEFAULT_SYSTEM_PROMPT = `你是一个运行在终端中的 AI 编程助手 // 加载配置 export function loadConfig(): AgentConfig { - // 优先从环境变量获取 - const apiKey = process.env.ANTHROPIC_API_KEY; - const model = process.env.AI_MODEL || 'claude-sonnet-4-20250514'; + // 从环境变量获取 + const provider = (process.env.AI_PROVIDER as ProviderType) || 'anthropic'; + const anthropicApiKey = process.env.ANTHROPIC_API_KEY; + const deepseekApiKey = process.env.DEEPSEEK_API_KEY; + const model = process.env.AI_MODEL; const maxTokens = parseInt(process.env.AI_MAX_TOKENS || '4096', 10); - // 如果环境变量没有,尝试从配置文件读取 + // 从配置文件读取 let storedConfig: StoredConfig = {}; if (fs.existsSync(CONFIG_FILE)) { try { @@ -45,18 +55,32 @@ export function loadConfig(): AgentConfig { } } - const finalApiKey = apiKey || storedConfig.apiKey; + // 确定最终的 provider + const finalProvider = storedConfig.provider || provider; + + // 根据 provider 获取对应的 API Key + let finalApiKey: string | undefined; + if (finalProvider === 'anthropic') { + finalApiKey = anthropicApiKey || storedConfig.apiKey; + } else if (finalProvider === 'deepseek') { + finalApiKey = deepseekApiKey || storedConfig.deepseekApiKey; + } if (!finalApiKey) { - console.error('❌ 错误: 未设置 ANTHROPIC_API_KEY'); - console.error('请设置环境变量: export ANTHROPIC_API_KEY=your-api-key'); - console.error('或运行: ai-assist --init 进行初始化配置'); + const envVar = finalProvider === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'DEEPSEEK_API_KEY'; + console.error(`❌ 错误: 未设置 ${envVar}`); + console.error(`请设置环境变量: export ${envVar}=your-api-key`); + console.error('或运行: ai-assist init 进行初始化配置'); process.exit(1); } + // 确定模型 + const finalModel = model || storedConfig.model || DEFAULT_MODELS[finalProvider]; + return { + provider: finalProvider, apiKey: finalApiKey, - model: storedConfig.model || model, + model: finalModel, maxTokens: storedConfig.maxTokens || maxTokens, systemPrompt: DEFAULT_SYSTEM_PROMPT, }; @@ -91,24 +115,52 @@ export async function initConfig(): Promise { console.log('\n🔧 初始化 AI Terminal Assistant 配置\n'); + // 选择 provider + const { provider } = await inquirer.prompt([ + { + type: 'list', + name: 'provider', + message: '选择 AI 服务商:', + choices: [ + { name: 'Anthropic (Claude)', value: 'anthropic' }, + { name: 'DeepSeek', value: 'deepseek' }, + ], + default: 'anthropic', + }, + ]); + + // 根据 provider 显示不同的模型选项 + const modelChoices = + provider === 'anthropic' + ? [ + { name: 'Claude Sonnet 4 (推荐,平衡性能和成本)', value: 'claude-sonnet-4-20250514' }, + { name: 'Claude Opus 4 (最强,成本较高)', value: 'claude-opus-4-20250514' }, + { name: 'Claude 3.5 Haiku (快速,成本低)', value: 'claude-3-5-haiku-20241022' }, + ] + : [ + { name: 'DeepSeek Chat (推荐)', value: 'deepseek-chat' }, + { name: 'DeepSeek Reasoner (推理增强)', value: 'deepseek-reasoner' }, + ]; + + const apiKeyField = provider === 'anthropic' ? 'apiKey' : 'deepseekApiKey'; + const apiKeyMessage = + provider === 'anthropic' + ? '请输入你的 Anthropic API Key:' + : '请输入你的 DeepSeek API Key:'; + const answers = await inquirer.prompt([ { type: 'password', - name: 'apiKey', - message: '请输入你的 Anthropic API Key:', - validate: (input: string) => - input.length > 0 || 'API Key 不能为空', + name: apiKeyField, + message: apiKeyMessage, + validate: (input: string) => input.length > 0 || 'API Key 不能为空', }, { type: 'list', name: 'model', message: '选择默认模型:', - choices: [ - { name: 'Claude Sonnet 4 (推荐,平衡性能和成本)', value: 'claude-sonnet-4-20250514' }, - { name: 'Claude Opus 4 (最强,成本较高)', value: 'claude-opus-4-20250514' }, - { name: 'Claude 3.5 Haiku (快速,成本低)', value: 'claude-3-5-haiku-20241022' }, - ], - default: 'claude-sonnet-4-20250514', + choices: modelChoices, + default: DEFAULT_MODELS[provider as ProviderType], }, { type: 'number', @@ -118,7 +170,7 @@ export async function initConfig(): Promise { }, ]); - saveConfig(answers); + saveConfig({ provider, ...answers }); console.log('\n✅ 配置已保存到', CONFIG_FILE); console.log('现在可以运行 ai-assist 开始使用了!\n'); }