diff --git a/demo/src/features/ai.ts b/demo/src/features/ai.ts index 50fb1234..254a530e 100644 --- a/demo/src/features/ai.ts +++ b/demo/src/features/ai.ts @@ -1,4 +1,5 @@ import { ExtensionHostKind, registerExtension } from '@codingame/monaco-vscode-api/extensions' +import * as vscode from 'vscode' const { getApi } = registerExtension( { @@ -9,14 +10,83 @@ const { getApi } = registerExtension( vscode: '*' }, contributes: { + configuration: { + title: 'Codingame AI Demo', + properties: { + codingameAICompletionsEnabled: { + type: 'object', + scope: 'window', + default: { + '*': true, + plaintext: false, + markdown: false, + scminput: false + }, + additionalProperties: { + type: 'boolean' + }, + markdownDescription: + 'Enable or disable auto triggering of Copilot completions for specified [languages](https://code.visualstudio.com/docs/languages/identifiers). You can still trigger suggestions manually using `Alt + \\`' + }, + codingameNextEditSuggestionsEnabled: { + type: 'boolean', + default: true, + tags: ['nextEditSuggestions', 'onExp'], + scope: 'language-overridable' + } + } + }, commands: [ { command: 'aiSuggestedCommand', title: 'This is a command suggested by the AI' } + ], + chatParticipants: [ + { + id: 'codingame.aiDemo.participant', + fullName: 'Codingame AI', + name: 'codingame-ai', + isDefault: true, + modes: ['agent'] + } + ], + languageModelChatProviders: [ + { + vendor: 'coddingame.aiDemo.modelProvider', + displayName: 'Codingame provider' + } + ], + languageModelTools: [ + { + name: 'codingame-tool', + toolReferenceName: 'codingame-tool', + displayName: 'Codingame tool', + userDescription: 'A tool that multiply a number by two', + modelDescription: 'Use this tool to get the result of a multiplication by two', + canBeReferencedInPrompt: true, + inputSchema: { + type: 'object', + properties: { + value: { + type: 'number' + } + }, + required: ['value'], + additionalProperties: false + } + } ] }, - enabledApiProposals: ['aiRelatedInformation'] + enabledApiProposals: [ + 'aiRelatedInformation', + 'mappedEditsProvider', + 'chatSessionsProvider', + 'defaultChatParticipant', + 'chatParticipantAdditions', + 'chatParticipantPrivate', + 'languageModelThinkingPart' + ] }, ExtensionHostKind.LocalProcess, { @@ -24,22 +94,149 @@ const { getApi } = registerExtension( } ) -void getApi().then(async (vscode) => { - vscode.commands.registerCommand('aiSuggestedCommand', () => { - void vscode.window.showInformationMessage('Hello', { +void getApi().then(async (vscodeApi) => { + vscodeApi.commands.registerCommand('aiSuggestedCommand', () => { + void vscodeApi.window.showInformationMessage('Hello', { detail: 'You just run the AI suggested command', modal: true }) }) - vscode.ai.registerRelatedInformationProvider(vscode.RelatedInformationType.CommandInformation, { - provideRelatedInformation() { + vscodeApi.ai.registerRelatedInformationProvider( + vscodeApi.RelatedInformationType.CommandInformation, + { + provideRelatedInformation() { + return [ + { + type: vscode.RelatedInformationType.CommandInformation, + command: 'aiSuggestedCommand', + weight: 9999 + } + ] + } + } + ) + + interface CodingameToolParameters { + value: number + } + + class CodingameTool implements vscode.LanguageModelTool { + async invoke( + options: vscode.LanguageModelToolInvocationPrepareOptions + ) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart('The result is: ' + options.input.value * 2) + ]) + } + + async prepareInvocation( + options: vscode.LanguageModelToolInvocationPrepareOptions + ): Promise { + const confirmationMessages = { + title: vscode.l10n.t('Use Codingame tool'), + message: new vscode.MarkdownString( + 'AI wants to get the double of `' + options.input.value + '`' + ) + } + + return { + invocationMessage: new vscode.MarkdownString( + 'Doubling the value `' + options.input.value + '`' + ), + confirmationMessages + } + } + } + + vscodeApi.lm.registerTool('codingame-tool', new CodingameTool()) + + const _onDidChangeLanguageModelChatInformation = new vscodeApi.EventEmitter() + + vscodeApi.lm.registerLanguageModelChatProvider('coddingame.aiDemo.modelProvider', { + provideLanguageModelChatInformation() { return [ { - type: vscode.RelatedInformationType.CommandInformation, - command: 'aiSuggestedCommand', - weight: 9999 + id: 'codingame', + capabilities: { + toolCalling: true + }, + family: 'codingame', + maxInputTokens: 1000000, + maxOutputTokens: 1000000, + name: 'Codingame', + version: '1.0.0', + isDefault: true, + isUserSelectable: true } ] - } + }, + async provideTokenCount() { + return 0 + }, + async provideLanguageModelChatResponse( + _model, + _messages, + _options, + progress: vscode.Progress + ) { + progress.report(new vscode.LanguageModelThinkingPart('Think', 'thinkId')) + await new Promise((resolve) => setTimeout(resolve, 300)) + progress.report(new vscode.LanguageModelThinkingPart('ing...', 'thinkId')) + await new Promise((resolve) => setTimeout(resolve, 1000)) + progress.report( + new vscode.LanguageModelToolCallPart('callId', 'codingame-tool', { value: 21 }) + ) + await new Promise((resole) => setTimeout(resole, 2000)) + progress.report(new vscode.LanguageModelTextPart('Tool')) + await new Promise((resole) => setTimeout(resole, 300)) + progress.report(new vscode.LanguageModelTextPart(' called\n')) + }, + onDidChangeLanguageModelChatInformation: _onDidChangeLanguageModelChatInformation.event }) + + _onDidChangeLanguageModelChatInformation.fire() + + vscodeApi.chat.createChatParticipant( + 'codingame.aiDemo.participant', + async ( + request: vscode.ChatRequest, + _context: vscode.ChatContext, + response: vscode.ChatResponseStream + ) => { + const modelResponse = await request.model.sendRequest([ + vscodeApi.LanguageModelChatMessage.User(request.prompt) + ]) + for await (const part of modelResponse.stream) { + if (part instanceof vscode.LanguageModelTextPart) { + response.markdown(part.value) + } else if (part instanceof vscode.LanguageModelThinkingPart) { + response.thinkingProgress({ + id: part.id, + text: part.value, + metadata: part.metadata + }) + } else if (part instanceof vscode.LanguageModelToolCallPart) { + const res = await vscode.lm.invokeTool(part.name, { + toolInvocationToken: request.toolInvocationToken, + input: part.input + }) + let toolResult = '' + for (const toolPart of res.content) { + if (toolPart instanceof vscode.LanguageModelTextPart) { + toolResult += toolPart.value + } + } + response.markdown('Tool result: `' + toolResult + '`\n') + } + } + + const firstFile = request.references[0]?.value + if (firstFile != null && firstFile instanceof vscodeApi.Uri) { + response.textEdit( + request.references[0]!.value as vscode.Uri, + vscodeApi.TextEdit.replace(new vscodeApi.Range(5, 0, 7, 0), 'Hello world') + ) + } + } + ) }) diff --git a/demo/src/loader.ts b/demo/src/loader.ts index 28f8c1fe..6d033895 100644 --- a/demo/src/loader.ts +++ b/demo/src/loader.ts @@ -58,6 +58,38 @@ if (locale != null) { const mode = searchParams.get('mode') const sandboxed = searchParams.has('sandboxed') +declare global { + interface Window { + _VSCODE_PRODUCT_JSON: object + } +} + +window._VSCODE_PRODUCT_JSON = { + defaultChatAgent: { + chatExtensionId: 'codingame.aiDemo', + extensionId: '', + completionsEnablementSetting: 'codingameAICompletionsEnabled', + nextEditSuggestionsSetting: 'codingameNextEditSuggestionsEnabled', + provider: { + default: { + id: 'codingame', + name: 'Codingame' + }, + apple: { + id: 'codingame', + name: 'Codingame' + }, + enterprise: { + id: 'codingame', + name: 'Codingame' + }, + google: { + id: 'codingame', + name: 'Codingame' + } + } + } +} ;(async () => { if (sandboxed) { window.vscodeContainer = await new Promise((resolve) => { diff --git a/demo/src/setup.common.ts b/demo/src/setup.common.ts index 31e5098b..5d11ab17 100644 --- a/demo/src/setup.common.ts +++ b/demo/src/setup.common.ts @@ -52,7 +52,9 @@ import getWorkspaceTrustOverride from '@codingame/monaco-vscode-workspace-trust- import getLogServiceOverride from '@codingame/monaco-vscode-log-service-override' import getWorkingCopyServiceOverride from '@codingame/monaco-vscode-working-copy-service-override' import getTestingServiceOverride from '@codingame/monaco-vscode-testing-service-override' -import getChatServiceOverride from '@codingame/monaco-vscode-chat-service-override' +import getChatServiceOverride, { + ChatEntitlement +} from '@codingame/monaco-vscode-chat-service-override' import getNotebookServiceOverride from '@codingame/monaco-vscode-notebook-service-override' import getWelcomeServiceOverride from '@codingame/monaco-vscode-welcome-service-override' import getWalkThroughServiceOverride from '@codingame/monaco-vscode-walkthrough-service-override' @@ -443,7 +445,21 @@ export const commonServices: IEditorOverrideServices = { ...getWorkingCopyServiceOverride(), ...getScmServiceOverride(), ...getTestingServiceOverride(), - ...getChatServiceOverride(), + ...getChatServiceOverride({ + customEntitlement: { + anonymous: false, + entitlement: ChatEntitlement.Enterprise, + previewFeaturesDisabled: false, + quotas: {}, + sentiment: { + installed: true, + hidden: false, + disabled: false, + untrusted: false, + registered: false + } + } + }), ...getNotebookServiceOverride(), ...getWelcomeServiceOverride(), ...getWalkThroughServiceOverride(), diff --git a/package-lock.json b/package-lock.json index 048b07fd..f65245e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -182,6 +182,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -3345,6 +3346,7 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -4274,6 +4276,7 @@ "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4387,6 +4390,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -4863,6 +4867,7 @@ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.168.tgz", "integrity": "sha512-emtXKWZmyOZhcEg6StZ3qFU6M++FM506+2V/E//iqMitCDFfJAGJNJYUS5o0/PRN0MaIKo1ladXhfnozAKaGTA==", "license": "MIT", + "peer": true, "workspaces": [ "addons/*" ] @@ -4879,6 +4884,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5209,6 +5215,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5817,6 +5824,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -6354,6 +6362,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8352,6 +8361,7 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10802,6 +10812,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11501,6 +11512,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -12097,6 +12109,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "devOptional": true, + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -12246,6 +12259,7 @@ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.3.tgz", "integrity": "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==", "dev": true, + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/error": "^4.0.0", @@ -13172,7 +13186,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsx": { "version": "4.21.0", @@ -13250,6 +13265,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/service-override/chat.ts b/src/service-override/chat.ts index 14bd2dd2..2b7d99cf 100644 --- a/src/service-override/chat.ts +++ b/src/service-override/chat.ts @@ -41,7 +41,12 @@ import { ILanguageModelIgnoredFilesService } from 'vs/workbench/contrib/chat/com import { IChatMarkdownAnchorService } from 'vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownAnchorService.service' import { ChatMarkdownAnchorService } from 'vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMarkdownAnchorService' import { ChatEditingService } from 'vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl' -import { ChatEntitlementService } from 'vs/workbench/services/chat/common/chatEntitlementService' +import { + ChatEntitlement, + ChatEntitlementService, + type IChatSentiment, + type IQuotas +} from 'vs/workbench/services/chat/common/chatEntitlementService' import { PromptsService } from 'vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl' import { IChatEntitlementService } from 'vs/workbench/services/chat/common/chatEntitlementService.service' import { IPromptsService } from 'vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.service' @@ -114,12 +119,52 @@ import { IChatDebugService } from 'vs/workbench/contrib/chat/common/chatDebugSer import { ChatDebugServiceImpl } from 'vs/workbench/contrib/chat/common/chatDebugServiceImpl' import { IChatResponseResourceFileSystemProvider } from 'vs/workbench/contrib/chat/common/widget/chatResponseResourceFileSystemProvider.service' import { ChatResponseResourceFileSystemProvider } from 'vs/workbench/contrib/chat/common/widget/chatResponseResourceFileSystemProvider' +import { Event } from 'vs/base/common/event' +import { constObservable } from 'vs/base/common/observable' import 'vs/workbench/contrib/chat/browser/chat.contribution' import 'vs/workbench/contrib/terminal/terminal.chat.contribution' import 'vs/workbench/contrib/inlineChat/browser/inlineChat.contribution' import 'vs/workbench/contrib/remoteCodingAgents/browser/remoteCodingAgents.contribution' -export default function getServiceOverride(): IEditorOverrideServices { +export interface CustomEntitlement { + entitlement: ChatEntitlement + sentiment: IChatSentiment + anonymous: boolean + quotas: IQuotas + previewFeaturesDisabled: boolean +} + +class CustomEntitlementService implements IChatEntitlementService { + constructor(private customEntitlement: CustomEntitlement) {} + _serviceBrand: undefined + onDidChangeEntitlement = Event.None + entitlement = this.customEntitlement.entitlement + entitlementObs = constObservable(this.entitlement) + previewFeaturesDisabled = this.customEntitlement.previewFeaturesDisabled + organisations = undefined + isInternal = true + sku = undefined + copilotTrackingId = undefined + onDidChangeQuotaExceeded = Event.None + onDidChangeQuotaRemaining = Event.None + quotas = this.customEntitlement.quotas + onDidChangeSentiment = Event.None + sentiment = this.customEntitlement.sentiment + sentimentObs = constObservable(this.sentiment) + onDidChangeAnonymous = Event.None + anonymous = this.customEntitlement.anonymous + anonymousObs = constObservable(this.anonymous) + markAnonymousRateLimited(): void {} + async update(): Promise {} +} + +export interface ChatServiceOverrideOptions { + customEntitlement?: CustomEntitlement +} + +export default function getServiceOverride({ + customEntitlement +}: ChatServiceOverrideOptions = {}): IEditorOverrideServices { return { [IChatService.toString()]: new SyncDescriptor(ChatService, [], true), [IChatWidgetService.toString()]: new SyncDescriptor(ChatWidgetService, [], true), @@ -164,7 +209,10 @@ export default function getServiceOverride(): IEditorOverrideServices { [], true ), - [IChatEntitlementService.toString()]: new SyncDescriptor(ChatEntitlementService, [], true), + [IChatEntitlementService.toString()]: + customEntitlement != null + ? new SyncDescriptor(CustomEntitlementService, [customEntitlement], true) + : new SyncDescriptor(ChatEntitlementService, [], true), [IPromptsService.toString()]: new SyncDescriptor(PromptsService, [], true), [IChatStatusItemService.toString()]: new SyncDescriptor(ChatStatusItemService, [], true), [IChatContextPickService.toString()]: new SyncDescriptor(ChatContextPickService, [], true), @@ -257,3 +305,6 @@ export default function getServiceOverride(): IEditorOverrideServices { ) } } + +export { ChatEntitlement } +export type { IChatSentiment, IQuotas } diff --git a/vscode-patches/0012-fix-improve-extension-contribution-types.patch b/vscode-patches/0012-fix-improve-extension-contribution-types.patch index 0165f656..51705199 100644 --- a/vscode-patches/0012-fix-improve-extension-contribution-types.patch +++ b/vscode-patches/0012-fix-improve-extension-contribution-types.patch @@ -4,14 +4,14 @@ Date: Mon, 11 Mar 2024 17:38:30 +0100 Subject: [PATCH] fix: improve extension contribution types --- - .../platform/extensions/common/extensions.ts | 1438 ++++++++++++++++- + .../platform/extensions/common/extensions.ts | 1448 ++++++++++++++++- .../services/search/common/queryBuilder.ts | 8 +- .../themes/common/colorExtensionPoint.ts | 2 +- .../tokenClassificationExtensionPoint.ts | 1 + - 4 files changed, 1379 insertions(+), 70 deletions(-) + 4 files changed, 1389 insertions(+), 70 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts -index 3e5b309b826..ea81acadcb6 100644 +index 3e5b309b826..6e099d27098 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -3,6 +3,7 @@ @@ -1377,9 +1377,33 @@ index 3e5b309b826..ea81acadcb6 100644 } export interface IChatParticipantContribution { -@@ -207,31 +1365,106 @@ export interface IChatFileContribution { +@@ -176,6 +1334,7 @@ export interface IChatParticipantContribution { + description?: string; + isDefault?: boolean; + commands?: { name: string }[]; ++ modes: string[] } + export interface IToolContribution { +@@ -183,6 +1342,9 @@ export interface IToolContribution { + displayName: string; + modelDescription: string; + userDescription?: string; ++ toolReferenceName?: string; ++ canBeReferencedInPrompt?: boolean ++ inputSchema?: object + } + + export interface IToolSetContribution { +@@ -206,32 +1368,112 @@ export interface IChatFileContribution { + readonly when?: string; + } + ++export interface ILanguageModelChatProvider { ++ vendor: string ++ displayName?: string ++} ++ export interface IExtensionContributions { - commands?: ICommand[]; - configuration?: any; @@ -1504,11 +1528,12 @@ index 3e5b309b826..ea81acadcb6 100644 readonly chatParticipants?: ReadonlyArray; readonly chatPromptFiles?: ReadonlyArray; readonly chatInstructions?: ReadonlyArray; -@@ -240,6 +1473,81 @@ export interface IExtensionContributions { +@@ -240,6 +1482,82 @@ export interface IExtensionContributions { readonly languageModelTools?: ReadonlyArray; readonly languageModelToolSets?: ReadonlyArray; readonly mcpServerDefinitionProviders?: ReadonlyArray; + readonly modelContextServerCollections?: ReadonlyArray; ++ readonly languageModelChatProviders?: ReadonlyArray; + + + /**