2024-08-26 16:36:17 -07:00
import { contentType , renderElementJSON } from '@vscode/prompt-tsx' ;
2023-11-13 16:30:27 +01:00
import * as vscode from 'vscode' ;
2024-08-26 16:36:17 -07:00
import { CatToolPrompt } from './play' ;
2023-11-13 16:30:27 +01:00
export function activate ( context : vscode.ExtensionContext ) {
2024-08-05 15:34:56 -07:00
context . subscriptions . push ( registerChatTool ( ) ) ;
context . subscriptions . push ( registerChatParticipant ( ) ) ;
}
function registerChatTool() {
2024-08-26 16:36:17 -07:00
vscode . lm . registerTool ( 'chat-sample_catVoice' , {
async invoke ( parameters , token ) {
return {
[ contentType ] : await renderElementJSON ( CatToolPrompt , { } , parameters . tokenOptions , token ) ,
toString() {
return 'Reply in the voice of a cat! Use cat analogies when appropriate.' ;
} ,
} ;
} ,
} ) ;
2024-08-13 15:24:22 -07:00
return vscode . lm . registerTool ( 'chat-sample_tabCount' , {
2024-08-05 15:34:56 -07:00
async invoke ( parameters , token ) {
return {
toString() {
const activeTabCount = vscode . window . tabGroups . activeTabGroup . tabs . length ;
return ` There are ${ activeTabCount } tabs open. ` ;
} ,
} ;
} ,
} ) ;
}
2023-11-13 16:30:27 +01:00
2024-08-05 15:34:56 -07:00
function registerChatParticipant() {
2024-06-06 15:59:02 -07:00
const handler : vscode.ChatRequestHandler = async ( request : vscode.ChatRequest , chatContext : vscode.ChatContext , stream : vscode.ChatResponseStream , token : vscode.CancellationToken ) = > {
const models = await vscode . lm . selectChatModels ( {
vendor : 'copilot' ,
2024-08-05 15:34:56 -07:00
family : 'gpt-4o'
2024-06-06 15:59:02 -07:00
} ) ;
2024-08-05 15:34:56 -07:00
const model = models [ 0 ] ;
2024-08-13 15:24:22 -07:00
stream . markdown ( ` Available tools: ${ vscode . lm . tools . map ( tool = > tool . id ) . join ( ', ' ) } \ n \ n ` ) ;
2024-06-06 15:59:02 -07:00
2024-08-24 17:00:00 -07:00
const allTools = vscode . lm . tools . map ( ( tool ) : vscode . LanguageModelChatTool = > {
2024-08-06 15:44:45 -07:00
return {
2024-08-13 15:24:22 -07:00
name : tool.id ,
description : tool.modelDescription ,
2024-08-06 15:44:45 -07:00
parametersSchema : tool.parametersSchema ? ? { }
} ;
} ) ;
2024-06-06 15:59:02 -07:00
const options : vscode.LanguageModelChatRequestOptions = {
justification : 'Just because!' ,
2024-08-05 15:34:56 -07:00
} ;
2024-06-06 15:59:02 -07:00
2024-06-19 18:44:06 -07:00
const messages = [
vscode . LanguageModelChatMessage . User ( ` There is a selection of tools that may give helpful context to answer the user's query. If you aren't sure which tool is relevant, you can call multiple tools. ` ) ,
2024-08-05 15:34:56 -07:00
vscode . LanguageModelChatMessage . User ( request . prompt ) ,
2024-06-19 18:44:06 -07:00
] ;
2024-08-13 15:24:22 -07:00
const toolReferences = [ . . . request . toolReferences ] ;
2024-06-06 15:59:02 -07:00
const runWithFunctions = async ( ) = > {
2024-08-13 15:24:22 -07:00
const requestedTool = toolReferences . shift ( ) ;
2024-08-06 15:44:45 -07:00
if ( requestedTool ) {
2024-08-13 15:24:22 -07:00
options . toolChoice = requestedTool . id ;
options . tools = allTools . filter ( tool = > tool . name === requestedTool . id ) ;
2024-08-06 15:44:45 -07:00
} else {
options . toolChoice = undefined ;
options . tools = allTools ;
}
2024-06-06 15:59:02 -07:00
let didReceiveFunctionUse = false ;
2024-08-05 15:34:56 -07:00
const response = await model . sendRequest ( messages , options , token ) ;
2024-05-27 14:09:11 +02:00
2024-06-06 15:59:02 -07:00
for await ( const part of response . stream ) {
if ( part instanceof vscode . LanguageModelChatResponseTextPart ) {
2024-08-05 15:34:56 -07:00
stream . markdown ( part . value ) ;
2024-08-24 17:00:00 -07:00
} else if ( part instanceof vscode . LanguageModelChatResponseToolCallPart ) {
2024-08-13 15:24:22 -07:00
const tool = vscode . lm . tools . find ( tool = > tool . id === part . name ) ;
2024-06-06 15:59:02 -07:00
if ( ! tool ) {
// BAD tool choice?
continue ;
}
2024-08-05 15:34:56 -07:00
let parameters : any ;
try {
parameters = JSON . parse ( part . parameters ) ;
} catch ( err ) {
throw new Error ( ` Got invalid tool use parameters: " ${ part . parameters } ". ( ${ ( err as Error ) . message } ) ` ) ;
}
2024-08-13 15:24:22 -07:00
stream . progress ( ` Calling tool: ${ tool . id } with ${ part . parameters } ` ) ;
2024-08-24 17:00:00 -07:00
const result = await vscode . lm . invokeTool ( tool . id , { parameters : JSON.parse ( part . parameters ) } , token ) ;
2024-06-06 15:59:02 -07:00
2024-08-24 17:00:00 -07:00
let assistantMsg = vscode . LanguageModelChatMessage . Assistant ( '' ) ;
assistantMsg . content2 = [ new vscode . LanguageModelChatResponseToolCallPart ( tool . id , part . toolCallId , part . parameters ) ] ;
messages . push ( assistantMsg ) ;
2024-06-06 15:59:02 -07:00
// NOTE that the result of calling a function is a special content type of a USER-message
let message = vscode . LanguageModelChatMessage . User ( '' ) ;
2024-08-24 17:00:00 -07:00
message . content2 = [ new vscode . LanguageModelChatMessageToolResultPart ( part . toolCallId , result . toString ( ) ) ] ;
2024-08-05 15:34:56 -07:00
messages . push ( message ) ;
2024-06-06 15:59:02 -07:00
// IMPORTANT
// IMPORTANT working around CAPI always wanting to end with a `User`-message
// IMPORTANT
2024-08-13 15:24:22 -07:00
messages . push ( vscode . LanguageModelChatMessage . User ( ` Above is the result of calling the function ${ tool . id } . The user cannot see this result, so you should explain it to the user if referencing it in your answer. ` ) ) ;
2024-06-06 15:59:02 -07:00
didReceiveFunctionUse = true ;
2024-05-27 14:09:11 +02:00
}
2024-02-11 23:05:52 -03:00
}
2024-06-06 15:59:02 -07:00
if ( didReceiveFunctionUse ) {
// RE-enter
return runWithFunctions ( ) ;
}
} ;
2024-08-05 15:34:56 -07:00
await runWithFunctions ( ) ;
2023-11-13 16:30:27 +01:00
} ;
2024-02-11 23:05:52 -03:00
2024-08-05 15:34:56 -07:00
const toolUser = vscode . chat . createChatParticipant ( 'chat-sample.tools' , handler ) ;
2024-06-19 18:44:06 -07:00
toolUser . iconPath = new vscode . ThemeIcon ( 'tools' ) ;
2024-08-05 15:34:56 -07:00
return toolUser ;
2023-11-13 16:30:27 +01:00
}
2024-02-23 13:09:10 +01:00
2023-11-13 16:30:27 +01:00
export function deactivate() { }