-
Notifications
You must be signed in to change notification settings - Fork 33.2k
chat: add multiple 'initiator' types to provideLanguageModelResponse
#250018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
MCP servers can make chat requests via sampling. This swaps `extensionId` in `provideLanguageModelResponse` with an `initiator` so we can represent these calls properly.
I don't understand this. AFAIK the |
In copilot-chat this is indeed the case. However we lack any way to say that a chat didn't come from an extension, and giving an invalid extension ID will cause copilot-chat to throw. This API change allows for us to express the initator correctly. |
AFAIK that's something we never really used. There is an EXP from last year that serves as a kill switch (in case an extension goes bad) and then there is some CAPI logic with the @connor4312 are you planning on extending that to MCP servers? IMO we should reconsider this and instead of glorifying the initiator we should just make extensionId optional and eventually phase it out. There will also be cases in which the initiator is neither MCP nor an extension but just VS Code itself |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's first agree on this being needed
So, I suggest to pass |
Currently in main I just use the copilot extension ID to make it work, which is a hack. If we don't need this I'm good with making the extension ID optional instead.
Agree, my thought was, if we keep the initiator then we can add an additional type to the union when this case arises.
Yes, MCP supports "sampling" which is basically making an LM request. At the moment, until we get the registry fully in, we don't know the 'identity' of MCP servers and only have their user-defined label. But once we get the registry in we'll know (for registry-based servers) their name in a deterministic way. |
Tho, stdio servers, that I have locally and that I configure via mcp.json, always have a user-defined label, right? |
Yes |
Also, I think we may need/want this because the extension's |
That's only needed because the chat extension itself doesn't use the API to make LM requests. For all other cases we know the initiator of LM requests (based on the scoped API) and this event is something we won't finalize. |
Then let's plan a future with CAPI where
@lramos15 can you check with CAPI folks on this please |
I made some API updates to reflect that, lmk what you think (or feel free to just push changes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given we aim to finalize this API soon, I have done some API nit.
(Tho, I am unsure if the initiator aspect is something we would finalise or not?)
reason: string; | ||
} | ||
|
||
export type LanguageModelRequestInitiator = ExtensionLanguageModelRequestInitiator | McpServerLanguageModelRequestInitiator | InternalLanguageModelRequestInitiator; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the API we don't do kind-discriminated or-types but plain simple classes, like InlineValue
. I think we have two options:
- have different initiators be represented as classes, or
- have one fits-all initiator which has the
kind
-attribute and something generic like identifier
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The one case I was looking at was quickpick items vs separators that have a kind
like we have here. The only reason I didn't do classes is that there's no reason for an extension to instantiate these types, but that could also be done with a protected or private constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yikes that quick pick. tho, it is same, same but different. there is no or-type. just a single type that's different depending on the value of kind. That's something we can have too (basically my 2nd suggestion)
|
||
provideLanguageModelResponse(messages: Array<LanguageModelChatMessage | LanguageModelChatMessage2>, options: LanguageModelChatRequestOptions, extensionId: string, progress: Progress<ChatResponseFragment2>, token: CancellationToken): Thenable<any>; | ||
provideLanguageModelResponse(messages: Array<LanguageModelChatMessage | LanguageModelChatMessage2>, options: LanguageModelChatRequestOptions, initiator: LanguageModelRequestInitiator, progress: Progress<ChatResponseFragment2>, token: CancellationToken): Thenable<any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely out of scope for this PR but the initiator
argument should probably be merged with the option
argument and then be a new, fresh type for the provider-side. So that LanguageModelChatRequestOptions
is reused (in a cyclic API) and so that things are contained nicely
/** | ||
* Represents a large language model that accepts ChatML messages and produces a streaming response | ||
*/ | ||
export interface LanguageModelChatProvider { | ||
|
||
// TODO@API remove or keep proposed? | ||
onDidReceiveLanguageModelResponse2?: Event<{ readonly extensionId: string; readonly participant?: string; readonly tokenCount?: number }>; | ||
onDidReceiveLanguageModelResponse2?: Event<{ readonly initiator: LanguageModelRequestInitiator; readonly participant?: string; readonly tokenCount?: number }>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it follows the beaten path but this needs revisiting. Core should just know when a LM has been used and AFAIK this is only needed because our extension makes requests without going through the API. Maybe @sandy081 can clarify if/how this is still needed
MCP servers can make chat requests via sampling. This swaps
extensionId
in
provideLanguageModelResponse
with aninitiator
so we canrepresent these calls properly.