mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
Add LM chat model provider sample (#1200)
This commit is contained in:
5
chat-model-provider-sample/.gitignore
vendored
Normal file
5
chat-model-provider-sample/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
out
|
||||
dist
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
35
chat-model-provider-sample/.vscode/launch.json
vendored
Normal file
35
chat-model-provider-sample/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js",
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "tasks: watch-tests"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
chat-model-provider-sample/.vscode/settings.json
vendored
Normal file
9
chat-model-provider-sample/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"out": true
|
||||
},
|
||||
"git.branchProtection": [
|
||||
"main"
|
||||
],
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
40
chat-model-provider-sample/.vscode/tasks.json
vendored
Normal file
40
chat-model-provider-sample/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch-tests",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "tasks: watch-tests",
|
||||
"dependsOn": [
|
||||
"npm: watch",
|
||||
"npm: watch-tests"
|
||||
],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
11
chat-model-provider-sample/.vscodeignore
Normal file
11
chat-model-provider-sample/.vscodeignore
Normal file
@ -0,0 +1,11 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
webpack.config.js
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
101
chat-model-provider-sample/README.md
Normal file
101
chat-model-provider-sample/README.md
Normal file
@ -0,0 +1,101 @@
|
||||
# Chat Model Provider Sample
|
||||
|
||||
This VS Code extension demonstrates how to implement a custom chat model provider using the Language Model (LM) API. It serves as a sample implementation for developers who want to integrate their own AI models with VS Code's chat functionality.
|
||||
|
||||
## Features
|
||||
|
||||
This extension provides:
|
||||
|
||||
- **Custom Chat Model Provider**: Implements the `LanguageModelChatProvider2` interface to register custom AI models
|
||||
- **Multiple Model Support**: Demonstrates how to provide multiple models from a single provider
|
||||
- **Sample Models**: Includes two example models:
|
||||
- **Dog Model**: Responds with dog-themed messages ("Woof!")
|
||||
- **Cat Model**: Responds with cat-themed messages ("Meow!")
|
||||
|
||||
## Architecture
|
||||
|
||||
The extension consists of two main components:
|
||||
|
||||
### Extension Activation (`src/extension.ts`)
|
||||
- Registers the sample chat model provider with VS Code's LM API
|
||||
- Uses the vendor ID `"sample"` to identify the provider
|
||||
|
||||
### Chat Model Provider (`src/provider.ts`)
|
||||
- Implements `LanguageModelChatProvider2` interface
|
||||
- Provides model information including capabilities (tool calling, vision support)
|
||||
- Handles chat requests and returns appropriate responses
|
||||
- Includes token counting functionality
|
||||
|
||||
## Model Capabilities
|
||||
|
||||
Each sample model declares the following capabilities:
|
||||
- **Tool Calling**: ✅ Enabled
|
||||
- **Vision**: ✅ Enabled
|
||||
- **Max Input Tokens**: 120,000
|
||||
- **Max Output Tokens**: 8,192
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
- VS Code version 1.103.0 or higher
|
||||
- Node.js and npm installed
|
||||
|
||||
### Installation and Development
|
||||
|
||||
1. Clone this repository
|
||||
2. Navigate to the extension directory:
|
||||
```bash
|
||||
cd chat-model-provider-sample
|
||||
```
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
4. Compile the extension:
|
||||
```bash
|
||||
npm run compile
|
||||
```
|
||||
5. Press `F5` to launch a new Extension Development Host window
|
||||
6. The extension will be active and ready to provide chat models
|
||||
|
||||
### Building and Watching
|
||||
|
||||
- **Build once**: `npm run compile`
|
||||
- **Watch mode**: `npm run watch` (automatically recompiles on file changes)
|
||||
- **Lint code**: `npm run lint`
|
||||
|
||||
## Usage
|
||||
|
||||
Once the extension is active:
|
||||
|
||||
1. Open VS Code's chat interface
|
||||
2. Click the model picker and click manage models
|
||||
3. Select the sample provider
|
||||
4. Check the models based on what you want in the model picker
|
||||
5. Send a request to the model
|
||||
|
||||
## API Usage
|
||||
|
||||
This extension uses the proposed `chatProvider` API. The key components include:
|
||||
|
||||
- `vscode.lm.registerChatModelProvider()` - Registers the provider
|
||||
- `LanguageModelChatProvider2` interface - Defines the provider contract
|
||||
- `LanguageModelChatInformation` - Describes model capabilities
|
||||
- `ChatResponseFragment2` - Handles streaming responses
|
||||
|
||||
## Customization
|
||||
|
||||
To create your own chat model provider:
|
||||
|
||||
1. Modify the `SampleChatModelProvider` class in `src/provider.ts`
|
||||
2. Update the model information in `getChatModelInfo()`
|
||||
3. Implement your custom logic in `provideLanguageModelChatResponse()`
|
||||
4. Adjust the vendor ID and model IDs as needed
|
||||
5. Update the `package.json` with your extension details
|
||||
|
||||
|
||||
## Related
|
||||
|
||||
- [VS Code Extension API](https://code.visualstudio.com/api)
|
||||
- [Language Model API Documentation](https://code.visualstudio.com/api/extension-guides/chat)
|
||||
- [VS Code Extension Samples](https://github.com/Microsoft/vscode-extension-samples)
|
||||
49
chat-model-provider-sample/eslint.config.mjs
Normal file
49
chat-model-provider-sample/eslint.config.mjs
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* ESLint configuration for the project.
|
||||
*
|
||||
* See https://eslint.style and https://typescript-eslint.io for additional linting options.
|
||||
*/
|
||||
// @ts-check
|
||||
import js from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: [
|
||||
'.vscode-test',
|
||||
'out',
|
||||
'**/*.d.ts'
|
||||
]
|
||||
},
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'],
|
||||
},
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
{
|
||||
plugins: {
|
||||
'@stylistic': stylistic
|
||||
},
|
||||
rules: {
|
||||
'curly': 'warn',
|
||||
'@stylistic/semi': ['warn', 'always'],
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'warn',
|
||||
{
|
||||
'selector': 'import',
|
||||
'format': ['camelCase', 'PascalCase']
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
'argsIgnorePattern': '^_'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
1831
chat-model-provider-sample/package-lock.json
generated
Normal file
1831
chat-model-provider-sample/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
chat-model-provider-sample/package.json
Normal file
53
chat-model-provider-sample/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "chat-model-provider-sample",
|
||||
"enabledApiProposals": ["chatProvider"],
|
||||
"publisher": "vscode-samples",
|
||||
"displayName": "Copilot Model Provider Sample",
|
||||
"description": "Sample extension which provides chat models via the LM API.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/vscode-extension-samples"
|
||||
},
|
||||
"version": "0.1.0",
|
||||
"engines": {
|
||||
"vscode": "^1.103.0-20250721"
|
||||
},
|
||||
"categories": [
|
||||
"AI",
|
||||
"Chat"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onStartupFinished"
|
||||
],
|
||||
"contributes": {
|
||||
"languageModels": [
|
||||
{
|
||||
"vendor": "sample",
|
||||
"displayName": "Sample Model Vendor"
|
||||
}
|
||||
]
|
||||
},
|
||||
"main": "./out/extension.js",
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"download-api": "dts dev",
|
||||
"postdownload-api": "dts main",
|
||||
"postinstall": "npm run download-api",
|
||||
"compile": "tsc -p ./",
|
||||
"lint": "eslint",
|
||||
"watch": "tsc -watch -p ./"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@stylistic/eslint-plugin": "^2.9.0",
|
||||
"@types/node": "^22",
|
||||
"@vscode/dts": "^0.4.1",
|
||||
"@types/vscode": "^1.102.0",
|
||||
"eslint": "^9.13.0",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.26.0"
|
||||
}
|
||||
}
|
||||
8
chat-model-provider-sample/src/extension.ts
Normal file
8
chat-model-provider-sample/src/extension.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { SampleChatModelProvider } from './provider';
|
||||
|
||||
export function activate(_: vscode.ExtensionContext) {
|
||||
vscode.lm.registerChatModelProvider('sample', new SampleChatModelProvider());
|
||||
}
|
||||
|
||||
export function deactivate() { }
|
||||
39
chat-model-provider-sample/src/provider.ts
Normal file
39
chat-model-provider-sample/src/provider.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { CancellationToken, ChatResponseFragment2, LanguageModelChatInformation, LanguageModelChatMessage, LanguageModelChatProvider2, LanguageModelChatRequestHandleOptions, LanguageModelTextPart, Progress, ProviderResult } from "vscode";
|
||||
|
||||
function getChatModelInfo(id: string, name: string): LanguageModelChatInformation {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
description: "A sample chat model for demonstration purposes.",
|
||||
family: "sample-family",
|
||||
maxInputTokens: 120000,
|
||||
maxOutputTokens: 8192,
|
||||
version: "1.0.0",
|
||||
capabilities: {
|
||||
toolCalling: true,
|
||||
vision: true,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export class SampleChatModelProvider implements LanguageModelChatProvider2 {
|
||||
prepareLanguageModelChat(_options: { silent: boolean; }, _token: CancellationToken): ProviderResult<LanguageModelChatInformation[]> {
|
||||
return [
|
||||
getChatModelInfo("sample-dog-model", "Dog Model"),
|
||||
getChatModelInfo("sample-cat-model", "Cat Model"),
|
||||
];
|
||||
}
|
||||
async provideLanguageModelChatResponse(model: LanguageModelChatInformation, _messages: Array<LanguageModelChatMessage>, _options: LanguageModelChatRequestHandleOptions, progress: Progress<ChatResponseFragment2>, _token: CancellationToken): Promise<void> {
|
||||
if (model.id === "sample-dog-model") {
|
||||
progress.report({index: 0, part: new LanguageModelTextPart("Woof! This is a dog model response.") });
|
||||
} else if (model.id === "sample-cat-model") {
|
||||
progress.report({index: 0, part: new LanguageModelTextPart("Meow! This is a cat model response.") });
|
||||
} else {
|
||||
progress.report({ index: 0, part: new LanguageModelTextPart("Unknown model.") });
|
||||
}
|
||||
}
|
||||
async provideTokenCount(_model: LanguageModelChatInformation, _text: string | LanguageModelChatMessage, _token: CancellationToken): Promise<number> {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
25
chat-model-provider-sample/tsconfig.json
Normal file
25
chat-model-provider-sample/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "ES2022",
|
||||
"lib": [
|
||||
"ES2022"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true, /* enable all strict type-checking options */
|
||||
"outDir": "out",
|
||||
"skipLibCheck": true /* Skip type checking of declaration files */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"vscode.proposed.chatProvider.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"vscode.d.ts"
|
||||
]
|
||||
}
|
||||
20792
chat-model-provider-sample/vscode.d.ts
vendored
Normal file
20792
chat-model-provider-sample/vscode.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
144
chat-model-provider-sample/vscode.proposed.chatProvider.d.ts
vendored
Normal file
144
chat-model-provider-sample/vscode.proposed.chatProvider.d.ts
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
|
||||
// @API extension ship a d.ts files for their options
|
||||
|
||||
// @API the LanguageModelChatProvider2 is an alternative that combines a source, like ollama etc, with
|
||||
// concrete models. The `provideLanguageModelChatData` would do the discovery and auth dances and later
|
||||
// the model data is passed to the concrete function for making a requested or counting token
|
||||
|
||||
|
||||
// TODO@API name scheme
|
||||
export interface LanguageModelChatRequestHandleOptions {
|
||||
|
||||
// initiator
|
||||
readonly extensionId: string;
|
||||
|
||||
/**
|
||||
* A set of options that control the behavior of the language model. These options are specific to the language model
|
||||
* and need to be looked up in the respective documentation.
|
||||
*/
|
||||
readonly modelOptions: { [name: string]: any };
|
||||
|
||||
/**
|
||||
* An optional list of tools that are available to the language model. These could be registered tools available via
|
||||
* {@link lm.tools}, or private tools that are just implemented within the calling extension.
|
||||
*
|
||||
* If the LLM requests to call one of these tools, it will return a {@link LanguageModelToolCallPart} in
|
||||
* {@link LanguageModelChatResponse.stream}. It's the caller's responsibility to invoke the tool. If it's a tool
|
||||
* registered in {@link lm.tools}, that means calling {@link lm.invokeTool}.
|
||||
*
|
||||
* Then, the tool result can be provided to the LLM by creating an Assistant-type {@link LanguageModelChatMessage} with a
|
||||
* {@link LanguageModelToolCallPart}, followed by a User-type message with a {@link LanguageModelToolResultPart}.
|
||||
*/
|
||||
tools?: LanguageModelChatTool[];
|
||||
|
||||
/**
|
||||
* The tool-selecting mode to use. {@link LanguageModelChatToolMode.Auto} by default.
|
||||
*/
|
||||
toolMode?: LanguageModelChatToolMode;
|
||||
}
|
||||
|
||||
// TODO@API names: LanguageModelChatMetadata, LanguageModelChatItem
|
||||
export interface LanguageModelChatInformation {
|
||||
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Human-readable name of the language model.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* Opaque family-name of the language model. Values might be `gpt-3.5-turbo`, `gpt4`, `phi2`, or `llama`
|
||||
* but they are defined by extensions contributing languages and subject to change.
|
||||
*/
|
||||
readonly family: string;
|
||||
|
||||
/**
|
||||
* An optional, human-readable description of the language model.
|
||||
*/
|
||||
readonly description?: string;
|
||||
|
||||
/**
|
||||
* An optional, human-readable string representing the cost of using the language model.
|
||||
*/
|
||||
readonly cost?: string;
|
||||
|
||||
/**
|
||||
* Opaque version string of the model. This is defined by the extension contributing the language model
|
||||
* and subject to change while the identifier is stable.
|
||||
*/
|
||||
readonly version: string;
|
||||
|
||||
readonly maxInputTokens: number;
|
||||
|
||||
readonly maxOutputTokens: number;
|
||||
|
||||
/**
|
||||
* When present, this gates the use of `requestLanguageModelAccess` behind an authorization flow where
|
||||
* the user must approve of another extension accessing the models contributed by this extension.
|
||||
* Additionally, the extension can provide a label that will be shown in the UI.
|
||||
*/
|
||||
auth?: true | { label: string };
|
||||
|
||||
// TODO@API maybe an enum, LanguageModelChatProviderPickerAvailability?
|
||||
// TODO@API isPreselected proposed
|
||||
readonly isDefault?: boolean;
|
||||
|
||||
// TODO@API nuke
|
||||
readonly isUserSelectable?: boolean;
|
||||
|
||||
readonly capabilities?: {
|
||||
|
||||
// TODO@API have mimeTypes that you support
|
||||
readonly vision?: boolean;
|
||||
|
||||
// TODO@API should be `boolean | number` so extensions can express how many tools they support
|
||||
readonly toolCalling?: boolean | number;
|
||||
|
||||
// TODO@API DO NOT SUPPORT THIS
|
||||
// readonly agentMode?: boolean;
|
||||
|
||||
// TODO@API support prompt TSX style messages, MAYBE leave it out for now
|
||||
readonly promptTsx?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Optional category to group models by in the model picker.
|
||||
* The lower the order, the higher the category appears in the list.
|
||||
* Has no effect if `isUserSelectable` is `false`.
|
||||
* If not specified, the model will appear in the "Other Models" category.
|
||||
*/
|
||||
readonly category?: { label: string; order: number };
|
||||
}
|
||||
|
||||
export interface LanguageModelChatProvider2<T extends LanguageModelChatInformation = LanguageModelChatInformation> {
|
||||
|
||||
// signals a change from the provider to the editor so that prepareLanguageModelChat is called again
|
||||
onDidChange?: Event<void>;
|
||||
|
||||
// NOT cacheable (between reloads)
|
||||
prepareLanguageModelChat(options: { silent: boolean }, token: CancellationToken): ProviderResult<T[]>;
|
||||
|
||||
provideLanguageModelChatResponse(model: T, messages: Array<LanguageModelChatMessage | LanguageModelChatMessage2>, options: LanguageModelChatRequestHandleOptions, progress: Progress<ChatResponseFragment2>, token: CancellationToken): Thenable<any>;
|
||||
|
||||
provideTokenCount(model: T, text: string | LanguageModelChatMessage | LanguageModelChatMessage2, token: CancellationToken): Thenable<number>;
|
||||
}
|
||||
|
||||
export namespace lm {
|
||||
|
||||
export function registerChatModelProvider(vendor: string, provider: LanguageModelChatProvider2): Disposable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ChatResponseFragment2 {
|
||||
index: number;
|
||||
part: LanguageModelTextPart | LanguageModelToolCallPart;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user