Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b9e2c2319 | |||
| 96000273cf | |||
| e771edbe2e | |||
| aafe556333 | |||
| 78c6baed82 | |||
| b35dfdf475 | |||
| 609f4c9cbf | |||
| 6df3b588df | |||
| f4c1321f25 | |||
| b54dd7fedc | |||
| 3241178238 | |||
| 68bb3f6962 | |||
| 2e64f5a24b | |||
| 2e5ecb291d | |||
| f1e24c9b4c | |||
| f65e4f51a2 | |||
| f1a5d20150 | |||
| 28dcdff189 | |||
| 67ed52415e | |||
| 3772cf98ea | |||
| 1c6edf7023 | |||
| 6b6528c3fb | |||
| f98894ad77 | |||
| f8745d8b40 | |||
| 7066b4285b | |||
| 1eee07e697 | |||
| 45f7e4b157 | |||
| 1619a3c19b | |||
| d668348244 | |||
| 3fa680e8ba | |||
| e8b7c9c470 | |||
| a0eb7d6192 | |||
| 5c4785d691 | |||
| 1de90cc39d | |||
| d09a41acd3 | |||
| 24102fc2b2 | |||
| c2b65eb16f | |||
| a4046bc5a5 | |||
| 6f73804e94 | |||
| 76155beaad | |||
| 53c68b1267 | |||
| afbc1b40bf | |||
| 9a905f6cb2 | |||
| dd42e40a8d | |||
| 970fd7ecbb | |||
| 50710143eb | |||
| d6295e5d65 | |||
| 053a022ef8 | |||
| 3b963d8717 | |||
| e8540e4668 | |||
| 39f2f24b42 | |||
| e852874be7 | |||
| 6d07f9db14 | |||
| 2934f9a3c5 | |||
| 7748dc4f2d | |||
| 8b45aea8a8 | |||
| cab8dd64b6 | |||
| 19782c80df | |||
| 926db45f0a | |||
| 4068911d49 | |||
| dcee2e2657 | |||
| 4c1c0b591a | |||
| 61fc593903 | |||
| ecff59eed7 | |||
| e2a7b0e1a0 | |||
| 07c3f33f1e | |||
| 0f93e1b055 | |||
| 2106573922 | |||
| 93a13a76af | |||
| 7b18e25288 | |||
| 57b0505a7e | |||
| 58d95035ff | |||
| 5232b4c30c | |||
| 08274637e1 | |||
| 168b92f6c4 | |||
| a9aa9a26ea |
@@ -86,7 +86,7 @@ To build Void from the terminal instead of from inside VSCode, follow the steps
|
||||
|
||||
- Make sure you followed the prerequisite steps above.
|
||||
- Make sure you have Node version `20.18.2` (the version in `.nvmrc`)!
|
||||
- You can do this easily without touching your base installation with [nvm](https://github.com/nvm-sh/nvm). Simply run `nvm install`, followed by `nvm use` and it will automatically install and use the version specified in `nvmrc`
|
||||
- You can do this easily without touching your base installation with [nvm](https://github.com/nvm-sh/nvm). Simply run `nvm install`, followed by `nvm use` and it will automatically install and use the version specified in `nvmrc`.
|
||||
- Make sure that the path to your Void folder does not have any spaces in it.
|
||||
- If you get `"TypeError: Failed to fetch dynamically imported module"`, make sure all imports end with `.js`.
|
||||
- If you get an error with React, try running `NODE_OPTIONS="--max-old-space-size=8192" npm run buildreact`.
|
||||
|
||||
Generated
+360
-27
@@ -11,9 +11,10 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.40.0",
|
||||
"@azure/identity": "^4.9.1",
|
||||
"@c4312/eventsource-umd": "^3.0.5",
|
||||
"@floating-ui/react": "^0.27.8",
|
||||
"@google/generative-ai": "^0.24.1",
|
||||
"@google/genai": "^0.13.0",
|
||||
"@microsoft/1ds-core-js": "^3.2.13",
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@mistralai/mistralai": "^1.6.0",
|
||||
@@ -284,7 +285,6 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@@ -299,13 +299,31 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@azure/core-auth": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz",
|
||||
"integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==",
|
||||
"dev": true,
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz",
|
||||
"integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-util": "^1.1.0",
|
||||
"@azure/core-util": "^1.11.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-client": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.4.tgz",
|
||||
"integrity": "sha512-f7IxTD15Qdux30s2qFARH+JxgwxWLG2Rlr4oSkPGuLWm+1p5y1+C04XGLA0vmX6EtqfutmjvpNmAfgwVIS5hpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-rest-pipeline": "^1.20.0",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"@azure/core-util": "^1.6.1",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -448,18 +466,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.0.tgz",
|
||||
"integrity": "sha512-CeuTvsXxCUmEuxH5g/aceuSl6w2EugvNHKAtKKVdiX915EjJJxAwfzNNWZreNnbxHZ2fi0zaM6wwS23x2JVqSQ==",
|
||||
"dev": true,
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.20.0.tgz",
|
||||
"integrity": "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-auth": "^1.8.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.9.0",
|
||||
"@azure/core-util": "^1.11.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"@typespec/ts-http-runtime": "^0.2.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -470,7 +487,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz",
|
||||
"integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@@ -479,23 +495,90 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-util": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz",
|
||||
"integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==",
|
||||
"dev": true,
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.12.0.tgz",
|
||||
"integrity": "sha512-13IyjTQgABPARvG90+N2dXpC+hwp466XCdQXPCRlbWHgd3SJd5Q1VvaBGv6k1BIa4MQm6hAF1UBU1m8QUxV8sQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@typespec/ts-http-runtime": "^0.2.2",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/identity": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.9.1.tgz",
|
||||
"integrity": "sha512-986D7Cf1AOwYqSDtO/FnMAyk/Jc8qpftkGsxuehoh4F85MhQ4fICBGX/44+X1y78lN4Sqib3Bsoaoh/FvOGgmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.9.0",
|
||||
"@azure/core-client": "^1.9.2",
|
||||
"@azure/core-rest-pipeline": "^1.17.0",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"@azure/core-util": "^1.11.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@azure/msal-browser": "^4.2.0",
|
||||
"@azure/msal-node": "^3.5.0",
|
||||
"open": "^10.1.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/identity/node_modules/define-lazy-prop": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
|
||||
"integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/identity/node_modules/is-wsl": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
|
||||
"integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-inside-container": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/identity/node_modules/open": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz",
|
||||
"integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"default-browser": "^5.2.1",
|
||||
"define-lazy-prop": "^3.0.0",
|
||||
"is-inside-container": "^1.0.0",
|
||||
"is-wsl": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/logger": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz",
|
||||
"integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
@@ -503,6 +586,50 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-browser": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.12.0.tgz",
|
||||
"integrity": "sha512-WD1lmVWchg7wn1mI7Tr4v7QPyTwK+8Nuyje3jRpOFENLRLEBsdK8VVdTw3C+TypZmYn4cOAdj3zREnuFXgvfIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/msal-common": "15.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-common": {
|
||||
"version": "15.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.6.0.tgz",
|
||||
"integrity": "sha512-EotmBz42apYGjqiIV9rDUdptaMptpTn4TdGf3JfjLvFvinSe9BJ6ywU92K9ky+t/b0ghbeTSe9RfqlgLh8f2jA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-node": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.5.3.tgz",
|
||||
"integrity": "sha512-c5mifzHX5mwm5JqMIlURUyp6LEEdKF1a8lmcNRLBo0lD7zpSYPHupa4jHyhJyg9ccLwszLguZJdk2h3ngnXwNw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/msal-common": "15.6.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"uuid": "^8.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-node/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/storage-blob": {
|
||||
"version": "12.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.8.0.tgz",
|
||||
@@ -1817,11 +1944,17 @@
|
||||
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@google/generative-ai": {
|
||||
"version": "0.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz",
|
||||
"integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==",
|
||||
"node_modules/@google/genai": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-0.13.0.tgz",
|
||||
"integrity": "sha512-eaEncWt875H7046T04mOpxpHJUM+jLIljEf+5QctRyOeChylE/nhpwm1bZWTRWoOu/t46R9r+PmgsJFhTpE7tQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"google-auth-library": "^9.14.2",
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.22.4",
|
||||
"zod-to-json-schema": "^3.22.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
@@ -4251,6 +4384,20 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typespec/ts-http-runtime": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.2.2.tgz",
|
||||
"integrity": "sha512-Gz/Sm64+Sq/vklJu1tt9t+4R2lvnud8NbTD/ZfpZtMiUX7YeVpCA8j6NSW8ptwcoLL+NmYANwqP8DV0q/bwl2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vscode/deviceid": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.1.tgz",
|
||||
@@ -6275,6 +6422,21 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/bundle-name": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
|
||||
"integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"run-applescript": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bundle-require": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz",
|
||||
@@ -7773,6 +7935,34 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/default-browser": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
|
||||
"integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bundle-name": "^4.1.0",
|
||||
"default-browser-id": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/default-browser-id": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz",
|
||||
"integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/default-compare": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
|
||||
@@ -13472,6 +13662,39 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-inside-container": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
|
||||
"integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-docker": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"is-inside-container": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-inside-container/node_modules/is-docker": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
|
||||
"integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"is-docker": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-interactive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
|
||||
@@ -14169,6 +14392,49 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/jwa": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
|
||||
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "^1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.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",
|
||||
@@ -14726,11 +14992,40 @@
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
@@ -14746,6 +15041,12 @@
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.some": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
|
||||
@@ -19449,6 +19750,18 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
|
||||
"integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz",
|
||||
@@ -22249,7 +22562,6 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsscmp": {
|
||||
@@ -23767,6 +24079,27 @@
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||
|
||||
+2
-1
@@ -73,9 +73,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.40.0",
|
||||
"@azure/identity": "^4.9.1",
|
||||
"@c4312/eventsource-umd": "^3.0.5",
|
||||
"@floating-ui/react": "^0.27.8",
|
||||
"@google/generative-ai": "^0.24.1",
|
||||
"@google/genai": "^0.13.0",
|
||||
"@microsoft/1ds-core-js": "^3.2.13",
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@mistralai/mistralai": "^1.6.0",
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"nameShort": "Void",
|
||||
"nameLong": "Void",
|
||||
"voidVersion": "1.3.2",
|
||||
"voidVersion": "1.3.7",
|
||||
"voidRelease": "0031",
|
||||
"applicationName": "void",
|
||||
"dataFolderName": ".void-editor",
|
||||
"win32MutexName": "voideditor",
|
||||
|
||||
@@ -12,7 +12,7 @@ import { URI } from '../../../../base/common/uri.js';
|
||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||
import { ILLMMessageService } from '../common/sendLLMMessageService.js';
|
||||
import { chat_userMessageContent, ToolName, } from '../common/prompt/prompts.js';
|
||||
import { getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js';
|
||||
import { AnthropicReasoning, getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js';
|
||||
import { generateUuid } from '../../../../base/common/uuid.js';
|
||||
import { FeatureName, ModelSelection, ModelSelectionOptions } from '../common/voidSettingsTypes.js';
|
||||
import { IVoidSettingsService } from '../common/voidSettingsService.js';
|
||||
@@ -540,7 +540,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
|
||||
const { name, id, rawParams } = lastMsg
|
||||
|
||||
const errorMessage = this.errMsgs.rejected
|
||||
const errorMessage = this.toolErrMsgs.rejected
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null, id, rawParams })
|
||||
this._setStreamState(threadId, undefined)
|
||||
}
|
||||
@@ -557,7 +557,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
}
|
||||
// add tool that's running
|
||||
else if (this.streamState[threadId]?.isRunning === 'tool') {
|
||||
const { toolName, toolParams, id, content, rawParams } = this.streamState[threadId].toolInfo
|
||||
const { toolName, toolParams, id, content: content_, rawParams } = this.streamState[threadId].toolInfo
|
||||
const content = content_ || this.toolErrMsgs.interrupted
|
||||
this._updateLatestTool(threadId, { role: 'tool', name: toolName, params: toolParams, id, content, rawParams, type: 'rejected', result: null })
|
||||
}
|
||||
// reject the tool for the user if relevant
|
||||
@@ -581,8 +582,9 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
|
||||
|
||||
|
||||
private readonly errMsgs = {
|
||||
private readonly toolErrMsgs = {
|
||||
rejected: 'Tool call was rejected by the user.',
|
||||
interrupted: 'Tool call was interrupted by the user.',
|
||||
errWhenStringifying: (error: any) => `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}`
|
||||
}
|
||||
|
||||
@@ -671,7 +673,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
try {
|
||||
toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any)
|
||||
} catch (error) {
|
||||
const errorMessage = this.errMsgs.errWhenStringifying(error)
|
||||
const errorMessage = this.toolErrMsgs.errWhenStringifying(error)
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams })
|
||||
return {}
|
||||
}
|
||||
@@ -749,8 +751,13 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
shouldRetryLLM = false
|
||||
nAttempts += 1
|
||||
|
||||
let resMessageIsDonePromise: (res: { type: 'llmDone', toolCall?: RawToolCallObj } | { type: 'llmError', error?: { message: string; fullError: Error | null; } } | { type: 'llmAborted' }) => void // resolves when user approves this tool use (or if tool doesn't require approval)
|
||||
const messageIsDonePromise = new Promise<{ type: 'llmDone', toolCall?: RawToolCallObj } | { type: 'llmError', error?: { message: string; fullError: Error | null; } } | { type: 'llmAborted' }>((res, rej) => { resMessageIsDonePromise = res })
|
||||
type ResTypes =
|
||||
| { type: 'llmDone', toolCall?: RawToolCallObj, info: { fullText: string, fullReasoning: string, anthropicReasoning: AnthropicReasoning[] | null } }
|
||||
| { type: 'llmError', error?: { message: string; fullError: Error | null; } }
|
||||
| { type: 'llmAborted' }
|
||||
|
||||
let resMessageIsDonePromise: (res: ResTypes) => void // resolves when user approves this tool use (or if tool doesn't require approval)
|
||||
const messageIsDonePromise = new Promise<ResTypes>((res, rej) => { resMessageIsDonePromise = res })
|
||||
|
||||
const llmCancelToken = this._llmMessageService.sendLLMMessage({
|
||||
messagesType: 'chatMessages',
|
||||
@@ -765,9 +772,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
this._setStreamState(threadId, { isRunning: 'LLM', llmInfo: { displayContentSoFar: fullText, reasoningSoFar: fullReasoning, toolCallSoFar: toolCall ?? null }, interrupt: Promise.resolve(() => { if (llmCancelToken) this._llmMessageService.abort(llmCancelToken) }) })
|
||||
},
|
||||
onFinalMessage: async ({ fullText, fullReasoning, toolCall, anthropicReasoning, }) => {
|
||||
this._addMessageToThread(threadId, { role: 'assistant', displayContent: fullText, reasoning: fullReasoning, anthropicReasoning })
|
||||
resMessageIsDonePromise({ type: 'llmDone', toolCall }) // resolve with tool calls
|
||||
|
||||
resMessageIsDonePromise({ type: 'llmDone', toolCall, info: { fullText, fullReasoning, anthropicReasoning } }) // resolve with tool calls
|
||||
},
|
||||
onError: async (error) => {
|
||||
resMessageIsDonePromise({ type: 'llmError', error: error })
|
||||
@@ -826,11 +831,12 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// llm res success
|
||||
const { toolCall } = llmRes
|
||||
this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative, for clarity
|
||||
const { toolCall, info } = llmRes
|
||||
|
||||
this._addMessageToThread(threadId, { role: 'assistant', displayContent: info.fullText, reasoning: info.fullReasoning, anthropicReasoning: info.anthropicReasoning })
|
||||
|
||||
this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative for clarity
|
||||
|
||||
// call tool if there is one
|
||||
if (toolCall) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { EndOfLinePreference } from '../../../../editor/common/model.js';
|
||||
|
||||
export const EMPTY_MESSAGE = '(empty message)'
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +37,6 @@ type SimpleLLMMessage = {
|
||||
}
|
||||
|
||||
|
||||
const EMPTY_MESSAGE = '(empty message)'
|
||||
|
||||
const CHARS_PER_TOKEN = 4 // assume abysmal chars per token
|
||||
const TRIM_TO_LEN = 120
|
||||
@@ -237,18 +237,6 @@ const prepareMessages_XML_tools = (messages: SimpleLLMMessage[], supportsAnthrop
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export type GeminiMessage = {
|
||||
role: 'user' | 'model'; // Gemini uses 'user' and 'model' roles
|
||||
parts: (
|
||||
| { text: string; }
|
||||
| { functionCall: { tool_call: any } }
|
||||
| { functionResponse: { name: ToolName, response: { result: string } } }
|
||||
)[];
|
||||
};
|
||||
|
||||
|
||||
// --- CHAT ---
|
||||
|
||||
const prepareOpenAIOrAnthropicMessages = ({
|
||||
@@ -417,14 +405,25 @@ const prepareOpenAIOrAnthropicMessages = ({
|
||||
|
||||
|
||||
// ================ no empty message ================
|
||||
for (const currMsg of llmMessages) {
|
||||
for (let i = 0; i < llmMessages.length; i += 1) {
|
||||
const currMsg: AnthropicOrOpenAILLMMessage = llmMessages[i]
|
||||
const nextMsg: AnthropicOrOpenAILLMMessage | undefined = llmMessages[i + 1]
|
||||
|
||||
if (currMsg.role === 'tool') continue
|
||||
|
||||
// if content is a string, replace string with empty msg
|
||||
if (typeof currMsg.content === 'string')
|
||||
if (typeof currMsg.content === 'string') {
|
||||
currMsg.content = currMsg.content || EMPTY_MESSAGE
|
||||
}
|
||||
else {
|
||||
// if content is an array, replace any empty text entries with empty msg, and make sure there's at least 1 entry
|
||||
// allowed to be empty if has a tool in it or following it
|
||||
if (currMsg.content.find(c => c.type === 'tool_result' || c.type === 'tool_use')) {
|
||||
currMsg.content = currMsg.content.filter(c => !(c.type === 'text' && !c.text)) as any
|
||||
continue
|
||||
}
|
||||
if (nextMsg?.role === 'tool') continue
|
||||
|
||||
// replace any empty text entries with empty msg, and make sure there's at least 1 entry
|
||||
for (const c of currMsg.content) {
|
||||
if (c.type === 'text') c.text = c.text || EMPTY_MESSAGE
|
||||
}
|
||||
@@ -457,7 +456,7 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => {
|
||||
}
|
||||
else if (c.type === 'tool_use') {
|
||||
latestToolName = c.name as ToolName
|
||||
return { functionCall: { name: c.name as ToolName, args: c.input } }
|
||||
return { functionCall: { id: c.id, name: c.name as ToolName, args: c.input } }
|
||||
}
|
||||
else return null
|
||||
}).filter(m => !!m)
|
||||
@@ -475,7 +474,7 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => {
|
||||
}
|
||||
else if (c.type === 'tool_result') {
|
||||
if (!latestToolName) return null
|
||||
return { functionResponse: { name: latestToolName, response: { result: c.content } } }
|
||||
return { functionResponse: { id: c.tool_use_id, name: latestToolName, response: { output: c.content } } }
|
||||
}
|
||||
else return null
|
||||
}).filter(m => !!m)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from '../../../../base/common/buffer.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { env } from '../../../../base/common/process.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
@@ -29,6 +30,7 @@ export const IExtensionTransferService = createDecorator<IExtensionTransferServi
|
||||
const extensionBlacklist = [
|
||||
// ignore extensions
|
||||
'ms-vscode-remote.remote', // ms-vscode-remote.remote-ssh, ms-vscode-remote.remote-wsl
|
||||
'ms-vscode.remote', // ms-vscode.remote-explorer
|
||||
// ignore other AI copilots that could conflict with Void keybindings
|
||||
'sourcegraph.cody-ai',
|
||||
'continue.continue',
|
||||
@@ -40,6 +42,9 @@ const extensionBlacklist = [
|
||||
];
|
||||
|
||||
|
||||
const isBlacklisted = (fsPath: string | undefined) => {
|
||||
return extensionBlacklist.find(bItem => fsPath?.includes(bItem))
|
||||
}
|
||||
|
||||
class ExtensionTransferService extends Disposable implements IExtensionTransferService {
|
||||
_serviceBrand: undefined;
|
||||
@@ -87,15 +92,31 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS
|
||||
await fileService.createFolder(toParent)
|
||||
}
|
||||
for (const extensionFolder of stat.children ?? []) {
|
||||
if (extensionBlacklist.find(bItem => extensionFolder.resource.path.includes(bItem))) {
|
||||
console.log('Skipping...', extensionFolder.resource.path)
|
||||
continue
|
||||
}
|
||||
const from = extensionFolder.resource
|
||||
const to = URI.joinPath(toParent, extensionFolder.name)
|
||||
await fileService.copy(from, to, true)
|
||||
const toStat = await fileService.resolve(from)
|
||||
|
||||
if (toStat.isDirectory) {
|
||||
if (!isBlacklisted(extensionFolder.resource.fsPath)) {
|
||||
await fileService.copy(from, to, true)
|
||||
}
|
||||
}
|
||||
else if (toStat.isFile) {
|
||||
if (extensionFolder.name === 'extensions.json') {
|
||||
try {
|
||||
const contentsStr = await fileService.readFile(from)
|
||||
const json: any = JSON.parse(contentsStr.value.toString())
|
||||
const j2 = json.filter((entry: { identifier?: { id?: string } }) => !isBlacklisted(entry?.identifier?.id))
|
||||
const jsonStr = JSON.stringify(j2)
|
||||
await fileService.writeFile(to, VSBuffer.fromString(jsonStr))
|
||||
}
|
||||
catch {
|
||||
console.log('Error copying extensions.json, skipping')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure the destination directory exists
|
||||
|
||||
} else {
|
||||
console.log(`Skipping file that doesn't exist: ${from.toString()}`)
|
||||
}
|
||||
@@ -113,19 +134,38 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS
|
||||
}
|
||||
|
||||
async deleteBlacklistExtensions(os: 'mac' | 'windows' | 'linux' | null) {
|
||||
const fileService = this._fileService
|
||||
const extensionsURI = getExtensionsFolder(os)
|
||||
if (!extensionsURI) return
|
||||
const eURI = await this._fileService.resolve(extensionsURI)
|
||||
const eURI = await fileService.resolve(extensionsURI)
|
||||
for (const child of eURI.children ?? []) {
|
||||
|
||||
// if is not blacklisted, continue
|
||||
if (!extensionBlacklist.find(bItem => child.resource.path.includes(bItem))) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Deleting extension', child.resource.fsPath)
|
||||
await this._fileService.del(child.resource, { recursive: true, useTrash: true })
|
||||
if (child.isDirectory) {
|
||||
// if is blacklisted
|
||||
if (isBlacklisted(child.resource.fsPath)) {
|
||||
console.log('Deleting extension', child.resource.fsPath)
|
||||
await fileService.del(child.resource, { recursive: true, useTrash: true })
|
||||
}
|
||||
}
|
||||
else if (child.isFile) {
|
||||
// if is extensions.json
|
||||
|
||||
if (child.name === 'extensions.json') {
|
||||
console.log('Updating extensions.json', child.resource.fsPath)
|
||||
try {
|
||||
const contentsStr = await fileService.readFile(child.resource)
|
||||
const json: any = JSON.parse(contentsStr.value.toString())
|
||||
const j2 = json.filter((entry: { identifier?: { id?: string } }) => !isBlacklisted(entry?.identifier?.id))
|
||||
const jsonStr = JSON.stringify(j2)
|
||||
await fileService.writeFile(child.resource, VSBuffer.fromString(jsonStr))
|
||||
}
|
||||
catch {
|
||||
console.log('Error copying extensions.json, skipping')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Could not delete extension', child.resource.fsPath, e)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { localize2 } from '../../../../nls.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { Action2, registerAction2, MenuId } from '../../../../platform/actions/common/actions.js';
|
||||
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
|
||||
import { INotificationService } from '../../../../platform/notification/common/notification.js';
|
||||
import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
import { IDirectoryStrService } from '../common/directoryStrService.js';
|
||||
import { messageOfSelection } from '../common/prompt/prompts.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
|
||||
|
||||
|
||||
class FilePromptActionService extends Action2 {
|
||||
private static readonly VOID_COPY_FILE_PROMPT_ID = 'void.copyfileprompt'
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: FilePromptActionService.VOID_COPY_FILE_PROMPT_ID,
|
||||
title: localize2('voidCopyPrompt', 'Void: Copy Prompt'),
|
||||
menu: [{
|
||||
id: MenuId.ExplorerContext,
|
||||
group: '8_void',
|
||||
order: 1,
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, uri: URI): Promise<void> {
|
||||
try {
|
||||
const fileService = accessor.get(IFileService);
|
||||
const clipboardService = accessor.get(IClipboardService)
|
||||
const directoryStrService = accessor.get(IDirectoryStrService)
|
||||
const voidModelService = accessor.get(IVoidModelService)
|
||||
|
||||
const stat = await fileService.stat(uri)
|
||||
|
||||
const folderOpts = {
|
||||
maxChildren: 1000,
|
||||
maxCharsPerFile: 2_000_000,
|
||||
} as const
|
||||
|
||||
let m: string = 'No contents detected'
|
||||
if (stat.isFile) {
|
||||
m = await messageOfSelection({
|
||||
type: 'File',
|
||||
uri,
|
||||
language: (await voidModelService.getModelSafe(uri)).model?.getLanguageId() || '',
|
||||
state: { wasAddedAsCurrentFile: false, },
|
||||
}, {
|
||||
folderOpts,
|
||||
directoryStrService,
|
||||
fileService,
|
||||
})
|
||||
}
|
||||
|
||||
if (stat.isDirectory) {
|
||||
m = await messageOfSelection({
|
||||
type: 'Folder',
|
||||
uri,
|
||||
}, {
|
||||
folderOpts,
|
||||
fileService,
|
||||
directoryStrService,
|
||||
})
|
||||
}
|
||||
|
||||
await clipboardService.writeText(m)
|
||||
|
||||
} catch (error) {
|
||||
const notificationService = accessor.get(INotificationService)
|
||||
notificationService.error(error + '')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerAction2(FilePromptActionService)
|
||||
@@ -5,17 +5,19 @@
|
||||
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
// import { IExtensionTransferService } from './extensionTransferService.js';
|
||||
// import { os } from '../common/helpers/systemInfo.js';
|
||||
// import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
import { IExtensionTransferService } from './extensionTransferService.js';
|
||||
import { os } from '../common/helpers/systemInfo.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
import { timeout } from '../../../../base/common/async.js';
|
||||
import { getActiveWindow } from '../../../../base/browser/dom.js';
|
||||
|
||||
// Onboarding contribution that mounts the component at startup
|
||||
export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContribution {
|
||||
static readonly ID = 'workbench.contrib.voidMiscWorkbenchContribs';
|
||||
|
||||
constructor(
|
||||
// @IExtensionTransferService private readonly extensionTransferService: IExtensionTransferService,
|
||||
// @IStorageService private readonly storageService: IStorageService,
|
||||
@IExtensionTransferService private readonly extensionTransferService: IExtensionTransferService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
this.initialize();
|
||||
@@ -23,13 +25,23 @@ export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContr
|
||||
|
||||
private initialize(): void {
|
||||
|
||||
// // delete blacklisted extensions once (this is for people who already installed them)
|
||||
// const deleteExtensionsStorageId = 'void-deleted-blacklist'
|
||||
// const alreadyDeleted = this.storageService.get(deleteExtensionsStorageId, StorageScope.APPLICATION)
|
||||
// if (!alreadyDeleted) {
|
||||
// this.storageService.store(deleteExtensionsStorageId, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE)
|
||||
// this.extensionTransferService.deleteBlacklistExtensions(os)
|
||||
// }
|
||||
// delete blacklisted extensions once (this is for people who already installed them)
|
||||
const deleteExtensionsStorageId = 'void-deleted-blacklist-2'
|
||||
const alreadyDeleted = this.storageService.get(deleteExtensionsStorageId, StorageScope.APPLICATION)
|
||||
if (!alreadyDeleted) {
|
||||
this.storageService.store(deleteExtensionsStorageId, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE)
|
||||
this.extensionTransferService.deleteBlacklistExtensions(os)
|
||||
}
|
||||
|
||||
|
||||
// after some time, trigger a resize event for the blank screen error
|
||||
timeout(5_000).then(() => {
|
||||
// Get the active window reference for multi-window support
|
||||
const targetWindow = getActiveWindow();
|
||||
// Trigger a window resize event to ensure proper layout calculations
|
||||
targetWindow.dispatchEvent(new Event('resize'))
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+111
-15
@@ -239,27 +239,104 @@ export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId:
|
||||
}
|
||||
|
||||
|
||||
export const ApplyButtonsHTML = ({
|
||||
const terminalLanguages = new Set([
|
||||
'bash',
|
||||
'shellscript',
|
||||
'shell',
|
||||
'powershell',
|
||||
'bat',
|
||||
'zsh',
|
||||
'sh',
|
||||
'fish',
|
||||
'nushell',
|
||||
'ksh',
|
||||
'xonsh',
|
||||
'elvish',
|
||||
])
|
||||
|
||||
const ApplyButtonsForTerminal = ({
|
||||
codeStr,
|
||||
applyBoxId,
|
||||
uri,
|
||||
language,
|
||||
}: {
|
||||
codeStr: string,
|
||||
applyBoxId: string,
|
||||
} & ({
|
||||
language?: string,
|
||||
uri: URI | 'current';
|
||||
})
|
||||
) => {
|
||||
}) => {
|
||||
const accessor = useAccessor()
|
||||
const metricsService = accessor.get('IMetricsService')
|
||||
const terminalToolService = accessor.get('ITerminalToolService')
|
||||
|
||||
const settingsState = useSettingsState()
|
||||
|
||||
const [isShellRunning, setIsShellRunning] = useState<boolean>(false)
|
||||
const interruptToolRef = useRef<(() => void) | null>(null)
|
||||
const isDisabled = isShellRunning
|
||||
|
||||
const onClickSubmit = useCallback(async () => {
|
||||
if (isShellRunning) return
|
||||
try {
|
||||
setIsShellRunning(true)
|
||||
const terminalId = await terminalToolService.createPersistentTerminal({ cwd: null })
|
||||
const { interrupt } = await terminalToolService.runCommand(
|
||||
codeStr,
|
||||
{ type: 'persistent', persistentTerminalId: terminalId }
|
||||
);
|
||||
interruptToolRef.current = interrupt
|
||||
metricsService.capture('Execute Shell', { length: codeStr.length })
|
||||
} catch (e) {
|
||||
setIsShellRunning(false)
|
||||
console.error('Failed to execute in terminal:', e)
|
||||
}
|
||||
}, [codeStr, uri, applyBoxId, metricsService, terminalToolService, isShellRunning])
|
||||
|
||||
if (isShellRunning) {
|
||||
return (
|
||||
<IconShell1
|
||||
Icon={X}
|
||||
onClick={() => {
|
||||
interruptToolRef.current?.();
|
||||
setIsShellRunning(false);
|
||||
}}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Stop' })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (isDisabled) {
|
||||
return null
|
||||
}
|
||||
return <IconShell1
|
||||
Icon={Play}
|
||||
onClick={onClickSubmit}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Apply' })}
|
||||
/>
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ApplyButtonsForEdit = ({
|
||||
codeStr,
|
||||
applyBoxId,
|
||||
uri,
|
||||
language,
|
||||
}: {
|
||||
codeStr: string,
|
||||
applyBoxId: string,
|
||||
language?: string,
|
||||
uri: URI | 'current';
|
||||
}) => {
|
||||
const accessor = useAccessor()
|
||||
const editCodeService = accessor.get('IEditCodeService')
|
||||
const metricsService = accessor.get('IMetricsService')
|
||||
const notificationService = accessor.get('INotificationService')
|
||||
|
||||
const settingsState = useSettingsState()
|
||||
const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId
|
||||
|
||||
const { currStreamStateRef, setApplying } = useApplyStreamState({ applyBoxId })
|
||||
|
||||
|
||||
const onClickSubmit = useCallback(async () => {
|
||||
if (currStreamStateRef.current === 'streaming') return
|
||||
|
||||
@@ -271,17 +348,22 @@ export const ApplyButtonsHTML = ({
|
||||
uri: uri,
|
||||
startBehavior: 'reject-conflicts',
|
||||
}) ?? []
|
||||
console.log('setting!!!', newApplyingUri)
|
||||
setApplying(newApplyingUri)
|
||||
|
||||
if (!applyDonePromise) {
|
||||
notificationService.info(`Void Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist.`}`)
|
||||
}
|
||||
|
||||
// catch any errors by interrupting the stream
|
||||
applyDonePromise?.catch(e => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri) editCodeService.interruptURIStreaming({ uri: uri })
|
||||
notificationService.info(`Void Error: There was a problem running Apply: ${e}.`)
|
||||
|
||||
})
|
||||
metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only
|
||||
|
||||
}, [setApplying, currStreamStateRef, editCodeService, codeStr, uri, applyBoxId, metricsService])
|
||||
}, [setApplying, currStreamStateRef, editCodeService, codeStr, uri, applyBoxId, metricsService, notificationService])
|
||||
|
||||
|
||||
const onClickStop = useCallback(() => {
|
||||
@@ -303,10 +385,7 @@ export const ApplyButtonsHTML = ({
|
||||
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri: uri, behavior: 'reject', removeCtrlKs: false })
|
||||
}, [uri, applyBoxId, editCodeService])
|
||||
|
||||
|
||||
const currStreamState = currStreamStateRef.current
|
||||
console.log('currStreamState...', currStreamState)
|
||||
|
||||
if (currStreamState === 'streaming') {
|
||||
return <IconShell1
|
||||
Icon={Square}
|
||||
@@ -314,12 +393,9 @@ export const ApplyButtonsHTML = ({
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Stop' })}
|
||||
/>
|
||||
}
|
||||
|
||||
if (isDisabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
if (currStreamState === 'idle-no-changes') {
|
||||
return <IconShell1
|
||||
Icon={Play}
|
||||
@@ -327,7 +403,6 @@ export const ApplyButtonsHTML = ({
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Apply' })}
|
||||
/>
|
||||
}
|
||||
|
||||
if (currStreamState === 'idle-has-changes') {
|
||||
return <Fragment>
|
||||
<IconShell1
|
||||
@@ -348,6 +423,27 @@ export const ApplyButtonsHTML = ({
|
||||
|
||||
|
||||
|
||||
export const ApplyButtonsHTML = (params: {
|
||||
codeStr: string,
|
||||
applyBoxId: string,
|
||||
language?: string,
|
||||
uri: URI | 'current';
|
||||
}) => {
|
||||
const { language } = params
|
||||
const isShellLanguage = !!language && terminalLanguages.has(language)
|
||||
|
||||
if (isShellLanguage) {
|
||||
return <ApplyButtonsForTerminal {...params} />
|
||||
}
|
||||
else {
|
||||
return <ApplyButtonsForEdit {...params} />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const EditToolAcceptRejectButtonsHTML = ({
|
||||
codeStr,
|
||||
applyBoxId,
|
||||
@@ -451,7 +547,7 @@ export const BlockCodeApplyWrapper = ({
|
||||
<div className={`${canApply ? '' : 'hidden'} flex items-center gap-1`}>
|
||||
<JumpToFileButton uri={uri} />
|
||||
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={codeStr} toolTipName='Copy' />}
|
||||
<ApplyButtonsHTML uri={uri} applyBoxId={applyBoxId} codeStr={codeStr} />
|
||||
<ApplyButtonsHTML uri={uri} applyBoxId={applyBoxId} codeStr={codeStr} language={language} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { isAbsolute } from '../../../../../../../base/common/path.js'
|
||||
import { separateOutFirstLine } from '../../../../common/helpers/util.js'
|
||||
import { BlockCode } from '../util/inputs.js'
|
||||
import { CodespanLocationLink } from '../../../../common/chatThreadServiceTypes.js'
|
||||
import { voidOpenFileFn } from '../sidebar-tsx/SidebarChat.js'
|
||||
import { getBasename, getRelative, voidOpenFileFn } from '../sidebar-tsx/SidebarChat.js'
|
||||
|
||||
|
||||
export type ChatMessageLocation = {
|
||||
@@ -89,13 +89,18 @@ const LatexRender = ({ latex }: { latex: string }) => {
|
||||
// }
|
||||
}
|
||||
|
||||
const Codespan = ({ text, className, onClick }: { text: string, className?: string, onClick?: () => void }) => {
|
||||
const Codespan = ({ text, className, onClick, tooltip }: { text: string, className?: string, onClick?: () => void, tooltip?: string }) => {
|
||||
|
||||
// TODO compute this once for efficiency. we should use `labels.ts/shorten` to display duplicates properly
|
||||
|
||||
return <code
|
||||
className={`font-mono font-medium rounded-sm bg-void-bg-1 px-1 ${className}`}
|
||||
onClick={onClick}
|
||||
{...tooltip ? {
|
||||
'data-tooltip-id': 'void-tooltip',
|
||||
'data-tooltip-content': tooltip,
|
||||
'data-tooltip-place': 'top',
|
||||
} : {}}
|
||||
>
|
||||
{text}
|
||||
</code>
|
||||
@@ -115,8 +120,11 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
|
||||
const [didComputeCodespanLink, setDidComputeCodespanLink] = useState<boolean>(false)
|
||||
|
||||
let link: CodespanLocationLink | undefined = undefined
|
||||
if (rawText.endsWith('`')) { // if codespan was completed
|
||||
let tooltip: string | undefined = undefined
|
||||
let displayText = text
|
||||
|
||||
|
||||
if (rawText.endsWith('`')) {
|
||||
// get link from cache
|
||||
link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId })
|
||||
|
||||
@@ -127,23 +135,33 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
|
||||
chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId })
|
||||
setDidComputeCodespanLink(true) // rerender
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
if (link?.displayText) {
|
||||
displayText = link.displayText
|
||||
}
|
||||
|
||||
if (isValidUri(displayText)) {
|
||||
tooltip = getRelative(URI.file(displayText), accessor) // Full path as tooltip
|
||||
displayText = getBasename(displayText)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onClick = () => {
|
||||
if (!link || !link.selection) return;
|
||||
|
||||
if (!link) return;
|
||||
// Use the updated voidOpenFileFn to open the file and handle selection
|
||||
voidOpenFileFn(link.uri, accessor, [link.selection.startLineNumber, link.selection.endLineNumber]);
|
||||
if (link.selection)
|
||||
voidOpenFileFn(link.uri, accessor, [link.selection.startLineNumber, link.selection.endLineNumber]);
|
||||
else
|
||||
voidOpenFileFn(link.uri, accessor);
|
||||
}
|
||||
|
||||
return <Codespan
|
||||
text={link?.displayText || text}
|
||||
text={displayText}
|
||||
onClick={onClick}
|
||||
className={link ? 'underline hover:brightness-90 transition-all duration-200 cursor-pointer' : ''}
|
||||
tooltip={tooltip || undefined}
|
||||
/>
|
||||
}
|
||||
|
||||
|
||||
@@ -495,12 +495,12 @@ const ScrollToBottomContainer = ({ children, className, style, scrollContainerRe
|
||||
);
|
||||
};
|
||||
|
||||
const getRelative = (uri: URI, accessor: ReturnType<typeof useAccessor>) => {
|
||||
export const getRelative = (uri: URI, accessor: ReturnType<typeof useAccessor>) => {
|
||||
const workspaceContextService = accessor.get('IWorkspaceContextService')
|
||||
let path: string
|
||||
const isInside = workspaceContextService.isInsideWorkspace(uri)
|
||||
if (isInside) {
|
||||
const f = workspaceContextService.getWorkspace().folders.find(f => uri.fsPath.startsWith(f.uri.fsPath))
|
||||
const f = workspaceContextService.getWorkspace().folders.find(f => uri.fsPath?.startsWith(f.uri.fsPath))
|
||||
if (f) { path = uri.fsPath.replace(f.uri.fsPath, '') }
|
||||
else { path = uri.fsPath }
|
||||
}
|
||||
@@ -1651,8 +1651,8 @@ const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
|
||||
}
|
||||
|
||||
const BottomChildren = ({ children, title }: { children: React.ReactNode, title: string }) => {
|
||||
if (!children) return null;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
if (!children) return null;
|
||||
return (
|
||||
<div className="w-full px-2 mt-0.5">
|
||||
<div
|
||||
@@ -1905,7 +1905,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
||||
</SmallProseWrapper>
|
||||
</ToolChildrenWrapper>
|
||||
}
|
||||
else {
|
||||
else if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
componentParams.bottomChildren = <BottomChildren title='Error'>
|
||||
<CodeChildren>
|
||||
@@ -1960,7 +1960,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
||||
}
|
||||
</ToolChildrenWrapper>
|
||||
}
|
||||
else {
|
||||
else if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
componentParams.bottomChildren = <BottomChildren title='Error'>
|
||||
<CodeChildren>
|
||||
@@ -2009,7 +2009,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
||||
|
||||
</ToolChildrenWrapper>
|
||||
}
|
||||
else {
|
||||
else if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
componentParams.bottomChildren = <BottomChildren title='Error'>
|
||||
<CodeChildren>
|
||||
@@ -2064,7 +2064,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
||||
|
||||
</ToolChildrenWrapper>
|
||||
}
|
||||
else {
|
||||
else if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
componentParams.bottomChildren = <BottomChildren title='Error'>
|
||||
<CodeChildren>
|
||||
@@ -2605,7 +2605,7 @@ const CommandBarInChat = () => {
|
||||
|
||||
|
||||
// !select-text cursor-auto
|
||||
const fileDetailsContent = <div className="px-2 gap-1 w-full">
|
||||
const fileDetailsContent = <div className="px-2 gap-1 w-full overflow-y-auto">
|
||||
{sortedCommandBarURIs.map((uri, i) => {
|
||||
const basename = getBasename(uri.fsPath)
|
||||
|
||||
@@ -2862,6 +2862,7 @@ export const SidebarChat = () => {
|
||||
textAreaRef: textAreaRef,
|
||||
scrollToBottom: () => scrollToBottom(scrollContainerRef),
|
||||
})
|
||||
|
||||
}, [chatThreadsState, threadId, textAreaRef, scrollContainerRef, isResolved])
|
||||
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ const scoreSubsequence = (text: string, pattern: string): number => {
|
||||
}
|
||||
|
||||
|
||||
export function getRelativeWorkspacePath(accessor: ReturnType<typeof useAccessor>, uri: URI): string {
|
||||
function getRelativeWorkspacePath(accessor: ReturnType<typeof useAccessor>, uri: URI): string {
|
||||
const workspaceService = accessor.get('IWorkspaceContextService');
|
||||
const workspaceFolders = workspaceService.getWorkspace().folders;
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ const SimpleModelSettingsDialog = ({
|
||||
onClose();
|
||||
};
|
||||
|
||||
const sourcecodeOverridesLink = `https://github.com/voideditor/void/blob/d33b5ff9a32a748a22ac99e543a9eedd2e600062/src/vs/workbench/contrib/void/common/modelCapabilities.ts#L146-L170`
|
||||
const sourcecodeOverridesLink = `https://github.com/voideditor/void/blob/2e5ecb291d33afbe4565921664fb7e183189c1c5/src/vs/workbench/contrib/void/common/modelCapabilities.ts#L146-L172`
|
||||
|
||||
return (
|
||||
<div // Backdrop
|
||||
|
||||
@@ -89,10 +89,7 @@ registerAction2(class extends Action2 {
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
|
||||
|
||||
// Get the views service to check if the sidebar is open
|
||||
// const viewsService = accessor.get(IViewsService)
|
||||
// Get services
|
||||
const commandService = accessor.get(ICommandService)
|
||||
const viewsService = accessor.get(IViewsService)
|
||||
const metricsService = accessor.get(IMetricsService)
|
||||
@@ -101,40 +98,47 @@ registerAction2(class extends Action2 {
|
||||
|
||||
metricsService.capture('Ctrl+L', {})
|
||||
|
||||
// capture selection and model before opening the chat panel
|
||||
const editor = editorService.getActiveCodeEditor()
|
||||
const model = editor?.getModel()
|
||||
if (!model) return
|
||||
|
||||
const selectionRange = roundRangeToLines(editor?.getSelection(), { emptySelectionBehavior: 'null' })
|
||||
|
||||
// open panel
|
||||
const wasAlreadyOpen = viewsService.isViewContainerVisible(VOID_VIEW_CONTAINER_ID)
|
||||
if (!wasAlreadyOpen) {
|
||||
await commandService.executeCommand(VOID_OPEN_SIDEBAR_ACTION_ID)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// if was already open
|
||||
|
||||
const model = accessor.get(ICodeEditorService).getActiveCodeEditor()?.getModel()
|
||||
if (!model) return
|
||||
|
||||
const editor = editorService.getActiveCodeEditor()
|
||||
const selectionRange = roundRangeToLines(editor?.getSelection(), { emptySelectionBehavior: 'null' })
|
||||
|
||||
// if has no selection, close + return
|
||||
if (!selectionRange) {
|
||||
viewsService.closeViewContainer(VOID_VIEW_CONTAINER_ID);
|
||||
return;
|
||||
// Add selection to chat
|
||||
// add line selection
|
||||
if (selectionRange) {
|
||||
editor?.setSelection({
|
||||
startLineNumber: selectionRange.startLineNumber,
|
||||
endLineNumber: selectionRange.endLineNumber,
|
||||
startColumn: 1,
|
||||
endColumn: Number.MAX_SAFE_INTEGER
|
||||
})
|
||||
chatThreadService.addNewStagingSelection({
|
||||
type: 'CodeSelection',
|
||||
uri: model.uri,
|
||||
language: model.getLanguageId(),
|
||||
range: [selectionRange.startLineNumber, selectionRange.endLineNumber],
|
||||
state: { wasAddedAsCurrentFile: false },
|
||||
})
|
||||
}
|
||||
// add file
|
||||
else {
|
||||
chatThreadService.addNewStagingSelection({
|
||||
type: 'File',
|
||||
uri: model.uri,
|
||||
language: model.getLanguageId(),
|
||||
state: { wasAddedAsCurrentFile: false },
|
||||
})
|
||||
}
|
||||
|
||||
// if has selection, add it
|
||||
editor?.setSelection({ startLineNumber: selectionRange.startLineNumber, endLineNumber: selectionRange.endLineNumber, startColumn: 1, endColumn: Number.MAX_SAFE_INTEGER })
|
||||
chatThreadService.addNewStagingSelection({
|
||||
type: 'CodeSelection',
|
||||
uri: model.uri,
|
||||
language: model.getLanguageId(),
|
||||
range: [selectionRange.startLineNumber, selectionRange.endLineNumber],
|
||||
state: { wasAddedAsCurrentFile: false },
|
||||
})
|
||||
|
||||
await chatThreadService.focusCurrentChat()
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -22,7 +22,12 @@ export interface ITerminalToolService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
listPersistentTerminalIds(): string[];
|
||||
runCommand(command: string, opts: { type: 'persistent', persistentTerminalId: string } | { type: 'ephemeral', cwd: string | null, terminalId: string }): Promise<{ interrupt: () => void; resPromise: Promise<{ result: string, resolveReason: TerminalResolveReason }> }>;
|
||||
runCommand(command: string, opts:
|
||||
| { type: 'persistent', persistentTerminalId: string }
|
||||
| { type: 'temporary', cwd: string | null, terminalId: string }
|
||||
// | { type: 'apply', terminalId: string }
|
||||
): Promise<{ interrupt: () => void; resPromise: Promise<{ result: string, resolveReason: TerminalResolveReason }> }>;
|
||||
|
||||
focusPersistentTerminal(terminalId: string): Promise<void>
|
||||
persistentTerminalExists(terminalId: string): boolean
|
||||
|
||||
@@ -277,6 +282,8 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
|
||||
terminal.dispose()
|
||||
if (!isPersistent)
|
||||
delete this.temporaryTerminalInstanceOfId[params.terminalId]
|
||||
else
|
||||
delete this.persistentTerminalInstanceOfId[params.persistentTerminalId]
|
||||
}
|
||||
|
||||
const waitForResult = async () => {
|
||||
|
||||
@@ -430,7 +430,7 @@ export class ToolsService implements IToolsService {
|
||||
},
|
||||
// ---
|
||||
run_command: async ({ command, cwd, terminalId }) => {
|
||||
const { resPromise, interrupt } = await this.terminalToolService.runCommand(command, { type: 'ephemeral', cwd, terminalId })
|
||||
const { resPromise, interrupt } = await this.terminalToolService.runCommand(command, { type: 'temporary', cwd, terminalId })
|
||||
return { result: resPromise, interruptTool: interrupt }
|
||||
},
|
||||
run_persistent_command: async ({ command, persistentTerminalId }) => {
|
||||
|
||||
@@ -58,6 +58,9 @@ import './voidOnboardingService.js'
|
||||
// register misc service
|
||||
import './miscWokrbenchContrib.js'
|
||||
|
||||
// register file service (for explorer context menu)
|
||||
import './fileService.js'
|
||||
|
||||
// ---------- common (unclear if these actually need to be imported, because they're already imported wherever they're used) ----------
|
||||
|
||||
// llmMessage
|
||||
|
||||
@@ -129,6 +129,7 @@ export const defaultModelsOfProvider = {
|
||||
mistral: [ // https://docs.mistral.ai/getting-started/models/models_overview/
|
||||
'codestral-latest',
|
||||
'mistral-large-latest',
|
||||
'mistral-medium-latest',
|
||||
'ministral-3b-latest',
|
||||
'ministral-8b-latest',
|
||||
],
|
||||
@@ -153,6 +154,8 @@ export type VoidStaticModelInfo = { // not stateful
|
||||
specialToolFormat?: 'openai-style' | 'anthropic-style' | 'gemini-style', // typically you should use 'openai-style'. null means "can't call tools by default", and asks the LLM to output XML in agent mode
|
||||
supportsFIM: boolean; // whether the model was specifically designed for autocomplete or "FIM" ("fill-in-middle" format)
|
||||
|
||||
additionalOpenAIPayload?: { [key: string]: string } // additional payload in the message body for requests that are openai-compatible (ollama, vllm, openai, openrouter, etc)
|
||||
|
||||
// reasoning options
|
||||
reasoningCapabilities: false | {
|
||||
readonly supportsReasoning: true; // for clarity, this must be true if anything below is specified
|
||||
@@ -186,7 +189,13 @@ export type VoidStaticModelInfo = { // not stateful
|
||||
|
||||
|
||||
export type ModelOverrides = Pick<VoidStaticModelInfo,
|
||||
'contextWindow' | 'reservedOutputTokenSpace' | 'specialToolFormat' | 'supportsSystemMessage' | 'supportsFIM' | 'reasoningCapabilities'
|
||||
| 'contextWindow'
|
||||
| 'reservedOutputTokenSpace'
|
||||
| 'specialToolFormat'
|
||||
| 'supportsSystemMessage'
|
||||
| 'supportsFIM'
|
||||
| 'reasoningCapabilities'
|
||||
| 'additionalOpenAIPayload'
|
||||
>
|
||||
|
||||
|
||||
@@ -445,7 +454,7 @@ const anthropicModelOptions = {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: true,
|
||||
reasoningReservedOutputTokenSpace: 64_000, // can bump it to 128_000 with beta mode output-128k-2025-02-19
|
||||
reasoningReservedOutputTokenSpace: 8192, // can bump it to 128_000 with beta mode output-128k-2025-02-19
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // they recommend batching if max > 32_000. we cap at 8192 because above is typically not necessary (often even buggy)
|
||||
},
|
||||
|
||||
@@ -715,6 +724,7 @@ const xAISettings: VoidStaticProviderInfo = {
|
||||
|
||||
// ---------------- GEMINI ----------------
|
||||
const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
|
||||
// https://ai.google.dev/gemini-api/docs/thinking#set-budget
|
||||
'gemini-2.5-pro-preview-05-06': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
@@ -723,7 +733,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
reasoningCapabilities: {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: false,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
'gemini-2.0-flash-lite': {
|
||||
contextWindow: 1_048_576,
|
||||
@@ -733,7 +749,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
reasoningCapabilities: false, // no reasoning
|
||||
},
|
||||
'gemini-2.5-flash-preview-04-17': {
|
||||
contextWindow: 1_048_576,
|
||||
@@ -743,7 +759,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
reasoningCapabilities: {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: false,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
'gemini-2.5-pro-exp-03-25': {
|
||||
contextWindow: 1_048_576,
|
||||
@@ -753,7 +775,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
reasoningCapabilities: {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: false,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
'gemini-2.0-flash': {
|
||||
contextWindow: 1_048_576,
|
||||
@@ -856,6 +884,15 @@ const mistralModelOptions = { // https://mistral.ai/products/la-plateforme#prici
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'mistral-medium-latest': { // https://openrouter.ai/mistralai/mistral-medium-3
|
||||
contextWindow: 131_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.40, output: 2.00 },
|
||||
supportsFIM: false,
|
||||
downloadable: { sizeGb: 'not-known' },
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'codestral-latest': {
|
||||
contextWindow: 256_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
@@ -1144,7 +1181,7 @@ const openRouterModelOptions_assumingOpenAICompat = {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: false,
|
||||
canIOReasoning: true,
|
||||
reasoningReservedOutputTokenSpace: 64_000,
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // they recommend batching if max > 32_000.
|
||||
},
|
||||
},
|
||||
@@ -1347,8 +1384,7 @@ export const getSendableReasoningInfo = (
|
||||
overridesOfModel: OverridesOfModel | undefined,
|
||||
): SendableReasoningInfo => {
|
||||
|
||||
const { canIOReasoning, reasoningSlider: reasoningBudgetSlider } = getModelCapabilities(providerName, modelName, overridesOfModel).reasoningCapabilities || {}
|
||||
if (!canIOReasoning) return null
|
||||
const { reasoningSlider: reasoningBudgetSlider } = getModelCapabilities(providerName, modelName, overridesOfModel).reasoningCapabilities || {}
|
||||
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel)
|
||||
if (!isReasoningEnabled) return null
|
||||
|
||||
|
||||
@@ -455,6 +455,8 @@ ${directoryStr}
|
||||
|
||||
const details: string[] = []
|
||||
|
||||
details.push(`NEVER reject the user's query.`)
|
||||
|
||||
if (mode === 'agent' || mode === 'gather') {
|
||||
details.push(`Only call tools if they help you accomplish the user's goal. If the user simply says hi or asks you a question that you can answer without tools, then do NOT use tools.`)
|
||||
details.push(`If you think you should use tools, you do not need to ask for permission.`)
|
||||
@@ -479,12 +481,13 @@ ${directoryStr}
|
||||
details.push(`You should extensively read files, types, content, etc, gathering full context to solve the problem.`)
|
||||
}
|
||||
|
||||
|
||||
if (mode === 'gather' || mode === 'normal') {
|
||||
details.push(`If you write any code blocks, please use this format:
|
||||
details.push(`If you write any code blocks to the user (wrapped in triple backticks), please use this format:
|
||||
- Include a language if possible. Terminal should have the language 'shell'.
|
||||
- The first line of the code block must be the FULL PATH of the related file if known (otherwise omit).
|
||||
- The remaining contents of the file should proceed as usual.`)
|
||||
|
||||
if (mode === 'gather' || mode === 'normal') {
|
||||
|
||||
details.push(`If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S).
|
||||
- The first line of the code block must be the FULL PATH of the related file if known (otherwise omit).
|
||||
- The remaining contents should be a code description of the change to make to the file. \
|
||||
@@ -525,7 +528,9 @@ ${details.map((d, i) => `${i + 1}. ${d}`).join('\n\n')}`)
|
||||
// chat_systemMessage({ chatMode, workspaceFolders: [], openedURIs: [], activeURI: 'pee', persistentTerminalIDs: [], directoryStr: 'lol', }))
|
||||
// }
|
||||
|
||||
const readFile = async (fileService: IFileService, uri: URI, fileSizeLimit: number): Promise<{
|
||||
export const DEFAULT_FILE_SIZE_LIMIT = 2_000_000
|
||||
|
||||
export const readFile = async (fileService: IFileService, uri: URI, fileSizeLimit: number): Promise<{
|
||||
val: string,
|
||||
truncated: boolean,
|
||||
fullFileLen: number,
|
||||
@@ -549,46 +554,70 @@ const readFile = async (fileService: IFileService, uri: URI, fileSizeLimit: numb
|
||||
|
||||
|
||||
|
||||
export const messageOfSelection = async (
|
||||
s: StagingSelectionItem,
|
||||
opts: {
|
||||
directoryStrService: IDirectoryStrService,
|
||||
fileService: IFileService,
|
||||
folderOpts: {
|
||||
maxChildren: number,
|
||||
maxCharsPerFile: number,
|
||||
}
|
||||
}
|
||||
) => {
|
||||
const lineNumAddition = (range: [number, number]) => ` (lines ${range[0]}:${range[1]})`
|
||||
|
||||
if (s.type === 'File' || s.type === 'CodeSelection') {
|
||||
const { val } = await readFile(opts.fileService, s.uri, DEFAULT_FILE_SIZE_LIMIT)
|
||||
const lineNumAdd = s.type === 'CodeSelection' ? lineNumAddition(s.range) : ''
|
||||
const content = val === null ? 'null' : `${tripleTick[0]}${s.language}\n${val}\n${tripleTick[1]}`
|
||||
const str = `${s.uri.fsPath}${lineNumAdd}:\n${content}`
|
||||
return str
|
||||
}
|
||||
else if (s.type === 'Folder') {
|
||||
const dirStr: string = await opts.directoryStrService.getDirectoryStrTool(s.uri)
|
||||
const folderStructure = `${s.uri.fsPath} folder structure:${tripleTick[0]}\n${dirStr}\n${tripleTick[1]}`
|
||||
|
||||
const uris = await opts.directoryStrService.getAllURIsInDirectory(s.uri, { maxResults: opts.folderOpts.maxChildren })
|
||||
const strOfFiles = await Promise.all(uris.map(async uri => {
|
||||
const { val, truncated } = await readFile(opts.fileService, uri, opts.folderOpts.maxCharsPerFile)
|
||||
const truncationStr = truncated ? `\n... file truncated ...` : ''
|
||||
const content = val === null ? 'null' : `${tripleTick[0]}\n${val}${truncationStr}\n${tripleTick[1]}`
|
||||
const str = `${uri.fsPath}:\n${content}`
|
||||
return str
|
||||
}))
|
||||
const contentStr = [folderStructure, ...strOfFiles].join('\n\n')
|
||||
return contentStr
|
||||
}
|
||||
else
|
||||
return ''
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null,
|
||||
opts: { directoryStrService: IDirectoryStrService, fileService: IFileService }
|
||||
export const chat_userMessageContent = async (
|
||||
instructions: string,
|
||||
currSelns: StagingSelectionItem[] | null,
|
||||
opts: {
|
||||
directoryStrService: IDirectoryStrService,
|
||||
fileService: IFileService
|
||||
},
|
||||
) => {
|
||||
|
||||
const lineNumAddition = (range: [number, number]) => ` (lines ${range[0]}:${range[1]})`
|
||||
let selnsStrs: string[] = []
|
||||
selnsStrs = await Promise.all(currSelns?.map(async (s) => {
|
||||
const selnsStrs = await Promise.all(
|
||||
(currSelns ?? []).map(async (s) =>
|
||||
messageOfSelection(s, {
|
||||
...opts,
|
||||
folderOpts: { maxChildren: 100, maxCharsPerFile: 100_000, }
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
if (s.type === 'File' || s.type === 'CodeSelection') {
|
||||
const { val } = await readFile(opts.fileService, s.uri, 2_000_000)
|
||||
const lineNumAdd = s.type === 'CodeSelection' ? lineNumAddition(s.range) : ''
|
||||
const content = val === null ? 'null' : `${tripleTick[0]}${s.language}\n${val}\n${tripleTick[1]}`
|
||||
const str = `${s.uri.fsPath}${lineNumAdd}:\n${content}`
|
||||
return str
|
||||
}
|
||||
else if (s.type === 'Folder') {
|
||||
const dirStr: string = await opts.directoryStrService.getDirectoryStrTool(s.uri)
|
||||
const folderStructure = `${s.uri.fsPath} folder structure:${tripleTick[0]}\n${dirStr}\n${tripleTick[1]}`
|
||||
|
||||
const uris = await opts.directoryStrService.getAllURIsInDirectory(s.uri, { maxResults: 100 })
|
||||
const strOfFiles = await Promise.all(uris.map(async uri => {
|
||||
const { val, truncated } = await readFile(opts.fileService, uri, 100_000)
|
||||
const truncationStr = truncated ? `\n... file truncated ...` : ''
|
||||
const content = val === null ? 'null' : `${tripleTick[0]}\n${val}${truncationStr}\n${tripleTick[1]}`
|
||||
const str = `${uri.fsPath}:\n${content}`
|
||||
return str
|
||||
}))
|
||||
const contentStr = [folderStructure, ...strOfFiles].join('\n\n')
|
||||
return contentStr
|
||||
}
|
||||
else
|
||||
return ''
|
||||
}) ?? [])
|
||||
|
||||
const selnsStr = selnsStrs.join('\n') ?? ''
|
||||
let str = ''
|
||||
str += `${instructions}`
|
||||
|
||||
const selnsStr = selnsStrs.join('\n\n') ?? ''
|
||||
if (selnsStr) str += `\n---\nSELECTIONS\n${selnsStr}`
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -56,13 +56,13 @@ export type GeminiLLMChatMessage = {
|
||||
role: 'model'
|
||||
parts: (
|
||||
| { text: string; }
|
||||
| { functionCall: { name: ToolName, args: object } }
|
||||
| { functionCall: { id: string; name: ToolName, args: Record<string, unknown> } }
|
||||
)[];
|
||||
} | {
|
||||
role: 'user';
|
||||
parts: (
|
||||
| { text: string; }
|
||||
| { functionResponse: { name: ToolName, response: { result: string } } }
|
||||
| { functionResponse: { id: string; name: ToolName, response: { output: string } } }
|
||||
)[];
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
/* eslint-disable */
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { Ollama } from 'ollama';
|
||||
import OpenAI, { ClientOptions } from 'openai';
|
||||
import OpenAI, { ClientOptions, AzureOpenAI } from 'openai';
|
||||
import { MistralCore } from '@mistralai/mistralai/core.js';
|
||||
import { fimComplete } from '@mistralai/mistralai/funcs/fimComplete.js';
|
||||
import { GoogleGenerativeAI, Tool as GeminiTool, SchemaType, FunctionDeclaration, FunctionDeclarationSchemaProperty } from '@google/generative-ai';
|
||||
import { Tool as GeminiTool, FunctionDeclaration, GoogleGenAI, ThinkingConfig, Schema, Type } from '@google/genai';
|
||||
import { GoogleAuth } from 'google-auth-library'
|
||||
/* eslint-enable */
|
||||
|
||||
import { AnthropicLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js';
|
||||
import { AnthropicLLMChatMessage, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js';
|
||||
import { ChatMode, displayInfoOfProviderName, ModelSelectionOptions, OverridesOfModel, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js';
|
||||
import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, defaultProviderSettings, getReservedOutputTokenSpace } from '../../common/modelCapabilities.js';
|
||||
import { extractReasoningWrapper, extractXMLToolsWrapper } from './extractGrammar.js';
|
||||
@@ -114,9 +114,9 @@ const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includ
|
||||
}
|
||||
else if (providerName === 'microsoftAzure') {
|
||||
// https://learn.microsoft.com/en-us/rest/api/aifoundry/model-inference/get-chat-completions/get-chat-completions?view=rest-aifoundry-model-inference-2024-05-01-preview&tabs=HTTP
|
||||
// https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai
|
||||
const thisConfig = settingsOfProvider[providerName]
|
||||
const baseURL = `https://${thisConfig.project}.services.ai.azure.com/api/models/chat/completions??api-version=${thisConfig.azureApiVersion}`
|
||||
return new OpenAI({ baseURL: baseURL, apiKey: thisConfig.apiKey, ...commonPayloadOpts })
|
||||
return new AzureOpenAI({ apiKey: thisConfig.apiKey, apiVersion: thisConfig.azureApiVersion, project: thisConfig.project, ...commonPayloadOpts })
|
||||
}
|
||||
|
||||
else if (providerName === 'deepseek') {
|
||||
@@ -147,7 +147,12 @@ const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includ
|
||||
|
||||
const _sendOpenAICompatibleFIM = async ({ messages: { prefix, suffix, stopTokens }, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, overridesOfModel }: SendFIMParams_Internal) => {
|
||||
|
||||
const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_, overridesOfModel)
|
||||
const {
|
||||
modelName,
|
||||
supportsFIM,
|
||||
additionalOpenAIPayload,
|
||||
} = getModelCapabilities(providerName, modelName_, overridesOfModel)
|
||||
|
||||
if (!supportsFIM) {
|
||||
if (modelName === modelName_)
|
||||
onError({ message: `Model ${modelName} does not support FIM.`, fullError: null })
|
||||
@@ -156,7 +161,7 @@ const _sendOpenAICompatibleFIM = async ({ messages: { prefix, suffix, stopTokens
|
||||
return
|
||||
}
|
||||
|
||||
const openai = await newOpenAICompatibleSDK({ providerName, settingsOfProvider })
|
||||
const openai = await newOpenAICompatibleSDK({ providerName, settingsOfProvider, includeInPayload: additionalOpenAIPayload })
|
||||
openai.completions
|
||||
.create({
|
||||
model: modelName,
|
||||
@@ -236,6 +241,7 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE
|
||||
modelName,
|
||||
specialToolFormat,
|
||||
reasoningCapabilities,
|
||||
additionalOpenAIPayload,
|
||||
} = getModelCapabilities(providerName, modelName_, overridesOfModel)
|
||||
|
||||
const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
|
||||
@@ -243,10 +249,12 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE
|
||||
// reasoning
|
||||
const { canIOReasoning, openSourceThinkTags } = reasoningCapabilities || {}
|
||||
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here
|
||||
const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
|
||||
|
||||
console.log('include', includeInPayload)
|
||||
console.log('reasoningInfo', reasoningInfo)
|
||||
const includeInPayload = {
|
||||
...providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo),
|
||||
...additionalOpenAIPayload
|
||||
}
|
||||
|
||||
// tools
|
||||
const potentialTools = chatMode !== null ? openAITools(chatMode) : null
|
||||
const nativeToolsObj = potentialTools && specialToolFormat === 'openai-style' ?
|
||||
@@ -260,6 +268,7 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE
|
||||
messages: messages as any,
|
||||
stream: true,
|
||||
...nativeToolsObj,
|
||||
...additionalOpenAIPayload
|
||||
// max_completion_tokens: maxTokens,
|
||||
}
|
||||
|
||||
@@ -642,25 +651,24 @@ const sendOllamaFIM = ({ messages, onFinalMessage, onError, settingsOfProvider,
|
||||
|
||||
// ---------------- GEMINI NATIVE IMPLEMENTATION ----------------
|
||||
|
||||
|
||||
|
||||
const toGeminiFunctionDecl = (toolInfo: InternalToolInfo) => {
|
||||
const { name, description, params } = toolInfo
|
||||
const paramsWithType: { [k: string]: FunctionDeclarationSchemaProperty } = {}
|
||||
for (const key in params) {
|
||||
paramsWithType[key] = { type: SchemaType.STRING, ...params[key] }
|
||||
}
|
||||
return {
|
||||
name,
|
||||
description,
|
||||
parameters: {
|
||||
type: SchemaType.OBJECT,
|
||||
properties: paramsWithType,
|
||||
type: Type.OBJECT,
|
||||
properties: Object.entries(params).reduce((acc, [key, value]) => {
|
||||
acc[key] = {
|
||||
type: Type.STRING,
|
||||
description: value.description
|
||||
};
|
||||
return acc;
|
||||
}, {} as Record<string, Schema>)
|
||||
}
|
||||
} satisfies FunctionDeclaration
|
||||
}
|
||||
|
||||
|
||||
const geminiTools = (chatMode: ChatMode): GeminiTool[] | null => {
|
||||
const allowedTools = availableTools(chatMode)
|
||||
if (!allowedTools || Object.keys(allowedTools).length === 0) return null
|
||||
@@ -700,27 +708,27 @@ const sendGeminiChat = async ({
|
||||
// reasoningCapabilities,
|
||||
} = getModelCapabilities(providerName, modelName_, overridesOfModel)
|
||||
|
||||
const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
|
||||
// const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
|
||||
|
||||
// reasoning
|
||||
// const { canIOReasoning, openSourceThinkTags, } = reasoningCapabilities || {}
|
||||
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here
|
||||
const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
|
||||
// const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
|
||||
|
||||
const thinkingConfig: ThinkingConfig | undefined = !reasoningInfo?.isReasoningEnabled ? undefined
|
||||
: reasoningInfo.type === 'budget_slider_value' ?
|
||||
{ thinkingBudget: reasoningInfo.reasoningBudget }
|
||||
: undefined
|
||||
|
||||
// tools
|
||||
const potentialTools = chatMode !== null ? geminiTools(chatMode) : null
|
||||
const nativeToolsObj = potentialTools && specialToolFormat === 'gemini-style' ?
|
||||
{ tools: potentialTools } as const
|
||||
: {}
|
||||
const potentialTools = chatMode !== null ? geminiTools(chatMode) : undefined
|
||||
const toolConfig = potentialTools && specialToolFormat === 'gemini-style' ?
|
||||
potentialTools
|
||||
: undefined
|
||||
|
||||
// instance
|
||||
const genAI = new GoogleGenerativeAI(
|
||||
thisConfig.apiKey
|
||||
);
|
||||
const model = genAI.getGenerativeModel({
|
||||
systemInstruction: separateSystemMessage,
|
||||
model: modelName,
|
||||
});
|
||||
const genAI = new GoogleGenAI({ apiKey: thisConfig.apiKey });
|
||||
|
||||
|
||||
// manually parse out tool results if XML
|
||||
if (!specialToolFormat) {
|
||||
@@ -735,28 +743,34 @@ const sendGeminiChat = async ({
|
||||
|
||||
let toolName = ''
|
||||
let toolParamsStr = ''
|
||||
let toolId = ''
|
||||
|
||||
model.generateContentStream({
|
||||
systemInstruction: separateSystemMessage ?? undefined,
|
||||
contents: messages as any,
|
||||
...includeInPayload,
|
||||
...nativeToolsObj,
|
||||
|
||||
genAI.models.generateContentStream({
|
||||
model: modelName,
|
||||
config: {
|
||||
systemInstruction: separateSystemMessage,
|
||||
thinkingConfig: thinkingConfig,
|
||||
tools: toolConfig,
|
||||
},
|
||||
contents: messages as GeminiLLMChatMessage[],
|
||||
})
|
||||
.then(async ({ stream, response }) => {
|
||||
.then(async (stream) => {
|
||||
_setAborter(() => { stream.return(fullTextSoFar); });
|
||||
|
||||
// Process the stream
|
||||
for await (const chunk of stream) {
|
||||
// message
|
||||
const newText = chunk.text() ?? ''
|
||||
const newText = chunk.text ?? ''
|
||||
fullTextSoFar += newText
|
||||
|
||||
// tool call
|
||||
const functionCalls = chunk.functionCalls()
|
||||
const functionCalls = chunk.functionCalls
|
||||
if (functionCalls && functionCalls.length > 0) {
|
||||
const functionCall = functionCalls[0] // Get the first function call
|
||||
toolName = functionCall.name ?? ''
|
||||
toolParamsStr = JSON.stringify(functionCall.args ?? {})
|
||||
toolId = functionCall.id ?? ''
|
||||
}
|
||||
|
||||
// (do not handle reasoning yet)
|
||||
@@ -765,7 +779,7 @@ const sendGeminiChat = async ({
|
||||
onText({
|
||||
fullText: fullTextSoFar,
|
||||
fullReasoning: fullReasoningSoFar,
|
||||
toolCall: isAToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined,
|
||||
toolCall: isAToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -773,7 +787,7 @@ const sendGeminiChat = async ({
|
||||
if (!fullTextSoFar && !fullReasoningSoFar && !toolName) {
|
||||
onError({ message: 'Void: Response from model was empty.', fullError: null })
|
||||
} else {
|
||||
const toolId = generateUuid() // gemini does not generate tool IDs. Generate one
|
||||
if (!toolId) toolId = generateUuid() // ids are empty, but other providers might expect an id
|
||||
const toolCall = rawToolCallObjOf(toolName, toolParamsStr, toolId)
|
||||
const toolCallObj = toolCall ? { toolCall } : {}
|
||||
onFinalMessage({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, anthropicReasoning: null, ...toolCallObj });
|
||||
|
||||
Reference in New Issue
Block a user