From 6d5f8853062265545bc4701f8058a7836f23af46 Mon Sep 17 00:00:00 2001 From: Jelf Date: Fri, 20 Sep 2024 22:18:41 +0800 Subject: [PATCH 01/11] feat: ai sdk --- extensions/void/package-lock.json | 509 +++++++++++++++++++ extensions/void/package.json | 5 + extensions/void/src/common/sendLLMMessage.ts | 227 +++------ 3 files changed, 575 insertions(+), 166 deletions(-) diff --git a/extensions/void/package-lock.json b/extensions/void/package-lock.json index cb1b8f87..f2b84f0f 100644 --- a/extensions/void/package-lock.json +++ b/extensions/void/package-lock.json @@ -8,7 +8,12 @@ "name": "void", "version": "0.0.1", "dependencies": { + "@ai-sdk/anthropic": "^0.0.50", + "@ai-sdk/azure": "^0.0.39", + "@ai-sdk/openai": "^0.0.60", "@anthropic-ai/sdk": "^0.27.1", + "ai": "^3.3.43", + "ollama-ai-provider": "^0.15.0", "openai": "^4.57.0" }, "devDependencies": { @@ -46,6 +51,239 @@ "vscode": "^1.89.0" } }, + "node_modules/@ai-sdk/anthropic": { + "version": "0.0.50", + "resolved": "https://registry.npmmirror.com/@ai-sdk/anthropic/-/anthropic-0.0.50.tgz", + "integrity": "sha512-++mqmFcUoQgjoCchAU6eVG3QfKdwkeJVNdMZ+jUiNdawn8diA6BlARlu7xFT4F7W3bcStfYv4hK1jwRyzAQtCg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/azure": { + "version": "0.0.39", + "resolved": "https://registry.npmmirror.com/@ai-sdk/azure/-/azure-0.0.39.tgz", + "integrity": "sha512-P6CdIwLfkvkhf2hHkbnhqLOkVMQLu7XlksJ2YvzS4yx2Iwl8fsKLVnaCWMX9SBlYfamT/oea+rpyF60JhkdMZg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/openai": "0.0.61", + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/azure/node_modules/@ai-sdk/openai": { + "version": "0.0.61", + "resolved": "https://registry.npmmirror.com/@ai-sdk/openai/-/openai-0.0.61.tgz", + "integrity": "sha512-yIJ70xU9sbDjVAaNoq+W+0jnAgIUsx4e9VTnoNPXNTIQRpgpLvQ7iG8GYNgujO4oX4sLiHsWpOEMzrSwD0mNmw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "0.0.60", + "resolved": "https://registry.npmmirror.com/@ai-sdk/openai/-/openai-0.0.60.tgz", + "integrity": "sha512-NEdDdv3o76jT6UeWHxP6I/lMYcjFQhQGQi/U2gVqW1PEU4Pjaud7tAVSy27IPbiRakg6GOzWrltI2JhZgAI1wg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "0.0.23", + "resolved": "https://registry.npmmirror.com/@ai-sdk/provider/-/provider-0.0.23.tgz", + "integrity": "sha512-oAc49O5+xypVrKM7EUU5P/Y4DUL4JZUWVxhejoAVOTOl3WZUEWsMbP3QZR+TrimQIsS0WR/n9UuF6U0jPdp0tQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "1.0.19", + "resolved": "https://registry.npmmirror.com/@ai-sdk/provider-utils/-/provider-utils-1.0.19.tgz", + "integrity": "sha512-p02Fq5Mnc8T6nwRBN1Iaou8YXvN1sDS6hbmJaD5UaRbXjizbh+8rpFS/o7jqAHTwf3uHCDitP3pnODyHdc/CDQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "eventsource-parser": "1.1.2", + "nanoid": "3.3.6", + "secure-json-parse": "2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/provider-utils/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/@ai-sdk/react": { + "version": "0.0.59", + "resolved": "https://registry.npmmirror.com/@ai-sdk/react/-/react-0.0.59.tgz", + "integrity": "sha512-1WbgO3J2/OoheMuNMxy5itJ3NVqOpqpAQxFNp7AoXgnDv4wDF4kTif61rTlKh7dCPvBHj2HXLmob+TrVFaWhYw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "1.0.19", + "@ai-sdk/ui-utils": "0.0.44", + "swr": "2.2.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19", + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/solid": { + "version": "0.0.47", + "resolved": "https://registry.npmmirror.com/@ai-sdk/solid/-/solid-0.0.47.tgz", + "integrity": "sha512-lVMxIxtuNqoo/TObSFGflEP2dUeJv7bfPQbS4jHTZGBNlyhgBRY2Xc19yNjA3QKRfvQNDVoQusqxn+18MiHJJQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "1.0.19", + "@ai-sdk/ui-utils": "0.0.44" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "solid-js": "^1.7.7" + }, + "peerDependenciesMeta": { + "solid-js": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/svelte": { + "version": "0.0.49", + "resolved": "https://registry.npmmirror.com/@ai-sdk/svelte/-/svelte-0.0.49.tgz", + "integrity": "sha512-gV0MhaWxkatjf7uJrCAHO3bWrihokNUwGhuMCgyG+y53lwJKAYhR0zCoDRM2HnTJ89fdnx/PVe3R9fOWEVY5qA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "1.0.19", + "@ai-sdk/ui-utils": "0.0.44", + "sswr": "2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "svelte": "^3.0.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils": { + "version": "0.0.44", + "resolved": "https://registry.npmmirror.com/@ai-sdk/ui-utils/-/ui-utils-0.0.44.tgz", + "integrity": "sha512-0qiyun/n5zqJzQs/WfQT86dZE5DiDhSHJc7b7ZGLYvNMztHkRQmak2zUCZP4IyGVZEicyEPQK6NEEpBgkmd3Dg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.19", + "json-schema": "0.4.0", + "secure-json-parse": "2.7.0", + "zod-to-json-schema": "3.23.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/vue": { + "version": "0.0.50", + "resolved": "https://registry.npmmirror.com/@ai-sdk/vue/-/vue-0.0.50.tgz", + "integrity": "sha512-eIWfxqpKwRdL3rxJMg1HDJcjfugFJGg4P934Tl69S7UCot2/U4BPZoESVJQFroS1elbKHaMRgv0ZJt1ddWQPjQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "1.0.19", + "@ai-sdk/ui-utils": "0.0.44", + "swrv": "1.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "vue": "^3.3.4" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -533,6 +771,15 @@ "node": ">= 8" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -564,6 +811,12 @@ "integrity": "sha512-qVqLpd49rmJA2nZzLVsmfS/aiiBpfVE95dHhPVwG0NmSBAt+riPxnj53wq2oBq5m4Q2RF1IWFEUpnZTgrQZfEQ==", "dev": true }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmmirror.com/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1000,6 +1253,73 @@ "node": ">= 8.0.0" } }, + "node_modules/ai": { + "version": "3.3.43", + "resolved": "https://registry.npmmirror.com/ai/-/ai-3.3.43.tgz", + "integrity": "sha512-B4susrnVOapUVRYWuzNp3ChFZ7lBB3w9VFofIDiwrAoOkrljImkZgE2cUdIvWzwj/v3DiBtfPsPbOv+S2YRkCQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.19", + "@ai-sdk/react": "0.0.59", + "@ai-sdk/solid": "0.0.47", + "@ai-sdk/svelte": "0.0.49", + "@ai-sdk/ui-utils": "0.0.44", + "@ai-sdk/vue": "0.0.50", + "@opentelemetry/api": "1.9.0", + "eventsource-parser": "1.1.2", + "json-schema": "0.4.0", + "jsondiffpatch": "0.6.0", + "nanoid": "3.3.6", + "secure-json-parse": "2.7.0", + "zod-to-json-schema": "3.23.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "openai": "^4.42.0", + "react": "^18 || ^19", + "sswr": "^2.1.0", + "svelte": "^3.0.0 || ^4.0.0", + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + }, + "react": { + "optional": true + }, + "sswr": { + "optional": true + }, + "svelte": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/ai/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1670,6 +1990,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1997,6 +2323,12 @@ "node": ">=0.3.1" } }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -2578,6 +2910,15 @@ "node": ">=6" } }, + "node_modules/eventsource-parser": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-1.1.2.tgz", + "integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==", + "license": "MIT", + "engines": { + "node": ">=14.18" + } + }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -3957,6 +4298,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3969,6 +4316,35 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "license": "MIT", + "dependencies": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/jsondiffpatch/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -5193,6 +5569,69 @@ "whatwg-fetch": "^3.6.20" } }, + "node_modules/ollama-ai-provider": { + "version": "0.15.0", + "resolved": "https://registry.npmmirror.com/ollama-ai-provider/-/ollama-ai-provider-0.15.0.tgz", + "integrity": "sha512-pBRv2PjOPFdjB2fxOcu4dV3oT6NUxUI+V3c0Cu3r8Dwv6WmhHkRGLMscyRMU4Q2YVAtbghrvkfBGQmaqpMh/KQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "@ai-sdk/provider-utils": "1.0.18", + "partial-json": "0.1.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/ollama-ai-provider/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.18", + "resolved": "https://registry.npmmirror.com/@ai-sdk/provider-utils/-/provider-utils-1.0.18.tgz", + "integrity": "sha512-9u/XE/dB1gsIGcxiC5JfGOLzUz+EKRXt66T8KYWwDg4x8d02P+fI/EPOgkf+T4oLBrcQgvs4GPXPKoXGPJxBbg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "0.0.23", + "eventsource-parser": "1.1.2", + "nanoid": "3.3.6", + "secure-json-parse": "2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/ollama-ai-provider/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5465,6 +5904,12 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "dev": true }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6248,6 +6693,12 @@ "loose-envify": "^1.1.0" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -6386,6 +6837,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/sswr": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/sswr/-/sswr-2.1.0.tgz", + "integrity": "sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==", + "license": "MIT", + "dependencies": { + "swrev": "^4.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -6687,6 +7150,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.5", + "resolved": "https://registry.npmmirror.com/swr/-/swr-2.2.5.tgz", + "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", + "license": "MIT", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/swrev": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/swrev/-/swrev-4.0.0.tgz", + "integrity": "sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==", + "license": "MIT" + }, + "node_modules/swrv": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/swrv/-/swrv-1.0.4.tgz", + "integrity": "sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g==", + "license": "Apache-2.0", + "peerDependencies": { + "vue": ">=3.2.26 < 4" + } + }, "node_modules/tailwindcss": { "version": "3.4.12", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.12.tgz", @@ -7180,6 +7671,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7575,6 +8075,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod-to-json-schema": { + "version": "3.23.2", + "resolved": "https://registry.npmmirror.com/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz", + "integrity": "sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.23.3" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/extensions/void/package.json b/extensions/void/package.json index ebccfe72..f0223115 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -137,7 +137,12 @@ "typescript-eslint": "^8.3.0" }, "dependencies": { + "@ai-sdk/anthropic": "^0.0.50", + "@ai-sdk/azure": "^0.0.39", + "@ai-sdk/openai": "^0.0.60", "@anthropic-ai/sdk": "^0.27.1", + "ai": "^3.3.43", + "ollama-ai-provider": "^0.15.0", "openai": "^4.57.0" } } diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index ca3c34bd..f68db66d 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -1,15 +1,27 @@ -import Anthropic from '@anthropic-ai/sdk'; -import OpenAI from 'openai'; - -// import ollama from 'ollama' +import { streamText } from 'ai' +import { createOpenAI, OpenAIProviderSettings } from '@ai-sdk/openai'; +import { anthropic, AnthropicProviderSettings } from '@ai-sdk/anthropic'; +import { AzureOpenAIProviderSettings, createAzure } from '@ai-sdk/azure'; +import { createOllama, OllamaProviderSettings } from 'ollama-ai-provider'; export type ApiConfig = { anthropic: { - apikey: string, + apikey: string, + /** @default 'claude-3-5-sonnet-20240620' */ + model?: string, + setting: AnthropicProviderSettings }, openai: { - apikey: string - }, + apikey: string + /** @default 'gpt-4o' */ + model?: string, + setting: OpenAIProviderSettings + }, + azure: { + apiKey: string, + deploymentId: string, + setting: AzureOpenAIProviderSettings + }, greptile: { apikey: string, githubPAT: string, @@ -19,14 +31,14 @@ export type ApiConfig = { branch: string // e.g. 'main' } }, - ollama: { - // TODO + ollama: { + /** @default 'llama3.1' */ + model: string + setting: OllamaProviderSettings }, whichApi: string } - - type OnText = (newText: string, fullText: string) => void export type LLMMessage = { @@ -54,95 +66,6 @@ type SendLLMMessageFnTypeExternal = (params: { abort: () => void } - - - -// Claude -const sendClaudeMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => { - - - const anthropic = new Anthropic({ apiKey: apiConfig.anthropic.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"] - - const stream = anthropic.messages.stream({ - model: "claude-3-5-sonnet-20240620", - max_tokens: 1024, - messages: messages, - }); - - let did_abort = false - - // when receive text - stream.on('text', (newText, fullText) => { - if (did_abort) return - onText(newText, fullText) - }) - - // when we get the final message on this stream (or when error/fail) - stream.on('finalMessage', (claude_response) => { - if (did_abort) return - // stringify the response's content - let content = claude_response.content.map(c => { if (c.type === 'text') { return c.text } }).join('\n'); - onFinalMessage(content) - }) - - - // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either - const abort = () => { - // stream.abort() // this doesnt appear to do anything, but it should try to stop claude from generating anymore - did_abort = true - } - - return { abort } - -}; - - - - -// OpenAI -const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => { - - let did_abort = false - let fullText = '' - - // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either - let abort: () => void = () => { did_abort = true } - - const openai = new OpenAI({ apiKey: apiConfig.openai.apikey, dangerouslyAllowBrowser: true }); - - openai.chat.completions.create({ - model: 'gpt-4o-2024-08-06', - messages: messages, - stream: true, - }) - .then(async response => { - abort = () => { - // response.controller.abort() // this isn't needed now, to keep consistency with claude will leave it commented - did_abort = true; - } - // when receive text - try { - for await (const chunk of response) { - if (did_abort) return; - const newText = chunk.choices[0]?.delta?.content || ''; - fullText += newText; - onText(newText, fullText); - } - onFinalMessage(fullText); - } - // when error/fail - catch (error) { - console.error('Error in OpenAI stream:', error); - onFinalMessage(fullText); - } - // when we get the final message on this stream - onFinalMessage(fullText) - }) - return { abort }; -}; - - - // Greptile // https://docs.greptile.com/api-reference/query // https://docs.greptile.com/quickstart#sample-response-streamed @@ -207,79 +130,51 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin .catch(e => { console.error('Error in Greptile stream:', e); onFinalMessage(fullText); - }); - return { abort } - - - } export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, onFinalMessage, apiConfig }) => { - if (!apiConfig) return { abort: () => { } } + if (!apiConfig) return { abort: () => { } } + const whichApi = apiConfig.whichApi + // TODO: create an @ai-sdk provider for greptile + if (whichApi === 'greptile') + return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }) - const whichApi = apiConfig.whichApi - - if (whichApi === 'anthropic') { - return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig }) - } - else if (whichApi === 'openai') { - return sendOpenAIMsg({ messages, onText, onFinalMessage, apiConfig }) - } - else if (whichApi === 'greptile') { - return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }) - } - else if (whichApi === 'ollama') { - return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig }) // TODO - } - else { - console.error(`Error: whichApi was ${whichApi}, which is not recognized!`) - return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig }) // TODO - } + const model = getAiModel(apiConfig.whichApi, apiConfig) + const abortController = new AbortController() + const abortSignal = abortController.signal + streamText({ + model, + messages, + abortSignal, + }).then(async (result) => { + let fullText = '' + for await (const textPart of result.textStream) { + fullText += textPart + onText(textPart, fullText) + } + onFinalMessage(fullText) + }) + return { abort: abortController.abort } } - -// Ollama -// const sendOllamaMsg: sendMsgFnType = ({ messages, onText, onFinalMessage }) => { - -// let did_abort = false -// let fullText = '' - -// // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either -// let abort: () => void = () => { -// did_abort = true -// } - -// ollama.chat({ model: 'llama3.1', messages: messages, stream: true }) -// .then(async response => { - -// abort = () => { -// // response.abort() // this isn't needed now, to keep consistency with claude will leave it commented for now -// did_abort = true; -// } - -// // when receive text -// try { -// for await (const part of response) { -// if (did_abort) return -// let newText = part.message.content -// fullText += newText -// onText(newText, fullText) -// } -// } -// // when error/fail -// catch (e) { -// onFinalMessage(fullText) -// return -// } - -// // when we get the final message on this stream -// onFinalMessage(fullText) -// }) - -// return { abort }; -// }; - +export const getAiModel = (provider: string, apiConfig?: ApiConfig | null) => { + switch (provider) { + case 'openai': return createOpenAI({ + apiKey: apiConfig?.openai.apikey, + })(apiConfig?.openai.model || 'gpt-4o') + case 'anthropic': return anthropic(apiConfig?.anthropic.model || 'claude-3-5-sonnet-20240620') + case 'ollama': return createOllama(apiConfig?.ollama.setting)(apiConfig?.ollama.model || 'llama3.1') + case 'azure': { + if (!apiConfig?.azure.deploymentId) { + throw new Error(`Error: azure deploymentId is not defined`) + } + return createAzure(apiConfig?.azure.setting)(apiConfig?.azure.deploymentId) + } + default: + throw new Error(`Error: provider was ${provider}, which is not recognized!`) + } +} From 78e10a5593839e4f741a6ad286c72343e260f9ba Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 02:15:19 +0800 Subject: [PATCH 02/11] feat: update ollama config --- extensions/void/package-lock.json | 363 ++++++++++++++++-- extensions/void/src/SidebarWebviewProvider.ts | 4 +- extensions/void/src/common/sendLLMMessage.ts | 202 +++++----- extensions/void/src/extension.ts | 9 +- 4 files changed, 449 insertions(+), 129 deletions(-) diff --git a/extensions/void/package-lock.json b/extensions/void/package-lock.json index f2b84f0f..6ebe684b 100644 --- a/extensions/void/package-lock.json +++ b/extensions/void/package-lock.json @@ -14,7 +14,8 @@ "@anthropic-ai/sdk": "^0.27.1", "ai": "^3.3.43", "ollama-ai-provider": "^0.15.0", - "openai": "^4.57.0" + "openai": "^4.57.0", + "zod": "^3.23.8" }, "devDependencies": { "@eslint/js": "^9.9.1", @@ -296,6 +297,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@anthropic-ai/sdk": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.27.3.tgz", @@ -336,11 +350,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -422,6 +444,35 @@ "node": ">=4" } }, + "node_modules/@babel/parser": { + "version": "7.25.6", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "peer": true, + "dependencies": { + "@babel/types": "^7.25.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.6", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -692,7 +743,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -706,7 +756,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -715,7 +764,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -723,14 +771,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -820,8 +866,7 @@ "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", @@ -1198,6 +1243,118 @@ "node": ">=16" } }, + "node_modules/@vue/compiler-core": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.7.tgz", + "integrity": "sha512-A0gay3lK71MddsSnGlBxRPOugIVdACze9L/rCo5X5srCyjQfZOfYtSFMJc3aOZCM+xN55EQpb4R97rYn/iEbSw==", + "peer": true, + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.7", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "peer": true + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.7.tgz", + "integrity": "sha512-GYWl3+gO8/g0ZdYaJ18fYHdI/WVic2VuuUd1NsPp60DWXKy+XjdhFsDW7FbUto8siYYZcosBGn9yVBkjhq1M8Q==", + "peer": true, + "dependencies": { + "@vue/compiler-core": "3.5.7", + "@vue/shared": "3.5.7" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.7.tgz", + "integrity": "sha512-EjOJtCWJrC7HqoCEzOwpIYHm+JH7YmkxC1hG6VkqIukYRqj8KFUlTLK6hcT4nGgtVov2+ZfrdrRlcaqS78HnBA==", + "peer": true, + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.7", + "@vue/compiler-dom": "3.5.7", + "@vue/compiler-ssr": "3.5.7", + "@vue/shared": "3.5.7", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "peer": true + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.7.tgz", + "integrity": "sha512-oZx+jXP2k5arV/8Ly3TpQbfFyimMw2ANrRqvHJoKjPqtEzazxQGZjCLOfq8TnZ3wy2TOXdqfmVp4q7FyYeHV4g==", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.7", + "@vue/shared": "3.5.7" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.7.tgz", + "integrity": "sha512-yF0EpokpOHRNXyn/h6abXc9JFIzfdAf0MJHIi92xxCWS0mqrXH6+2aZ+A6EbSrspGzX5MHTd5N8iBA28HnXu9g==", + "peer": true, + "dependencies": { + "@vue/shared": "3.5.7" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.7.tgz", + "integrity": "sha512-OzLpBpKbZEaZVSNfd+hQbfBrDKux+b7Yl5hYhhWWWhHD7fEpF+CdI3Brm5k5GsufHEfvMcjruPxwQZuBN6nFYQ==", + "peer": true, + "dependencies": { + "@vue/reactivity": "3.5.7", + "@vue/shared": "3.5.7" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.7.tgz", + "integrity": "sha512-fL7cETfE27U2jyTgqzE382IGFY6a6uyznErn27KbbEzNctzxxUWYDbaN3B55l9nXh0xW2LRWPuWKOvjtO2UewQ==", + "peer": true, + "dependencies": { + "@vue/reactivity": "3.5.7", + "@vue/runtime-core": "3.5.7", + "@vue/shared": "3.5.7", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.7.tgz", + "integrity": "sha512-peRypij815eIDjpPpPXvYQGYqPH6QXwLJGWraJYPPn8JqWGl29A8QXnS7/Mh3TkMiOcdsJNhbFCoW2Agc2NgAQ==", + "peer": true, + "dependencies": { + "@vue/compiler-ssr": "3.5.7", + "@vue/shared": "3.5.7" + }, + "peerDependencies": { + "vue": "3.5.7" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.7.tgz", + "integrity": "sha512-NBE1PBIvzIedxIc2RZiKXvGbJkrZ2/hLf3h8GlS4/sP9xcXEZMFWOazFkNd6aGeUCMaproe5MHVYB3/4AW9q9g==", + "peer": true + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -1213,7 +1370,6 @@ "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1400,6 +1556,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -1587,6 +1752,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -2047,6 +2221,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2127,6 +2314,19 @@ "node": ">= 8" } }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "peer": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2142,8 +2342,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/data-view-buffer": { "version": "1.0.1", @@ -2387,6 +2586,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", @@ -2893,6 +3104,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "peer": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3952,6 +4172,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "peer": true, + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4277,8 +4506,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -4418,6 +4646,12 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "peer": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4469,7 +4703,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -4483,6 +4716,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -4662,6 +4904,12 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "peer": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5373,7 +5621,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -5959,11 +6206,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "peer": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "dev": true + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6008,7 +6265,6 @@ "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6286,7 +6542,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6822,7 +7077,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7150,6 +7404,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svelte": { + "version": "4.2.19", + "resolved": "https://registry.npmmirror.com/svelte/-/svelte-4.2.19.tgz", + "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/swr": { "version": "2.2.5", "resolved": "https://registry.npmmirror.com/swr/-/swr-2.2.5.tgz", @@ -7337,6 +7616,15 @@ "node": ">=0.8" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7493,7 +7781,7 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7728,6 +8016,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vue": { + "version": "3.5.7", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.7.tgz", + "integrity": "sha512-JcFm0f5j8DQO9E07pZRxqZ/ZsNopMVzHYXpKvnfqXFcA4JTi+4YcrikRn9wkzWsdj0YsLzlLIsR0zzGxA2P6Wg==", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.7", + "@vue/compiler-sfc": "3.5.7", + "@vue/runtime-dom": "3.5.7", + "@vue/server-renderer": "3.5.7", + "@vue/shared": "3.5.7" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "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", @@ -8075,6 +8384,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmmirror.com/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zod-to-json-schema": { "version": "3.23.2", "resolved": "https://registry.npmmirror.com/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz", diff --git a/extensions/void/src/SidebarWebviewProvider.ts b/extensions/void/src/SidebarWebviewProvider.ts index fe01c800..7fd8c83e 100644 --- a/extensions/void/src/SidebarWebviewProvider.ts +++ b/extensions/void/src/SidebarWebviewProvider.ts @@ -55,7 +55,9 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider { const nonce = getNonce(); // only scripts with the nonce are allowed to run, this is a recommended security measure - const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com'] + // Allow Ollama endpoint + const ollamaEndpoint = vscode.workspace.getConfiguration('void').get('ollamaSettings.endpoint') || 'http://localhost:11434' + const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com', ollamaEndpoint ] webview.html = ` diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index f68db66d..758c4f8e 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -1,70 +1,70 @@ import { streamText } from 'ai' import { createOpenAI, OpenAIProviderSettings } from '@ai-sdk/openai'; -import { anthropic, AnthropicProviderSettings } from '@ai-sdk/anthropic'; +import { AnthropicProviderSettings, createAnthropic } from '@ai-sdk/anthropic'; import { AzureOpenAIProviderSettings, createAzure } from '@ai-sdk/azure'; import { createOllama, OllamaProviderSettings } from 'ollama-ai-provider'; export type ApiConfig = { - anthropic: { - apikey: string, + anthropic: { + apiKey: string, /** @default 'claude-3-5-sonnet-20240620' */ model?: string, - setting: AnthropicProviderSettings - }, - openai: { - apikey: string + setting?: AnthropicProviderSettings + }, + openai: { + apiKey: string, /** @default 'gpt-4o' */ model?: string, - setting: OpenAIProviderSettings + setting?: OpenAIProviderSettings }, azure: { apiKey: string, deploymentId: string, - setting: AzureOpenAIProviderSettings + setting?: AzureOpenAIProviderSettings + }, + greptile: { + apikey: string, + githubPAT: string, + repoinfo: { + remote: string, // e.g. 'github' + repository: string, // e.g. 'voideditor/void' + branch: string // e.g. 'main' + } }, - greptile: { - apikey: string, - githubPAT: string, - repoinfo: { - remote: string, // e.g. 'github' - repository: string, // e.g. 'voideditor/void' - branch: string // e.g. 'main' - } - }, ollama: { /** @default 'llama3.1' */ model: string setting: OllamaProviderSettings - }, - whichApi: string + }, + whichApi: string } type OnText = (newText: string, fullText: string) => void export type LLMMessage = { - role: 'user' | 'assistant', - content: string + role: 'user' | 'assistant', + content: string } type SendLLMMessageFnTypeInternal = (params: { - messages: LLMMessage[], - onText: OnText, - onFinalMessage: (input: string) => void, - apiConfig: ApiConfig, + messages: LLMMessage[], + onText: OnText, + onFinalMessage: (input: string) => void, + apiConfig: ApiConfig, }) - => { - abort: () => void - } + => { + abort: () => void + } type SendLLMMessageFnTypeExternal = (params: { - messages: LLMMessage[], - onText: OnText, - onFinalMessage: (input: string) => void, - apiConfig: ApiConfig | null, + messages: LLMMessage[], + onText: OnText, + onFinalMessage: (input: string) => void, + apiConfig: ApiConfig | null, }) - => { - abort: () => void - } + => { + abort: () => void + } // Greptile // https://docs.greptile.com/api-reference/query @@ -72,66 +72,66 @@ type SendLLMMessageFnTypeExternal = (params: { const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => { - let did_abort = false - let fullText = '' + let did_abort = false + let fullText = '' - // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either - let abort: () => void = () => { did_abort = true } + // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either + let abort: () => void = () => { did_abort = true } - fetch('https://api.greptile.com/v2/query', { - method: 'POST', - headers: { - "Authorization": `Bearer ${apiConfig.greptile.apikey}`, - "X-Github-Token": `${apiConfig.greptile.githubPAT}`, - "Content-Type": `application/json`, - }, - body: JSON.stringify({ - messages, - stream: true, - repositories: [apiConfig.greptile.repoinfo] - }), - }) - // this is {message}\n{message}\n{message}...\n - .then(async response => { - const text = await response.text() - console.log('got greptile', text) - return JSON.parse(`[${text.trim().split('\n').join(',')}]`) - }) - // TODO make this actually stream, right now it just sends one message at the end - .then(async responseArr => { - if (did_abort) - return + fetch('https://api.greptile.com/v2/query', { + method: 'POST', + headers: { + "Authorization": `Bearer ${apiConfig.greptile.apikey}`, + "X-Github-Token": `${apiConfig.greptile.githubPAT}`, + "Content-Type": `application/json`, + }, + body: JSON.stringify({ + messages, + stream: true, + repositories: [apiConfig.greptile.repoinfo] + }), + }) + // this is {message}\n{message}\n{message}...\n + .then(async response => { + const text = await response.text() + console.log('got greptile', text) + return JSON.parse(`[${text.trim().split('\n').join(',')}]`) + }) + // TODO make this actually stream, right now it just sends one message at the end + .then(async responseArr => { + if (did_abort) + return - for (let response of responseArr) { + for (let response of responseArr) { - const type: string = response['type'] - const message = response['message'] + const type: string = response['type'] + const message = response['message'] - // when receive text - if (type === 'message') { - fullText += message - onText(message, fullText) - } - else if (type === 'sources') { - const { filepath, linestart, lineend } = message as { filepath: string, linestart: number | null, lineend: number | null } - fullText += filepath - onText(filepath, fullText) - } - // type: 'status' with an empty 'message' means last message - else if (type === 'status') { - if (!message) { - onFinalMessage(fullText) - } - } - } + // when receive text + if (type === 'message') { + fullText += message + onText(message, fullText) + } + else if (type === 'sources') { + const { filepath, linestart, lineend } = message as { filepath: string, linestart: number | null, lineend: number | null } + fullText += filepath + onText(filepath, fullText) + } + // type: 'status' with an empty 'message' means last message + else if (type === 'status') { + if (!message) { + onFinalMessage(fullText) + } + } + } - }) - .catch(e => { - console.error('Error in Greptile stream:', e); - onFinalMessage(fullText); - }); - return { abort } + }) + .catch(e => { + console.error('Error in Greptile stream:', e); + onFinalMessage(fullText); + }); + return { abort } } @@ -139,10 +139,10 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, if (!apiConfig) return { abort: () => { } } const whichApi = apiConfig.whichApi // TODO: create an @ai-sdk provider for greptile - if (whichApi === 'greptile') + if (whichApi === 'greptile') return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }) - const model = getAiModel(apiConfig.whichApi, apiConfig) + const model = getAiModel(apiConfig) const abortController = new AbortController() const abortSignal = abortController.signal streamText({ @@ -161,20 +161,18 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, return { abort: abortController.abort } } -export const getAiModel = (provider: string, apiConfig?: ApiConfig | null) => { - switch (provider) { - case 'openai': return createOpenAI({ - apiKey: apiConfig?.openai.apikey, - })(apiConfig?.openai.model || 'gpt-4o') - case 'anthropic': return anthropic(apiConfig?.anthropic.model || 'claude-3-5-sonnet-20240620') - case 'ollama': return createOllama(apiConfig?.ollama.setting)(apiConfig?.ollama.model || 'llama3.1') - case 'azure': { - if (!apiConfig?.azure.deploymentId) { +export const getAiModel = (apiConfig: ApiConfig) => { + switch (apiConfig.whichApi) { + case 'openai': return createOpenAI(apiConfig.openai.setting)(apiConfig.openai.model || 'gpt-4o') + case 'anthropic': return createAnthropic(apiConfig.anthropic.setting)(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') + case 'ollama': return createOllama(apiConfig.ollama.setting)(apiConfig.ollama.model || 'llama3.1') + case 'azure': { + if (!apiConfig.azure.deploymentId) { throw new Error(`Error: azure deploymentId is not defined`) } - return createAzure(apiConfig?.azure.setting)(apiConfig?.azure.deploymentId) + return createAzure(apiConfig.azure.setting)(apiConfig.azure.deploymentId) } default: - throw new Error(`Error: provider was ${provider}, which is not recognized!`) + throw new Error(`Error: provider was ${apiConfig.whichApi}, which is not recognized!`) } } diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 4cd8ac26..03016d0f 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -13,8 +13,8 @@ const readFileContentOfUri = async (uri: vscode.Uri) => { const getApiConfig = () => { const apiConfig: ApiConfig = { - anthropic: { apikey: vscode.workspace.getConfiguration('void').get('anthropicApiKey') ?? '' }, - openai: { apikey: vscode.workspace.getConfiguration('void').get('openAIApiKey') ?? '' }, + anthropic: { apiKey: vscode.workspace.getConfiguration('void').get('anthropicApiKey') ?? '' }, + openai: { apiKey: vscode.workspace.getConfiguration('void').get('openAIApiKey') ?? '' }, greptile: { apikey: vscode.workspace.getConfiguration('void').get('greptileApiKey') ?? '', githubPAT: vscode.workspace.getConfiguration('void').get('githubPAT') ?? '', @@ -25,7 +25,10 @@ const getApiConfig = () => { } }, ollama: { - // apikey: vscode.workspace.getConfiguration('void').get('ollamaSettings') ?? '', + model: vscode.workspace.getConfiguration('void').get('ollamaSettings.model') ?? '', + setting: { + baseURL: vscode.workspace.getConfiguration('void').get('ollamaSettings.baseURL') ?? '', + }, }, whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? '' } From 7b31f2841a90a89c98e79ff5d509bdaec3da4909 Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 14:16:27 +0800 Subject: [PATCH 03/11] chore: improve settings (#1) * refactor: refine `anthropic` and `openai` settings * refactor: improve configuration * chore: move to headers config * fix: ollama baseURL, add description * chore: change azure deploymentId * chore: eslint * chore: update allowed_urls --- extensions/void/package.json | 308 ++++++++++++++++-- extensions/void/src/SidebarWebviewProvider.ts | 18 +- extensions/void/src/common/sendLLMMessage.ts | 266 ++++++++------- extensions/void/src/extension.ts | 232 ++++++------- 4 files changed, 553 insertions(+), 271 deletions(-) diff --git a/extensions/void/package.json b/extensions/void/package.json index f0223115..4d6d54b0 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -15,35 +15,297 @@ "configuration": { "title": "API Keys", "properties": { - "void.whichApi": { + "void.provider": { "type": "string", "default": "anthropic", - "description": "Choose a model to use (anthropic | openai | greptile | ollama)" + "description": "Choose a provider (openai | anthropic | azure | greptile | ollama)", + "enum": [ + "openai", + "anthropic", + "azure", + "greptile", + "ollama" + ] }, - "void.anthropicApiKey": { - "type": "string", - "default": "", - "description": "Anthropic API Key" + "void.anthropic": { + "type": "object", + "properties": { + "model": { + "type": "string", + "default": "claude-3-5-sonnet-20240620", + "description": "Choose a model ('claude-3-5-sonnet-20240620' | 'claude-3-opus-20240229' | 'claude-3-sonnet-20240229' | 'claude-3-haiku-20240307')", + "enum": [ + "claude-3-5-sonnet-20240620", + "claude-3-opus-20240229", + "claude-3-sonnet-20240229", + "claude-3-haiku-20240307" + ] + }, + "providerSettings": { + "type": "object", + "properties": { + "baseURL": { + "type": "string", + "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." + }, + "apiKey": { + "type": "string", + "description": "Anthropic API key." + }, + "headers": { + "type": "object", + "description": "Custom headers to include in the requests." + } + } + } + } }, - "void.openAIApiKey": { - "type": "string", - "default": "", - "description": "OpenAI API Key" + "void.openai": { + "type": "object", + "properties": { + "model": { + "type": "string", + "default": "gpt-4o", + "description": "Choose a model ('o1-preview' | 'o1-mini' | 'gpt-4o' | 'gpt-4o-2024-05-13' | 'gpt-4o-2024-08-06' | 'gpt-4o-mini' | 'gpt-4o-mini-2024-07-18' | 'gpt-4-turbo' | 'gpt-4-turbo-2024-04-09' | 'gpt-4-turbo-preview' | 'gpt-4-0125-preview' | 'gpt-4-1106-preview' | 'gpt-4' | 'gpt-4-0613' | 'gpt-3.5-turbo-0125' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-1106')", + "enum": [ + "o1-preview", + "o1-mini", + "gpt-4o", + "gpt-4o-2024-05-13", + "gpt-4o-2024-08-06", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-turbo-preview", + "gpt-4-0125-preview", + "gpt-4-1106-preview", + "gpt-4", + "gpt-4-0613", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo", + "gpt-3.5-turbo-1106" + ] + }, + "providerSettings": { + "type": "object", + "properties": { + "baseURL": { + "type": "string", + "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." + }, + "apiKey": { + "type": "string", + "description": "OpenAI API key." + }, + "headers": { + "type": "object", + "description": "Custom headers to include in the requests." + }, + "organization": { + "type": "string", + "description": "OpenAI Organization." + }, + "project": { + "type": "string", + "description": "OpenAI project." + }, + "compatibility": { + "type": "string", + "description": "OpenAI compatibility mode. Should be set to `strict` when using the OpenAI API, and `compatible` when using 3rd party providers. In `compatible` mode, newer information such as streamOptions are not being sent. Defaults to 'compatible'.", + "enum": [ + "strict", + "compatible" + ] + } + } + } + } }, - "void.greptileApiKey": { - "type": "string", - "default": "", - "description": "Greptile API Key" + "void.azure": { + "type": "object", + "properties": { + "deploymentId": { + "type": "string", + "description": "Azure API deployment ID." + }, + "providerSettings": { + "type": "object", + "properties": { + "apiKey": { + "type": "string", + "description": "Azure API key." + }, + "baseURL": { + "type": "string", + "description": "Azure API base URL." + }, + "resourceName": { + "type": "string", + "description": "Name of the Azure OpenAI resource. Either this or `baseURL` can be used. \nThe resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/deployments/{modelId}{path}`" + }, + "headers": { + "type": "object", + "description": "Custom headers to include in the requests." + } + } + } + } }, - "void.githubPAT": { - "type": "string", - "default": "", - "description": "Greptile - Github PAT (gives Greptile access to your repo)" + "void.ollama": { + "type": "object", + "properties": { + "model": { + "type": "string", + "description": "Ollama model. 'codegemma' | 'codegemma:2b' | 'codegemma:7b' | 'codellama' | 'codellama:7b' | 'codellama:13b' | 'codellama:34b' | 'codellama:70b' | 'codellama:code' | 'codellama:python' | 'command-r' | 'command-r:35b' | 'command-r-plus' | 'command-r-plus:104b' | 'deepseek-coder-v2' | 'deepseek-coder-v2:16b' | 'deepseek-coder-v2:236b' | 'falcon2' | 'falcon2:11b' | 'firefunction-v2' | 'firefunction-v2:70b' | 'gemma' | 'gemma:2b' | 'gemma:7b' | 'gemma2' | 'gemma2:2b' | 'gemma2:9b' | 'gemma2:27b' | 'llama2' | 'llama2:7b' | 'llama2:13b' | 'llama2:70b' | 'llama3' | 'llama3:8b' | 'llama3:70b' | 'llama3-chatqa' | 'llama3-chatqa:8b' | 'llama3-chatqa:70b' | 'llama3-gradient' | 'llama3-gradient:8b' | 'llama3-gradient:70b' | 'llama3.1' | 'llama3.1:8b' | 'llama3.1:70b' | 'llama3.1:405b' | 'llava' | 'llava:7b' | 'llava:13b' | 'llava:34b' | 'llava-llama3' | 'llava-llama3:8b' | 'llava-phi3' | 'llava-phi3:3.8b' | 'mistral' | 'mistral:7b' | 'mistral-large' | 'mistral-large:123b' | 'mistral-nemo' | 'mistral-nemo:12b' | 'mixtral' | 'mixtral:8x7b' | 'mixtral:8x22b' | 'moondream' | 'moondream:1.8b' | 'openhermes' | 'openhermes:v2.5' | 'phi3' | 'phi3:3.8b' | 'phi3:14b' | 'phi3.5' | 'phi3.5:3.8b' | 'qwen' | 'qwen:7b' | 'qwen:14b' | 'qwen:32b' | 'qwen:72b' | 'qwen:110b' | 'qwen2' | 'qwen2:0.5b' | 'qwen2:1.5b' | 'qwen2:7b' | 'qwen2:72b' | 'smollm' | 'smollm:135m' | 'smollm:360m' | 'smollm:1.7b'", + "default": "llama3.1", + "enum": [ + "codegemma", + "codegemma:2b", + "codegemma:7b", + "codellama", + "codellama:7b", + "codellama:13b", + "codellama:34b", + "codellama:70b", + "codellama:code", + "codellama:python", + "command-r", + "command-r:35b", + "command-r-plus", + "command-r-plus:104b", + "deepseek-coder-v2", + "deepseek-coder-v2:16b", + "deepseek-coder-v2:236b", + "falcon2", + "falcon2:11b", + "firefunction-v2", + "firefunction-v2:70b", + "gemma", + "gemma:2b", + "gemma:7b", + "gemma2", + "gemma2:2b", + "gemma2:9b", + "gemma2:27b", + "llama2", + "llama2:7b", + "llama2:13b", + "llama2:70b", + "llama3", + "llama3:8b", + "llama3:70b", + "llama3-chatqa", + "llama3-chatqa:8b", + "llama3-chatqa:70b", + "llama3-gradient", + "llama3-gradient:8b", + "llama3-gradient:70b", + "llama3.1", + "llama3.1:8b", + "llama3.1:70b", + "llama3.1:405b", + "llava", + "llava:7b", + "llava:13b", + "llava:34b", + "llava-llama3", + "llava-llama3:8b", + "llava-phi3", + "llava-phi3:3.8b", + "mistral", + "mistral:7b", + "mistral-large", + "mistral-large:123b", + "mistral-nemo", + "mistral-nemo:12b", + "mixtral", + "mixtral:8x7b", + "mixtral:8x22b", + "moondream", + "moondream:1.8b", + "openhermes", + "openhermes:v2.5", + "phi3", + "phi3:3.8b", + "phi3:14b", + "phi3.5", + "phi3.5:3.8b", + "qwen", + "qwen:7b", + "qwen:14b", + "qwen:32b", + "qwen:72b", + "qwen:110b", + "qwen2", + "qwen2:0.5b", + "qwen2:1.5b", + "qwen2:7b", + "qwen2:72b", + "smollm", + "smollm:135m", + "smollm:360m", + "smollm:1.7b" + ] + }, + "providerSettings": { + "type": "object", + "properties": { + "baseURL": { + "type": "string", + "description": "Ollama base URL. Local API server can be started with `OLLAMA_ORIGINS=* ollama serve`" + }, + "headers": { + "type": "object", + "description": "Custom headers to include in the requests." + } + } + } + } }, - "void.ollamaSettings": { - "type": "string", - "default": "", - "description": "Ollama settings (coming soon...)" + "void.greptile": { + "type": "object", + "properties": { + "providerSettings": { + "type": "object", + "properties": { + "apikey": { + "type": "string", + "description": "Greptile API key." + }, + "headers": { + "type": "object", + "description": "Custom headers to include in the requests.", + "properties": { + "X-Github-Token": { + "type": "string", + "description": "GitHub PAT." + } + } + }, + "repoinfo": { + "type": "array", + "items": { + "type": "object", + "properties": { + "remote": { + "type": "string", + "description": "GitHub remote." + }, + "repository": { + "type": "string", + "description": "GitHub repository." + }, + "branch": { + "type": "string", + "description": "GitHub branch." + } + } + } + } + } + } + } } } }, @@ -145,4 +407,4 @@ "ollama-ai-provider": "^0.15.0", "openai": "^4.57.0" } -} +} \ No newline at end of file diff --git a/extensions/void/src/SidebarWebviewProvider.ts b/extensions/void/src/SidebarWebviewProvider.ts index 7fd8c83e..c95e1e12 100644 --- a/extensions/void/src/SidebarWebviewProvider.ts +++ b/extensions/void/src/SidebarWebviewProvider.ts @@ -1,5 +1,6 @@ // renders the code from `src/sidebar` +import { AzureOpenAIProviderSettings } from '@ai-sdk/azure'; import * as vscode from 'vscode'; function getNonce() { @@ -55,9 +56,20 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider { const nonce = getNonce(); // only scripts with the nonce are allowed to run, this is a recommended security measure - // Allow Ollama endpoint - const ollamaEndpoint = vscode.workspace.getConfiguration('void').get('ollamaSettings.endpoint') || 'http://localhost:11434' - const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com', ollamaEndpoint ] + const ollamaBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('ollama.providerSettings.baseURL') || 'http://127.0.0.1:11434').toString() + const azureProviderSettings = vscode.workspace.getConfiguration('void').get('azure.providerSettings') as AzureOpenAIProviderSettings + const azureBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('azure.providerSettings.baseURL') || `https://${azureProviderSettings.resourceName}.openai.azure.com/openai/deployments`).toString() + const openaiBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('openai.providerSettings.baseURL') || 'https://api.openai.com').toString() + const greptileBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('greptile.providerSettings.baseURL') || 'https://api.mistral.ai').toString() + const anthropicBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('anthropic.providerSettings.baseURL') || 'https://api.mistral.ai').toString() + const allowed_urls = [ + ollamaBaseURL, + azureBaseURL, + openaiBaseURL, + greptileBaseURL, + anthropicBaseURL + ] + webview.html = ` diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index 758c4f8e..256f2ca2 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -5,66 +5,67 @@ import { AzureOpenAIProviderSettings, createAzure } from '@ai-sdk/azure'; import { createOllama, OllamaProviderSettings } from 'ollama-ai-provider'; export type ApiConfig = { - anthropic: { - apiKey: string, - /** @default 'claude-3-5-sonnet-20240620' */ - model?: string, - setting?: AnthropicProviderSettings - }, - openai: { - apiKey: string, - /** @default 'gpt-4o' */ - model?: string, - setting?: OpenAIProviderSettings - }, - azure: { - apiKey: string, - deploymentId: string, - setting?: AzureOpenAIProviderSettings - }, - greptile: { - apikey: string, - githubPAT: string, - repoinfo: { - remote: string, // e.g. 'github' - repository: string, // e.g. 'voideditor/void' - branch: string // e.g. 'main' - } - }, - ollama: { - /** @default 'llama3.1' */ - model: string - setting: OllamaProviderSettings - }, - whichApi: string + /** @default 'anthropic' */ + provider: 'anthropic' | 'openai' | 'azure' | 'greptile' | 'ollama' + anthropic: { + /** @default 'claude-3-5-sonnet-20240620' */ + model: string, + providerSettings?: AnthropicProviderSettings + }, + openai: { + /** @default 'gpt-4o' */ + model: string, + providerSettings?: OpenAIProviderSettings + }, + azure: { + deploymentId: string, + providerSettings?: AzureOpenAIProviderSettings + }, + greptile: { + providerSettings?: { + apikey?: string, + githubPAT?: string, + headers?: Record, + repoinfo?: { + remote?: string, // e.g. 'github' + repository?: string, // e.g. 'voideditor/void' + branch?: string // e.g. 'main' + }[] + } + }, + ollama: { + /** @default 'llama3.1' */ + model: string + providerSettings?: OllamaProviderSettings + }, } type OnText = (newText: string, fullText: string) => void export type LLMMessage = { - role: 'user' | 'assistant', - content: string + role: 'user' | 'assistant', + content: string } type SendLLMMessageFnTypeInternal = (params: { - messages: LLMMessage[], - onText: OnText, - onFinalMessage: (input: string) => void, - apiConfig: ApiConfig, + messages: LLMMessage[], + onText: OnText, + onFinalMessage: (input: string) => void, + apiConfig: ApiConfig, }) - => { - abort: () => void - } + => { + abort: () => void + } type SendLLMMessageFnTypeExternal = (params: { - messages: LLMMessage[], - onText: OnText, - onFinalMessage: (input: string) => void, - apiConfig: ApiConfig | null, + messages: LLMMessage[], + onText: OnText, + onFinalMessage: (input: string) => void, + apiConfig: ApiConfig | null, }) - => { - abort: () => void - } + => { + abort: () => void + } // Greptile // https://docs.greptile.com/api-reference/query @@ -72,107 +73,102 @@ type SendLLMMessageFnTypeExternal = (params: { const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => { - let did_abort = false - let fullText = '' + let did_abort = false + let fullText = '' - // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either - let abort: () => void = () => { did_abort = true } + // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either + let abort: () => void = () => { did_abort = true } - fetch('https://api.greptile.com/v2/query', { - method: 'POST', - headers: { - "Authorization": `Bearer ${apiConfig.greptile.apikey}`, - "X-Github-Token": `${apiConfig.greptile.githubPAT}`, - "Content-Type": `application/json`, - }, - body: JSON.stringify({ - messages, - stream: true, - repositories: [apiConfig.greptile.repoinfo] - }), - }) - // this is {message}\n{message}\n{message}...\n - .then(async response => { - const text = await response.text() - console.log('got greptile', text) - return JSON.parse(`[${text.trim().split('\n').join(',')}]`) - }) - // TODO make this actually stream, right now it just sends one message at the end - .then(async responseArr => { - if (did_abort) - return + fetch('https://api.greptile.com/v2/query', { + method: 'POST', + headers: { + "Authorization": `Bearer ${apiConfig.greptile.providerSettings?.apikey}`, + "Content-Type": `application/json`, + ...apiConfig.greptile.providerSettings?.headers + }, + body: JSON.stringify({ + messages, + stream: true, + repositories: apiConfig.greptile.providerSettings?.repoinfo + }), + }) + // this is {message}\n{message}\n{message}...\n + .then(async response => { + const text = await response.text() + console.log('got greptile', text) + return JSON.parse(`[${text.trim().split('\n').join(',')}]`) + }) + // TODO make this actually stream, right now it just sends one message at the end + .then(async responseArr => { + if (did_abort) + return - for (let response of responseArr) { + for (let response of responseArr) { - const type: string = response['type'] - const message = response['message'] + const type: string = response['type'] + const message = response['message'] - // when receive text - if (type === 'message') { - fullText += message - onText(message, fullText) - } - else if (type === 'sources') { - const { filepath, linestart, lineend } = message as { filepath: string, linestart: number | null, lineend: number | null } - fullText += filepath - onText(filepath, fullText) - } - // type: 'status' with an empty 'message' means last message - else if (type === 'status') { - if (!message) { - onFinalMessage(fullText) - } - } - } + // when receive text + if (type === 'message') { + fullText += message + onText(message, fullText) + } + else if (type === 'sources') { + const { filepath, linestart, lineend } = message as { filepath: string, linestart: number | null, lineend: number | null } + fullText += filepath + onText(filepath, fullText) + } + // type: 'status' with an empty 'message' means last message + else if (type === 'status') { + if (!message) { + onFinalMessage(fullText) + } + } + } - }) - .catch(e => { - console.error('Error in Greptile stream:', e); - onFinalMessage(fullText); - }); - return { abort } + }) + .catch(e => { + console.error('Error in Greptile stream:', e); + onFinalMessage(fullText); + }); + return { abort } } export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, onFinalMessage, apiConfig }) => { - if (!apiConfig) return { abort: () => { } } - const whichApi = apiConfig.whichApi - // TODO: create an @ai-sdk provider for greptile - if (whichApi === 'greptile') - return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }) + if (!apiConfig) return { abort: () => { } } + const provider = apiConfig.provider + // TODO: create an @ai-sdk provider for greptile + if (provider === 'greptile') + return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }) - const model = getAiModel(apiConfig) - const abortController = new AbortController() - const abortSignal = abortController.signal - streamText({ - model, - messages, - abortSignal, - }).then(async (result) => { - let fullText = '' - for await (const textPart of result.textStream) { - fullText += textPart - onText(textPart, fullText) - } - onFinalMessage(fullText) - }) + const model = getAiModel(apiConfig) + const abortController = new AbortController() + const abortSignal = abortController.signal + streamText({ + model, + messages, + abortSignal, + }).then(async (result) => { + let fullText = '' + for await (const textPart of result.textStream) { + fullText += textPart + onText(textPart, fullText) + } + onFinalMessage(fullText) + }) - return { abort: abortController.abort } + return { abort: abortController.abort } } export const getAiModel = (apiConfig: ApiConfig) => { - switch (apiConfig.whichApi) { - case 'openai': return createOpenAI(apiConfig.openai.setting)(apiConfig.openai.model || 'gpt-4o') - case 'anthropic': return createAnthropic(apiConfig.anthropic.setting)(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') - case 'ollama': return createOllama(apiConfig.ollama.setting)(apiConfig.ollama.model || 'llama3.1') - case 'azure': { - if (!apiConfig.azure.deploymentId) { - throw new Error(`Error: azure deploymentId is not defined`) - } - return createAzure(apiConfig.azure.setting)(apiConfig.azure.deploymentId) - } - default: - throw new Error(`Error: provider was ${apiConfig.whichApi}, which is not recognized!`) - } + switch (apiConfig.provider) { + case 'openai': return createOpenAI(apiConfig.openai.providerSettings)(apiConfig.openai.model || 'gpt-4o') + case 'anthropic': return createAnthropic(apiConfig.anthropic.providerSettings)(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') + case 'ollama': return createOllama(apiConfig.ollama.providerSettings)(apiConfig.ollama.model || 'llama3.1') + case 'azure': return createAzure(apiConfig.azure.providerSettings)(`${apiConfig.azure.deploymentId}`) + default: + throw new Error(`Error: provider was ${apiConfig.provider}, which is not recognized!`) + } } diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 03016d0f..a0bdc9b5 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -1,156 +1,168 @@ import * as vscode from 'vscode'; import { WebviewMessage } from './shared_types'; -import { CtrlKCodeLensProvider } from './CtrlKCodeLensProvider'; import { getDiffedLines } from './getDiffedLines'; -import { ApprovalCodeLensProvider, SuggestedEdit } from './ApprovalCodeLensProvider'; +import { ApprovalCodeLensProvider } from './ApprovalCodeLensProvider'; import { SidebarWebviewProvider } from './SidebarWebviewProvider'; import { ApiConfig } from './common/sendLLMMessage'; const readFileContentOfUri = async (uri: vscode.Uri) => { - return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8').replace(/\r\n/g, '\n'); // must remove windows \r or every line will appear different because of it + return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8').replace(/\r\n/g, '\n'); // must remove windows \r or every line will appear different because of it } const getApiConfig = () => { - const apiConfig: ApiConfig = { - anthropic: { apiKey: vscode.workspace.getConfiguration('void').get('anthropicApiKey') ?? '' }, - openai: { apiKey: vscode.workspace.getConfiguration('void').get('openAIApiKey') ?? '' }, - greptile: { - apikey: vscode.workspace.getConfiguration('void').get('greptileApiKey') ?? '', - githubPAT: vscode.workspace.getConfiguration('void').get('githubPAT') ?? '', - repoinfo: { - remote: 'github', - repository: 'TODO', - branch: 'main' - } - }, - ollama: { - model: vscode.workspace.getConfiguration('void').get('ollamaSettings.model') ?? '', - setting: { - baseURL: vscode.workspace.getConfiguration('void').get('ollamaSettings.baseURL') ?? '', + const apiConfig: ApiConfig = { + provider: vscode.workspace.getConfiguration('void').get('provider') ?? 'anthropic', + anthropic: { + model: vscode.workspace.getConfiguration('void').get('anthropic.model') ?? 'claude-3-5-sonnet-20240620', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('anthropic.providerSettings') ?? {}, }, - }, - whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? '' - } - return apiConfig + }, + openai: { + model: vscode.workspace.getConfiguration('void').get('openai.model') ?? 'gpt-4o', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('openai.providerSettings') ?? {}, + }, + }, + azure: { + deploymentId: vscode.workspace.getConfiguration('void').get('azure.deploymentId') ?? '', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('azure.providerSettings') ?? {}, + }, + }, + ollama: { + model: vscode.workspace.getConfiguration('void').get('ollama.model') ?? 'llama3.1', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('ollama.providerSettings') ?? {}, + }, + }, + greptile: { + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('greptile.providerSettings') ?? {}, + } + }, + } + console.log(apiConfig); + return apiConfig } export function activate(context: vscode.ExtensionContext) { - // 1. Mount the chat sidebar - const webviewProvider = new SidebarWebviewProvider(context); - context.subscriptions.push( - vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, webviewProvider, { webviewOptions: { retainContextWhenHidden: true } }) - ); + // 1. Mount the chat sidebar + const webviewProvider = new SidebarWebviewProvider(context); + context.subscriptions.push( + vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, webviewProvider, { webviewOptions: { retainContextWhenHidden: true } }) + ); - // 2. Activate the sidebar on ctrl+l - context.subscriptions.push( - vscode.commands.registerCommand('void.ctrl+l', () => { + // 2. Activate the sidebar on ctrl+l + context.subscriptions.push( + vscode.commands.registerCommand('void.ctrl+l', () => { - const editor = vscode.window.activeTextEditor - if (!editor) - return + const editor = vscode.window.activeTextEditor + if (!editor) + return - // show the sidebar - vscode.commands.executeCommand('workbench.view.extension.voidViewContainer'); - // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar + // show the sidebar + vscode.commands.executeCommand('workbench.view.extension.voidViewContainer'); + // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar - // get the text the user is selecting - const selectionStr = editor.document.getText(editor.selection); + // get the text the user is selecting + const selectionStr = editor.document.getText(editor.selection); - // get the range of the selection - const selectionRange = editor.selection; + // get the range of the selection + const selectionRange = editor.selection; - // get the file the user is in - const filePath = editor.document.uri; + // get the file the user is in + const filePath = editor.document.uri; - // send message to the webview (Sidebar.tsx) - webviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, selectionRange, filePath } } satisfies WebviewMessage)); - }) - ); + // send message to the webview (Sidebar.tsx) + webviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, selectionRange, filePath } } satisfies WebviewMessage)); + }) + ); - // 3. Show an approve/reject codelens above each change - const approvalCodeLensProvider = new ApprovalCodeLensProvider(); - context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', approvalCodeLensProvider)); + // 3. Show an approve/reject codelens above each change + const approvalCodeLensProvider = new ApprovalCodeLensProvider(); + context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', approvalCodeLensProvider)); - // 4. Add approve/reject commands - context.subscriptions.push(vscode.commands.registerCommand('void.approveDiff', async (params) => { - approvalCodeLensProvider.approveDiff(params) - })); - context.subscriptions.push(vscode.commands.registerCommand('void.discardDiff', async (params) => { - approvalCodeLensProvider.discardDiff(params) - })); + // 4. Add approve/reject commands + context.subscriptions.push(vscode.commands.registerCommand('void.approveDiff', async (params) => { + approvalCodeLensProvider.approveDiff(params) + })); + context.subscriptions.push(vscode.commands.registerCommand('void.discardDiff', async (params) => { + approvalCodeLensProvider.discardDiff(params) + })); - // 5. - webviewProvider.webview.then( - webview => { + // 5. + webviewProvider.webview.then( + webview => { - // when config changes, send it to the sidebar - vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('void')) { - const apiConfig = getApiConfig() - webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) - } - }) + // when config changes, send it to the sidebar + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('void')) { + const apiConfig = getApiConfig() + webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) + } + }) - // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`) - webview.onDidReceiveMessage(async (m: WebviewMessage) => { + // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`) + webview.onDidReceiveMessage(async (m: WebviewMessage) => { - if (m.type === 'requestFiles') { + if (m.type === 'requestFiles') { - // get contents of all file paths - const files = await Promise.all( - m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) })) - ) + // get contents of all file paths + const files = await Promise.all( + m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) })) + ) - // send contents to webview - webview.postMessage({ type: 'files', files, } satisfies WebviewMessage) + // send contents to webview + webview.postMessage({ type: 'files', files, } satisfies WebviewMessage) - } else if (m.type === 'applyCode') { + } else if (m.type === 'applyCode') { - const editor = vscode.window.activeTextEditor - if (!editor) { - vscode.window.showInformationMessage('No active editor!') - return - } - const oldContents = await readFileContentOfUri(editor.document.uri) - const suggestedEdits = getDiffedLines(oldContents, m.code) - await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits) - } - else if (m.type === 'getApiConfig') { + const editor = vscode.window.activeTextEditor + if (!editor) { + vscode.window.showInformationMessage('No active editor!') + return + } + const oldContents = await readFileContentOfUri(editor.document.uri) + const suggestedEdits = getDiffedLines(oldContents, m.code) + await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits) + } + else if (m.type === 'getApiConfig') { - const apiConfig = getApiConfig() - console.log('Api config:', apiConfig) + const apiConfig = getApiConfig() + console.log('Api config:', apiConfig) - webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) + webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) - } - else { + } + else { - console.error('unrecognized command', m.type, m) - } - }) - } - ) + console.error('unrecognized command', m.type, m) + } + }) + } + ) - // Gets called when user presses ctrl + k (mounts ctrl+k-style codelens) - // TODO need to build this - // const ctrlKCodeLensProvider = new CtrlKCodeLensProvider(); - // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider)); - // context.subscriptions.push( - // vscode.commands.registerCommand('void.ctrl+k', () => { - // const editor = vscode.window.activeTextEditor; - // if (!editor) - // return - // ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection); - // // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses - // }) - // ) + // Gets called when user presses ctrl + k (mounts ctrl+k-style codelens) + // TODO need to build this + // const ctrlKCodeLensProvider = new CtrlKCodeLensProvider(); + // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider)); + // context.subscriptions.push( + // vscode.commands.registerCommand('void.ctrl+k', () => { + // const editor = vscode.window.activeTextEditor; + // if (!editor) + // return + // ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection); + // // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses + // }) + // ) } From d7a3c44e313f98f00c15b43653ccb34ce54d8241 Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 14:54:27 +0800 Subject: [PATCH 04/11] chore: eslint --- extensions/void/src/extension.ts | 244 +++++++++++++++---------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index a0bdc9b5..5c050edd 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -6,163 +6,163 @@ import { SidebarWebviewProvider } from './SidebarWebviewProvider'; import { ApiConfig } from './common/sendLLMMessage'; const readFileContentOfUri = async (uri: vscode.Uri) => { - return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8').replace(/\r\n/g, '\n'); // must remove windows \r or every line will appear different because of it + return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8').replace(/\r\n/g, '\n'); // must remove windows \r or every line will appear different because of it } const getApiConfig = () => { - const apiConfig: ApiConfig = { - provider: vscode.workspace.getConfiguration('void').get('provider') ?? 'anthropic', - anthropic: { - model: vscode.workspace.getConfiguration('void').get('anthropic.model') ?? 'claude-3-5-sonnet-20240620', - providerSettings: { - ...vscode.workspace.getConfiguration('void').get('anthropic.providerSettings') ?? {}, - }, - }, - openai: { - model: vscode.workspace.getConfiguration('void').get('openai.model') ?? 'gpt-4o', - providerSettings: { - ...vscode.workspace.getConfiguration('void').get('openai.providerSettings') ?? {}, - }, - }, - azure: { - deploymentId: vscode.workspace.getConfiguration('void').get('azure.deploymentId') ?? '', - providerSettings: { - ...vscode.workspace.getConfiguration('void').get('azure.providerSettings') ?? {}, - }, - }, - ollama: { - model: vscode.workspace.getConfiguration('void').get('ollama.model') ?? 'llama3.1', - providerSettings: { - ...vscode.workspace.getConfiguration('void').get('ollama.providerSettings') ?? {}, - }, - }, - greptile: { - providerSettings: { - ...vscode.workspace.getConfiguration('void').get('greptile.providerSettings') ?? {}, - } - }, - } - console.log(apiConfig); - return apiConfig + const apiConfig: ApiConfig = { + provider: vscode.workspace.getConfiguration('void').get('provider') ?? 'anthropic', + anthropic: { + model: vscode.workspace.getConfiguration('void').get('anthropic.model') ?? 'claude-3-5-sonnet-20240620', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('anthropic.providerSettings') ?? {}, + }, + }, + openai: { + model: vscode.workspace.getConfiguration('void').get('openai.model') ?? 'gpt-4o', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('openai.providerSettings') ?? {}, + }, + }, + azure: { + deploymentId: vscode.workspace.getConfiguration('void').get('azure.deploymentId') ?? '', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('azure.providerSettings') ?? {}, + }, + }, + ollama: { + model: vscode.workspace.getConfiguration('void').get('ollama.model') ?? 'llama3.1', + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('ollama.providerSettings') ?? {}, + }, + }, + greptile: { + providerSettings: { + ...vscode.workspace.getConfiguration('void').get('greptile.providerSettings') ?? {}, + } + }, + } + console.log(apiConfig); + return apiConfig } export function activate(context: vscode.ExtensionContext) { - // 1. Mount the chat sidebar - const webviewProvider = new SidebarWebviewProvider(context); - context.subscriptions.push( - vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, webviewProvider, { webviewOptions: { retainContextWhenHidden: true } }) - ); + // 1. Mount the chat sidebar + const webviewProvider = new SidebarWebviewProvider(context); + context.subscriptions.push( + vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, webviewProvider, { webviewOptions: { retainContextWhenHidden: true } }) + ); - // 2. Activate the sidebar on ctrl+l - context.subscriptions.push( - vscode.commands.registerCommand('void.ctrl+l', () => { + // 2. Activate the sidebar on ctrl+l + context.subscriptions.push( + vscode.commands.registerCommand('void.ctrl+l', () => { - const editor = vscode.window.activeTextEditor - if (!editor) - return + const editor = vscode.window.activeTextEditor + if (!editor) + return - // show the sidebar - vscode.commands.executeCommand('workbench.view.extension.voidViewContainer'); - // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar + // show the sidebar + vscode.commands.executeCommand('workbench.view.extension.voidViewContainer'); + // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar - // get the text the user is selecting - const selectionStr = editor.document.getText(editor.selection); + // get the text the user is selecting + const selectionStr = editor.document.getText(editor.selection); - // get the range of the selection - const selectionRange = editor.selection; + // get the range of the selection + const selectionRange = editor.selection; - // get the file the user is in - const filePath = editor.document.uri; + // get the file the user is in + const filePath = editor.document.uri; - // send message to the webview (Sidebar.tsx) - webviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, selectionRange, filePath } } satisfies WebviewMessage)); - }) - ); + // send message to the webview (Sidebar.tsx) + webviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, selectionRange, filePath } } satisfies WebviewMessage)); + }) + ); - // 3. Show an approve/reject codelens above each change - const approvalCodeLensProvider = new ApprovalCodeLensProvider(); - context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', approvalCodeLensProvider)); + // 3. Show an approve/reject codelens above each change + const approvalCodeLensProvider = new ApprovalCodeLensProvider(); + context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', approvalCodeLensProvider)); - // 4. Add approve/reject commands - context.subscriptions.push(vscode.commands.registerCommand('void.approveDiff', async (params) => { - approvalCodeLensProvider.approveDiff(params) - })); - context.subscriptions.push(vscode.commands.registerCommand('void.discardDiff', async (params) => { - approvalCodeLensProvider.discardDiff(params) - })); + // 4. Add approve/reject commands + context.subscriptions.push(vscode.commands.registerCommand('void.approveDiff', async (params) => { + approvalCodeLensProvider.approveDiff(params) + })); + context.subscriptions.push(vscode.commands.registerCommand('void.discardDiff', async (params) => { + approvalCodeLensProvider.discardDiff(params) + })); - // 5. - webviewProvider.webview.then( - webview => { + // 5. + webviewProvider.webview.then( + webview => { - // when config changes, send it to the sidebar - vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('void')) { - const apiConfig = getApiConfig() - webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) - } - }) + // when config changes, send it to the sidebar + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('void')) { + const apiConfig = getApiConfig() + webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) + } + }) - // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`) - webview.onDidReceiveMessage(async (m: WebviewMessage) => { + // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`) + webview.onDidReceiveMessage(async (m: WebviewMessage) => { - if (m.type === 'requestFiles') { + if (m.type === 'requestFiles') { - // get contents of all file paths - const files = await Promise.all( - m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) })) - ) + // get contents of all file paths + const files = await Promise.all( + m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) })) + ) - // send contents to webview - webview.postMessage({ type: 'files', files, } satisfies WebviewMessage) + // send contents to webview + webview.postMessage({ type: 'files', files, } satisfies WebviewMessage) - } else if (m.type === 'applyCode') { + } else if (m.type === 'applyCode') { - const editor = vscode.window.activeTextEditor - if (!editor) { - vscode.window.showInformationMessage('No active editor!') - return - } - const oldContents = await readFileContentOfUri(editor.document.uri) - const suggestedEdits = getDiffedLines(oldContents, m.code) - await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits) - } - else if (m.type === 'getApiConfig') { + const editor = vscode.window.activeTextEditor + if (!editor) { + vscode.window.showInformationMessage('No active editor!') + return + } + const oldContents = await readFileContentOfUri(editor.document.uri) + const suggestedEdits = getDiffedLines(oldContents, m.code) + await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits) + } + else if (m.type === 'getApiConfig') { - const apiConfig = getApiConfig() - console.log('Api config:', apiConfig) + const apiConfig = getApiConfig() + console.log('Api config:', apiConfig) - webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) + webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage) - } - else { + } + else { - console.error('unrecognized command', m.type, m) - } - }) - } - ) + console.error('unrecognized command', m.type, m) + } + }) + } + ) - // Gets called when user presses ctrl + k (mounts ctrl+k-style codelens) - // TODO need to build this - // const ctrlKCodeLensProvider = new CtrlKCodeLensProvider(); - // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider)); - // context.subscriptions.push( - // vscode.commands.registerCommand('void.ctrl+k', () => { - // const editor = vscode.window.activeTextEditor; - // if (!editor) - // return - // ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection); - // // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses - // }) - // ) + // Gets called when user presses ctrl + k (mounts ctrl+k-style codelens) + // TODO need to build this + // const ctrlKCodeLensProvider = new CtrlKCodeLensProvider(); + // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider)); + // context.subscriptions.push( + // vscode.commands.registerCommand('void.ctrl+k', () => { + // const editor = vscode.window.activeTextEditor; + // if (!editor) + // return + // ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection); + // // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses + // }) + // ) } From bf9029f9bc311c778b0c010016177183b6c1807f Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 14:56:15 +0800 Subject: [PATCH 05/11] chore: remove console.log --- extensions/void/src/extension.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 5c050edd..0b218bcd 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -43,7 +43,6 @@ const getApiConfig = () => { } }, } - console.log(apiConfig); return apiConfig } From 1beb011bfa2d9e7ce7ebb6241fa75f0b40839865 Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 15:45:36 +0800 Subject: [PATCH 06/11] refactor: configuration --- extensions/void/package.json | 397 +++++++++--------- extensions/void/src/SidebarWebviewProvider.ts | 8 +- extensions/void/src/common/sendLLMMessage.ts | 31 +- extensions/void/src/extension.ts | 6 + 4 files changed, 221 insertions(+), 221 deletions(-) diff --git a/extensions/void/package.json b/extensions/void/package.json index 4d6d54b0..972e1d3d 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -27,252 +27,233 @@ "ollama" ] }, - "void.anthropic": { + "void.anthropic.apiKey": { + "type": "string", + "description": "Anthropic API key." + }, + "void.anthropic.model": { + "type": "string", + "default": "claude-3-5-sonnet-20240620", + "description": "Choose a model ('claude-3-5-sonnet-20240620' | 'claude-3-opus-20240229' | 'claude-3-sonnet-20240229' | 'claude-3-haiku-20240307')", + "enum": [ + "claude-3-5-sonnet-20240620", + "claude-3-opus-20240229", + "claude-3-sonnet-20240229", + "claude-3-haiku-20240307" + ] + }, + "void.anthropic.providerSettings": { "type": "object", "properties": { - "model": { + "baseURL": { "type": "string", - "default": "claude-3-5-sonnet-20240620", - "description": "Choose a model ('claude-3-5-sonnet-20240620' | 'claude-3-opus-20240229' | 'claude-3-sonnet-20240229' | 'claude-3-haiku-20240307')", - "enum": [ - "claude-3-5-sonnet-20240620", - "claude-3-opus-20240229", - "claude-3-sonnet-20240229", - "claude-3-haiku-20240307" - ] + "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." }, - "providerSettings": { + "apiKey": {}, + "headers": { "type": "object", - "properties": { - "baseURL": { - "type": "string", - "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." - }, - "apiKey": { - "type": "string", - "description": "Anthropic API key." - }, - "headers": { - "type": "object", - "description": "Custom headers to include in the requests." - } - } + "description": "Custom headers to include in the requests." } } }, - "void.openai": { + "void.openai.model": { + "type": "string", + "default": "gpt-4o", + "description": "Choose a model ('o1-preview' | 'o1-mini' | 'gpt-4o' | 'gpt-4o-2024-05-13' | 'gpt-4o-2024-08-06' | 'gpt-4o-mini' | 'gpt-4o-mini-2024-07-18' | 'gpt-4-turbo' | 'gpt-4-turbo-2024-04-09' | 'gpt-4-turbo-preview' | 'gpt-4-0125-preview' | 'gpt-4-1106-preview' | 'gpt-4' | 'gpt-4-0613' | 'gpt-3.5-turbo-0125' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-1106')", + "enum": [ + "o1-preview", + "o1-mini", + "gpt-4o", + "gpt-4o-2024-05-13", + "gpt-4o-2024-08-06", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-turbo-preview", + "gpt-4-0125-preview", + "gpt-4-1106-preview", + "gpt-4", + "gpt-4-0613", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo", + "gpt-3.5-turbo-1106" + ] + }, + "void.openai.apiKey": { + "type": "string", + "description": "OpenAI API key." + }, + "void.openai.providerSettings": { "type": "object", "properties": { - "model": { + "baseURL": { "type": "string", - "default": "gpt-4o", - "description": "Choose a model ('o1-preview' | 'o1-mini' | 'gpt-4o' | 'gpt-4o-2024-05-13' | 'gpt-4o-2024-08-06' | 'gpt-4o-mini' | 'gpt-4o-mini-2024-07-18' | 'gpt-4-turbo' | 'gpt-4-turbo-2024-04-09' | 'gpt-4-turbo-preview' | 'gpt-4-0125-preview' | 'gpt-4-1106-preview' | 'gpt-4' | 'gpt-4-0613' | 'gpt-3.5-turbo-0125' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-1106')", - "enum": [ - "o1-preview", - "o1-mini", - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-2024-08-06", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-turbo-preview", - "gpt-4-0125-preview", - "gpt-4-1106-preview", - "gpt-4", - "gpt-4-0613", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo", - "gpt-3.5-turbo-1106" - ] + "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." }, - "providerSettings": { + "headers": { "type": "object", - "properties": { - "baseURL": { - "type": "string", - "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." - }, - "apiKey": { - "type": "string", - "description": "OpenAI API key." - }, - "headers": { - "type": "object", - "description": "Custom headers to include in the requests." - }, - "organization": { - "type": "string", - "description": "OpenAI Organization." - }, - "project": { - "type": "string", - "description": "OpenAI project." - }, - "compatibility": { - "type": "string", - "description": "OpenAI compatibility mode. Should be set to `strict` when using the OpenAI API, and `compatible` when using 3rd party providers. In `compatible` mode, newer information such as streamOptions are not being sent. Defaults to 'compatible'.", - "enum": [ - "strict", - "compatible" - ] - } - } + "description": "Custom headers to include in the requests." + }, + "organization": { + "type": "string", + "description": "OpenAI Organization." + }, + "project": { + "type": "string", + "description": "OpenAI project." + }, + "compatibility": { + "type": "string", + "description": "OpenAI compatibility mode. Should be set to `strict` when using the OpenAI API, and `compatible` when using 3rd party providers. In `compatible` mode, newer information such as streamOptions are not being sent. Defaults to 'compatible'.", + "enum": [ + "strict", + "compatible" + ] } } }, - "void.azure": { + "void.azure.deploymentId": { + "type": "string", + "description": "Azure API deployment ID." + }, + "void.azure.resourceName": { + "type": "string", + "description": "Name of the Azure OpenAI resource. Either this or `baseURL` can be used. \nThe resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/deployments/{modelId}{path}`" + }, + "void.azure.apiKey": { + "type": "string", + "description": "Azure API key." + }, + "void.azure.providerSettings": { "type": "object", "properties": { - "deploymentId": { + "baseURL": { "type": "string", - "description": "Azure API deployment ID." + "description": "Azure API base URL." }, - "providerSettings": { + "headers": { "type": "object", - "properties": { - "apiKey": { - "type": "string", - "description": "Azure API key." - }, - "baseURL": { - "type": "string", - "description": "Azure API base URL." - }, - "resourceName": { - "type": "string", - "description": "Name of the Azure OpenAI resource. Either this or `baseURL` can be used. \nThe resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/deployments/{modelId}{path}`" - }, - "headers": { - "type": "object", - "description": "Custom headers to include in the requests." - } - } + "description": "Custom headers to include in the requests." } } }, - "void.ollama": { + "void.ollama.model": { + "type": "string", + "description": "Ollama model. 'codegemma' | 'codegemma:2b' | 'codegemma:7b' | 'codellama' | 'codellama:7b' | 'codellama:13b' | 'codellama:34b' | 'codellama:70b' | 'codellama:code' | 'codellama:python' | 'command-r' | 'command-r:35b' | 'command-r-plus' | 'command-r-plus:104b' | 'deepseek-coder-v2' | 'deepseek-coder-v2:16b' | 'deepseek-coder-v2:236b' | 'falcon2' | 'falcon2:11b' | 'firefunction-v2' | 'firefunction-v2:70b' | 'gemma' | 'gemma:2b' | 'gemma:7b' | 'gemma2' | 'gemma2:2b' | 'gemma2:9b' | 'gemma2:27b' | 'llama2' | 'llama2:7b' | 'llama2:13b' | 'llama2:70b' | 'llama3' | 'llama3:8b' | 'llama3:70b' | 'llama3-chatqa' | 'llama3-chatqa:8b' | 'llama3-chatqa:70b' | 'llama3-gradient' | 'llama3-gradient:8b' | 'llama3-gradient:70b' | 'llama3.1' | 'llama3.1:8b' | 'llama3.1:70b' | 'llama3.1:405b' | 'llava' | 'llava:7b' | 'llava:13b' | 'llava:34b' | 'llava-llama3' | 'llava-llama3:8b' | 'llava-phi3' | 'llava-phi3:3.8b' | 'mistral' | 'mistral:7b' | 'mistral-large' | 'mistral-large:123b' | 'mistral-nemo' | 'mistral-nemo:12b' | 'mixtral' | 'mixtral:8x7b' | 'mixtral:8x22b' | 'moondream' | 'moondream:1.8b' | 'openhermes' | 'openhermes:v2.5' | 'phi3' | 'phi3:3.8b' | 'phi3:14b' | 'phi3.5' | 'phi3.5:3.8b' | 'qwen' | 'qwen:7b' | 'qwen:14b' | 'qwen:32b' | 'qwen:72b' | 'qwen:110b' | 'qwen2' | 'qwen2:0.5b' | 'qwen2:1.5b' | 'qwen2:7b' | 'qwen2:72b' | 'smollm' | 'smollm:135m' | 'smollm:360m' | 'smollm:1.7b'", + "default": "llama3.1", + "enum": [ + "codegemma", + "codegemma:2b", + "codegemma:7b", + "codellama", + "codellama:7b", + "codellama:13b", + "codellama:34b", + "codellama:70b", + "codellama:code", + "codellama:python", + "command-r", + "command-r:35b", + "command-r-plus", + "command-r-plus:104b", + "deepseek-coder-v2", + "deepseek-coder-v2:16b", + "deepseek-coder-v2:236b", + "falcon2", + "falcon2:11b", + "firefunction-v2", + "firefunction-v2:70b", + "gemma", + "gemma:2b", + "gemma:7b", + "gemma2", + "gemma2:2b", + "gemma2:9b", + "gemma2:27b", + "llama2", + "llama2:7b", + "llama2:13b", + "llama2:70b", + "llama3", + "llama3:8b", + "llama3:70b", + "llama3-chatqa", + "llama3-chatqa:8b", + "llama3-chatqa:70b", + "llama3-gradient", + "llama3-gradient:8b", + "llama3-gradient:70b", + "llama3.1", + "llama3.1:8b", + "llama3.1:70b", + "llama3.1:405b", + "llava", + "llava:7b", + "llava:13b", + "llava:34b", + "llava-llama3", + "llava-llama3:8b", + "llava-phi3", + "llava-phi3:3.8b", + "mistral", + "mistral:7b", + "mistral-large", + "mistral-large:123b", + "mistral-nemo", + "mistral-nemo:12b", + "mixtral", + "mixtral:8x7b", + "mixtral:8x22b", + "moondream", + "moondream:1.8b", + "openhermes", + "openhermes:v2.5", + "phi3", + "phi3:3.8b", + "phi3:14b", + "phi3.5", + "phi3.5:3.8b", + "qwen", + "qwen:7b", + "qwen:14b", + "qwen:32b", + "qwen:72b", + "qwen:110b", + "qwen2", + "qwen2:0.5b", + "qwen2:1.5b", + "qwen2:7b", + "qwen2:72b", + "smollm", + "smollm:135m", + "smollm:360m", + "smollm:1.7b" + ] + }, + "void.ollama.providerSettings": { "type": "object", "properties": { - "model": { + "baseURL": { "type": "string", - "description": "Ollama model. 'codegemma' | 'codegemma:2b' | 'codegemma:7b' | 'codellama' | 'codellama:7b' | 'codellama:13b' | 'codellama:34b' | 'codellama:70b' | 'codellama:code' | 'codellama:python' | 'command-r' | 'command-r:35b' | 'command-r-plus' | 'command-r-plus:104b' | 'deepseek-coder-v2' | 'deepseek-coder-v2:16b' | 'deepseek-coder-v2:236b' | 'falcon2' | 'falcon2:11b' | 'firefunction-v2' | 'firefunction-v2:70b' | 'gemma' | 'gemma:2b' | 'gemma:7b' | 'gemma2' | 'gemma2:2b' | 'gemma2:9b' | 'gemma2:27b' | 'llama2' | 'llama2:7b' | 'llama2:13b' | 'llama2:70b' | 'llama3' | 'llama3:8b' | 'llama3:70b' | 'llama3-chatqa' | 'llama3-chatqa:8b' | 'llama3-chatqa:70b' | 'llama3-gradient' | 'llama3-gradient:8b' | 'llama3-gradient:70b' | 'llama3.1' | 'llama3.1:8b' | 'llama3.1:70b' | 'llama3.1:405b' | 'llava' | 'llava:7b' | 'llava:13b' | 'llava:34b' | 'llava-llama3' | 'llava-llama3:8b' | 'llava-phi3' | 'llava-phi3:3.8b' | 'mistral' | 'mistral:7b' | 'mistral-large' | 'mistral-large:123b' | 'mistral-nemo' | 'mistral-nemo:12b' | 'mixtral' | 'mixtral:8x7b' | 'mixtral:8x22b' | 'moondream' | 'moondream:1.8b' | 'openhermes' | 'openhermes:v2.5' | 'phi3' | 'phi3:3.8b' | 'phi3:14b' | 'phi3.5' | 'phi3.5:3.8b' | 'qwen' | 'qwen:7b' | 'qwen:14b' | 'qwen:32b' | 'qwen:72b' | 'qwen:110b' | 'qwen2' | 'qwen2:0.5b' | 'qwen2:1.5b' | 'qwen2:7b' | 'qwen2:72b' | 'smollm' | 'smollm:135m' | 'smollm:360m' | 'smollm:1.7b'", - "default": "llama3.1", - "enum": [ - "codegemma", - "codegemma:2b", - "codegemma:7b", - "codellama", - "codellama:7b", - "codellama:13b", - "codellama:34b", - "codellama:70b", - "codellama:code", - "codellama:python", - "command-r", - "command-r:35b", - "command-r-plus", - "command-r-plus:104b", - "deepseek-coder-v2", - "deepseek-coder-v2:16b", - "deepseek-coder-v2:236b", - "falcon2", - "falcon2:11b", - "firefunction-v2", - "firefunction-v2:70b", - "gemma", - "gemma:2b", - "gemma:7b", - "gemma2", - "gemma2:2b", - "gemma2:9b", - "gemma2:27b", - "llama2", - "llama2:7b", - "llama2:13b", - "llama2:70b", - "llama3", - "llama3:8b", - "llama3:70b", - "llama3-chatqa", - "llama3-chatqa:8b", - "llama3-chatqa:70b", - "llama3-gradient", - "llama3-gradient:8b", - "llama3-gradient:70b", - "llama3.1", - "llama3.1:8b", - "llama3.1:70b", - "llama3.1:405b", - "llava", - "llava:7b", - "llava:13b", - "llava:34b", - "llava-llama3", - "llava-llama3:8b", - "llava-phi3", - "llava-phi3:3.8b", - "mistral", - "mistral:7b", - "mistral-large", - "mistral-large:123b", - "mistral-nemo", - "mistral-nemo:12b", - "mixtral", - "mixtral:8x7b", - "mixtral:8x22b", - "moondream", - "moondream:1.8b", - "openhermes", - "openhermes:v2.5", - "phi3", - "phi3:3.8b", - "phi3:14b", - "phi3.5", - "phi3.5:3.8b", - "qwen", - "qwen:7b", - "qwen:14b", - "qwen:32b", - "qwen:72b", - "qwen:110b", - "qwen2", - "qwen2:0.5b", - "qwen2:1.5b", - "qwen2:7b", - "qwen2:72b", - "smollm", - "smollm:135m", - "smollm:360m", - "smollm:1.7b" - ] + "description": "Ollama base URL. Local API server can be started with `OLLAMA_ORIGINS=* ollama serve`" }, - "providerSettings": { + "headers": { "type": "object", - "properties": { - "baseURL": { - "type": "string", - "description": "Ollama base URL. Local API server can be started with `OLLAMA_ORIGINS=* ollama serve`" - }, - "headers": { - "type": "object", - "description": "Custom headers to include in the requests." - } - } + "description": "Custom headers to include in the requests." } } }, + "void.greptile.apiKey": { + "type": "string", + "description": "Greptile API key." + }, "void.greptile": { "type": "object", "properties": { "providerSettings": { "type": "object", "properties": { - "apikey": { - "type": "string", - "description": "Greptile API key." - }, "headers": { "type": "object", "description": "Custom headers to include in the requests.", diff --git a/extensions/void/src/SidebarWebviewProvider.ts b/extensions/void/src/SidebarWebviewProvider.ts index c95e1e12..c1533cca 100644 --- a/extensions/void/src/SidebarWebviewProvider.ts +++ b/extensions/void/src/SidebarWebviewProvider.ts @@ -57,11 +57,11 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider { const ollamaBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('ollama.providerSettings.baseURL') || 'http://127.0.0.1:11434').toString() - const azureProviderSettings = vscode.workspace.getConfiguration('void').get('azure.providerSettings') as AzureOpenAIProviderSettings - const azureBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('azure.providerSettings.baseURL') || `https://${azureProviderSettings.resourceName}.openai.azure.com/openai/deployments`).toString() + const azureResourceName = vscode.workspace.getConfiguration('void').get('azure.resourceName') ?? '' + const azureBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('azure.providerSettings.baseURL') || `https://${azureResourceName}.openai.azure.com/openai/deployments`).toString() const openaiBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('openai.providerSettings.baseURL') || 'https://api.openai.com').toString() - const greptileBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('greptile.providerSettings.baseURL') || 'https://api.mistral.ai').toString() - const anthropicBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('anthropic.providerSettings.baseURL') || 'https://api.mistral.ai').toString() + const greptileBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('greptile.providerSettings.baseURL') || 'https://api.greptile.com').toString() + const anthropicBaseURL = new URL('/', vscode.workspace.getConfiguration('void').get('anthropic.providerSettings.baseURL') || 'https://api.anthropic.com').toString() const allowed_urls = [ ollamaBaseURL, azureBaseURL, diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index 256f2ca2..479a61d3 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -10,21 +10,24 @@ export type ApiConfig = { anthropic: { /** @default 'claude-3-5-sonnet-20240620' */ model: string, - providerSettings?: AnthropicProviderSettings + apiKey: string, + providerSettings?: Omit }, openai: { /** @default 'gpt-4o' */ model: string, - providerSettings?: OpenAIProviderSettings + apiKey: string, + providerSettings?: Omit }, azure: { deploymentId: string, - providerSettings?: AzureOpenAIProviderSettings + resourceName: string, + apiKey: string, + providerSettings?: Omit }, greptile: { + apiKey: string, providerSettings?: { - apikey?: string, - githubPAT?: string, headers?: Record, repoinfo?: { remote?: string, // e.g. 'github' @@ -83,7 +86,7 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin fetch('https://api.greptile.com/v2/query', { method: 'POST', headers: { - "Authorization": `Bearer ${apiConfig.greptile.providerSettings?.apikey}`, + "Authorization": `Bearer ${apiConfig.greptile.apiKey}`, "Content-Type": `application/json`, ...apiConfig.greptile.providerSettings?.headers }, @@ -164,10 +167,20 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, export const getAiModel = (apiConfig: ApiConfig) => { switch (apiConfig.provider) { - case 'openai': return createOpenAI(apiConfig.openai.providerSettings)(apiConfig.openai.model || 'gpt-4o') - case 'anthropic': return createAnthropic(apiConfig.anthropic.providerSettings)(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') + case 'openai': return createOpenAI({ + ...apiConfig.openai.providerSettings, + apiKey: apiConfig.openai.apiKey, + })(apiConfig.openai.model || 'gpt-4o') + case 'anthropic': return createAnthropic({ + ...apiConfig.anthropic.providerSettings, + apiKey: apiConfig.anthropic.apiKey, + })(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') case 'ollama': return createOllama(apiConfig.ollama.providerSettings)(apiConfig.ollama.model || 'llama3.1') - case 'azure': return createAzure(apiConfig.azure.providerSettings)(`${apiConfig.azure.deploymentId}`) + case 'azure': return createAzure({ + ...apiConfig.azure.providerSettings, + apiKey: apiConfig.azure.apiKey, + resourceName: apiConfig.azure.resourceName, + })(`${apiConfig.azure.deploymentId}`) default: throw new Error(`Error: provider was ${apiConfig.provider}, which is not recognized!`) } diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 0b218bcd..5d2ae6c0 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -15,18 +15,22 @@ const getApiConfig = () => { provider: vscode.workspace.getConfiguration('void').get('provider') ?? 'anthropic', anthropic: { model: vscode.workspace.getConfiguration('void').get('anthropic.model') ?? 'claude-3-5-sonnet-20240620', + apiKey: vscode.workspace.getConfiguration('void').get('anthropic.apiKey') ?? '', providerSettings: { ...vscode.workspace.getConfiguration('void').get('anthropic.providerSettings') ?? {}, }, }, openai: { model: vscode.workspace.getConfiguration('void').get('openai.model') ?? 'gpt-4o', + apiKey: vscode.workspace.getConfiguration('void').get('openai.apiKey') ?? '', providerSettings: { ...vscode.workspace.getConfiguration('void').get('openai.providerSettings') ?? {}, }, }, azure: { deploymentId: vscode.workspace.getConfiguration('void').get('azure.deploymentId') ?? '', + resourceName: vscode.workspace.getConfiguration('void').get('azure.resourceName') ?? '', + apiKey: vscode.workspace.getConfiguration('void').get('azure.apiKey') ?? '', providerSettings: { ...vscode.workspace.getConfiguration('void').get('azure.providerSettings') ?? {}, }, @@ -38,11 +42,13 @@ const getApiConfig = () => { }, }, greptile: { + apiKey: vscode.workspace.getConfiguration('void').get('greptile.apiKey') ?? '', providerSettings: { ...vscode.workspace.getConfiguration('void').get('greptile.providerSettings') ?? {}, } }, } + return apiConfig } From 4f9b5ac873977521b3347784536eabf735d8edeb Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 15:47:48 +0800 Subject: [PATCH 07/11] chore: rm --- extensions/void/src/SidebarWebviewProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/void/src/SidebarWebviewProvider.ts b/extensions/void/src/SidebarWebviewProvider.ts index c1533cca..13a28b9a 100644 --- a/extensions/void/src/SidebarWebviewProvider.ts +++ b/extensions/void/src/SidebarWebviewProvider.ts @@ -1,6 +1,5 @@ // renders the code from `src/sidebar` -import { AzureOpenAIProviderSettings } from '@ai-sdk/azure'; import * as vscode from 'vscode'; function getNonce() { From 44bc8bfdbf12865dd340a9719ccef79dc57e842a Mon Sep 17 00:00:00 2001 From: Jelf Date: Sat, 21 Sep 2024 16:09:28 +0800 Subject: [PATCH 08/11] chore: update --- extensions/void/package.json | 74 +++++++++++++++++------------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/extensions/void/package.json b/extensions/void/package.json index 972e1d3d..4bc55642 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -49,13 +49,16 @@ "type": "string", "description": "Use a different URL prefix for API calls, e.g. to use proxy servers." }, - "apiKey": {}, "headers": { "type": "object", "description": "Custom headers to include in the requests." } } }, + "void.openai.apiKey": { + "type": "string", + "description": "OpenAI API key." + }, "void.openai.model": { "type": "string", "default": "gpt-4o", @@ -80,10 +83,6 @@ "gpt-3.5-turbo-1106" ] }, - "void.openai.apiKey": { - "type": "string", - "description": "OpenAI API key." - }, "void.openai.providerSettings": { "type": "object", "properties": { @@ -113,6 +112,10 @@ } } }, + "void.azure.apiKey": { + "type": "string", + "description": "Azure API key." + }, "void.azure.deploymentId": { "type": "string", "description": "Azure API deployment ID." @@ -121,15 +124,12 @@ "type": "string", "description": "Name of the Azure OpenAI resource. Either this or `baseURL` can be used. \nThe resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/deployments/{modelId}{path}`" }, - "void.azure.apiKey": { - "type": "string", - "description": "Azure API key." - }, "void.azure.providerSettings": { "type": "object", "properties": { "baseURL": { "type": "string", + "default": "https://${resourceName}.openai.azure.com/openai/deployments", "description": "Azure API base URL." }, "headers": { @@ -236,6 +236,7 @@ "properties": { "baseURL": { "type": "string", + "default": "http://127.0.0.1:11434", "description": "Ollama base URL. Local API server can be started with `OLLAMA_ORIGINS=* ollama serve`" }, "headers": { @@ -248,40 +249,35 @@ "type": "string", "description": "Greptile API key." }, - "void.greptile": { + "void.greptile.providerSettings": { "type": "object", "properties": { - "providerSettings": { + "headers": { "type": "object", + "description": "Custom headers to include in the requests.", "properties": { - "headers": { - "type": "object", - "description": "Custom headers to include in the requests.", - "properties": { - "X-Github-Token": { - "type": "string", - "description": "GitHub PAT." - } - } - }, - "repoinfo": { - "type": "array", - "items": { - "type": "object", - "properties": { - "remote": { - "type": "string", - "description": "GitHub remote." - }, - "repository": { - "type": "string", - "description": "GitHub repository." - }, - "branch": { - "type": "string", - "description": "GitHub branch." - } - } + "X-Github-Token": { + "type": "string", + "description": "GitHub PAT." + } + } + }, + "repoinfo": { + "type": "array", + "items": { + "type": "object", + "properties": { + "remote": { + "type": "string", + "description": "GitHub remote." + }, + "repository": { + "type": "string", + "description": "GitHub repository." + }, + "branch": { + "type": "string", + "description": "GitHub branch." } } } From 6d513a1e602c8dea3f89f85cc5a04d840a0abfa9 Mon Sep 17 00:00:00 2001 From: Jelf Date: Sun, 22 Sep 2024 16:00:08 +0800 Subject: [PATCH 09/11] chore: improve setting --- extensions/void/package.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/void/package.json b/extensions/void/package.json index 4bc55642..5db043eb 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -258,7 +258,7 @@ "properties": { "X-Github-Token": { "type": "string", - "description": "GitHub PAT." + "description": "GitHub Token." } } }, @@ -269,15 +269,19 @@ "properties": { "remote": { "type": "string", - "description": "GitHub remote." + "description": "remote provider", + "enum": [ + "github", + "gitlab" + ] }, "repository": { "type": "string", - "description": "GitHub repository." + "description": "repository identifier in \"owner/repository\" format." }, "branch": { "type": "string", - "description": "GitHub branch." + "description": "git branch." } } } From 9b130edcfeb20819e0dcd3d6f1bc912cc5ce001a Mon Sep 17 00:00:00 2001 From: Jelf Date: Sun, 22 Sep 2024 17:35:51 +0800 Subject: [PATCH 10/11] feat: read `.voidrules` for system prompt --- extensions/void/src/common/getRules.ts | 12 ++++ extensions/void/src/common/sendLLMMessage.ts | 66 ++++++++++++-------- extensions/void/src/extension.ts | 17 ++++- extensions/void/src/shared_types.ts | 3 + extensions/void/src/sidebar/Sidebar.tsx | 2 +- extensions/void/src/sidebar/getVscodeApi.ts | 3 +- 6 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 extensions/void/src/common/getRules.ts diff --git a/extensions/void/src/common/getRules.ts b/extensions/void/src/common/getRules.ts new file mode 100644 index 00000000..0a7ef6c4 --- /dev/null +++ b/extensions/void/src/common/getRules.ts @@ -0,0 +1,12 @@ +import { awaitVSCodeResponse, getVSCodeAPI } from '../sidebar/getVscodeApi'; + +export async function getRules() { + try { + getVSCodeAPI().postMessage({ type: 'getRules', rules: '' }) + const rules = await awaitVSCodeResponse('getRules') + return rules.rules + } catch (error) { + console.error('Error reading .voidrules file:', error); + throw error; + } +} diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index 479a61d3..dcab0eaf 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -3,6 +3,7 @@ import { createOpenAI, OpenAIProviderSettings } from '@ai-sdk/openai'; import { AnthropicProviderSettings, createAnthropic } from '@ai-sdk/anthropic'; import { AzureOpenAIProviderSettings, createAzure } from '@ai-sdk/azure'; import { createOllama, OllamaProviderSettings } from 'ollama-ai-provider'; +import { getRules } from './getRules'; export type ApiConfig = { /** @default 'anthropic' */ @@ -142,24 +143,29 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, onFinalMessage, apiConfig }) => { if (!apiConfig) return { abort: () => { } } const provider = apiConfig.provider - // TODO: create an @ai-sdk provider for greptile + // TODO: create an @ai-sdk provider for greptile, if (provider === 'greptile') return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }) const model = getAiModel(apiConfig) + const abortController = new AbortController() const abortSignal = abortController.signal - streamText({ - model, - messages, - abortSignal, - }).then(async (result) => { - let fullText = '' - for await (const textPart of result.textStream) { - fullText += textPart - onText(textPart, fullText) - } - onFinalMessage(fullText) + + getRules().then(rules => { + streamText({ + model, + system: rules || '', + messages, + abortSignal, + }).then(async (result) => { + let fullText = '' + for await (const textPart of result.textStream) { + fullText += textPart + onText(textPart, fullText) + } + onFinalMessage(fullText) + }) }) return { abort: abortController.abort } @@ -167,20 +173,28 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, export const getAiModel = (apiConfig: ApiConfig) => { switch (apiConfig.provider) { - case 'openai': return createOpenAI({ - ...apiConfig.openai.providerSettings, - apiKey: apiConfig.openai.apiKey, - })(apiConfig.openai.model || 'gpt-4o') - case 'anthropic': return createAnthropic({ - ...apiConfig.anthropic.providerSettings, - apiKey: apiConfig.anthropic.apiKey, - })(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') - case 'ollama': return createOllama(apiConfig.ollama.providerSettings)(apiConfig.ollama.model || 'llama3.1') - case 'azure': return createAzure({ - ...apiConfig.azure.providerSettings, - apiKey: apiConfig.azure.apiKey, - resourceName: apiConfig.azure.resourceName, - })(`${apiConfig.azure.deploymentId}`) + case 'openai': + return createOpenAI({ + ...apiConfig.openai.providerSettings, + apiKey: apiConfig.openai.apiKey, + })(apiConfig.openai.model || 'gpt-4o') + + case 'anthropic': + return createAnthropic({ + ...apiConfig.anthropic.providerSettings, + apiKey: apiConfig.anthropic.apiKey, + })(apiConfig.anthropic.model || 'claude-3-5-sonnet-20240620') + + case 'ollama': + return createOllama(apiConfig.ollama.providerSettings)(apiConfig.ollama.model || 'llama3.1') + + case 'azure': + return createAzure({ + ...apiConfig.azure.providerSettings, + apiKey: apiConfig.azure.apiKey, + resourceName: apiConfig.azure.resourceName, + })(`${apiConfig.azure.deploymentId}`) + default: throw new Error(`Error: provider was ${apiConfig.provider}, which is not recognized!`) } diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 5d2ae6c0..fa2805e8 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -115,7 +115,22 @@ export function activate(context: vscode.ExtensionContext) { // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`) webview.onDidReceiveMessage(async (m: WebviewMessage) => { - if (m.type === 'requestFiles') { + if (m.type === 'getRules') { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (workspaceFolder) { + const rulesFilePath = vscode.Uri.joinPath(workspaceFolder.uri, '.voidrules'); + try { + const rulesContent = await readFileContentOfUri(rulesFilePath); + webview.postMessage({ type: 'getRules', rules: rulesContent } satisfies WebviewMessage); + } catch (error) { + console.error('Error reading .voidrules file:', error); + webview.postMessage({ type: 'getRules', rules: null } satisfies WebviewMessage); + } + } else { + webview.postMessage({ type: 'getRules', rules: null } satisfies WebviewMessage); + } + } + else if (m.type === 'requestFiles') { // get contents of all file paths const files = await Promise.all( diff --git a/extensions/void/src/shared_types.ts b/extensions/void/src/shared_types.ts index 4d7c0dc9..d443bbe6 100644 --- a/extensions/void/src/shared_types.ts +++ b/extensions/void/src/shared_types.ts @@ -27,6 +27,9 @@ type WebviewMessage = ( // editor -> sidebar | { type: 'apiConfig', apiConfig: ApiConfig } + // sidebar -> editor + | { type: 'getRules', rules: string | null } + ) type Command = WebviewMessage['type'] diff --git a/extensions/void/src/sidebar/Sidebar.tsx b/extensions/void/src/sidebar/Sidebar.tsx index 0107e228..86df6407 100644 --- a/extensions/void/src/sidebar/Sidebar.tsx +++ b/extensions/void/src/sidebar/Sidebar.tsx @@ -266,7 +266,7 @@ const Sidebar = () => { {!selection?.selectionStr ? null : (
-