-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
feat: change set support in chat input and chat model #14750
base: master
Are you sure you want to change the base?
Conversation
Very nice, looks really great. I believe this is a very powerful feature! Couple of high level comments/questions:
|
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.
Just a first highlevel look for now
request.response.response.addContent(new MarkdownChatResponseContentImpl( | ||
'I have created a change set for you. You can now review and apply it.' | ||
)); | ||
request.response.complete(); |
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 wonder when we should update the change set in a real scenario. Simple solution would be when a response is there, but if more then one file is changed, it looks too late from a user POV. Unfortunately, the agent currently does not know which functions are called, therefore, we have no good trigger.
} | ||
} | ||
|
||
async reject(): Promise<void> { |
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.
'reject' is rather 'undo' or 'discard', two comments:
- I would rename this action and only show it, if the change has already been applied
- I believe we need something to remove a change from a changeset without applying it.
}; | ||
|
||
@injectable() | ||
export class ChangeSetFileElement implements ChangeSetElement { |
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 am not 100% sure if I like the mixture between behavior and state in this class. It relies heavily on DI and Theia services and seems hard to test. Maybe we should extract the behavior to a separate interface?
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.
Yeah, I'm certainly open to extract e.g. an abstract super class. I considered it when I wrote it, but the only behavior related to changing the generic ChangeElement state is switching the _state
flag on accept and reject. So I thought it might not be worth it. Except for that, all behavior is file specific.
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.
My suggestion would be more to extract the behavior into a separate interface (delegation), I am not 100% sure myself, though.
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.
Is this used somewhere?
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.
Yes, the resource resolver is registered here:
https://github.com/eclipse-theia/theia/pull/14750/files/0817dcbf334ccfe0a430798ffcbce1dbcce9baa9#diff-444a8731310f8ef30095775299ab7f86f4c392bdd312ac603b9b795f9b3759d4R103
This is needed to obtain the suggested content of a file via the Theia URIs resolution, enabling to reuse the DiffUris in Theia.
With this change we enable passing optional context to tool calls. This is then used for tool calls in the context of a chat to pass on the chat request model. This way tool call handlers can retrieve additional information as a context or update the chat model directly.
React.useEffect(() => { | ||
const onChatModelUpdate = (event: ChatChangeEvent) => { | ||
if (ChatChangeEvent.isChangeSetEvent(event)) { | ||
forceUpdate(); |
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.
It seems the forceUpdate
is used to update the UI for changes in the changeSet. A bit more elegant would be to maintain a "view model" abstraction in useState
for the ChangeSetBox
and update that one whenever needed. Then we would not need a forceUpdate
const onResponseUpdate = () => { | ||
setInProgress(ChatRequestModel.isInProgress(lastRequest)); | ||
}; | ||
const listener = lastRequest?.response.onDidChange(onResponseUpdate); | ||
return () => listener?.dispose(); | ||
}, [lastRequest]); |
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.
This was not introduced by your PR but as I am just noticing it: We should never build useEffect
based transformations for state
which is under our control. At best this introduces unnecessary rerenderings and confusing state updates, at worst this ends up in endless rerender cycles.
As lastRequest
is maintained by us, we should perform any side effects we want to see at the same time as lastRrequest
is changed, i.e. by the code calling the setter.
const leftOptions = React.useMemo<Option[]>(() => ( | ||
props.showContext ? [{ | ||
title: 'Attach elements to context', | ||
handler: () => { /* TODO */ }, | ||
className: 'codicon-add' | ||
}] : [] | ||
), [props.showContext]); | ||
|
||
const rightOptions = React.useMemo<Option[]>(() => ( |
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.
At the moment the memos are not worth it. The only thing we save are the construction of the arrays, however the ChatInputOptions
will be rerendered anyway.
Only if we would memo
the whole ChatInputOptions
, then the useMemo
s are needed. Not sure whether all these complications are worth it, I would imagine that the ChatInputOptions
are cheap to render.
* Add the concept of a change set to chat model and input UI * Add an implementation of change set elements for files * Add an agent for testing: @Changeset Other fixes in Chat Input UI: * The inProgress state of the chat input was actually unsafely managed. This change addresses the proper management of the inProgress state. * The positioning, e.g. of the placeholder is now more adaptive. As the change set feature directly relates to another feature (context, work in progress), this change also already prepares for those changes in the chat UI: * Prepare chat input for adding context to requests * Add context in the form of variables to chat model Fixes #14749 Signed-off-by: Jonas Helming <[email protected]>
0817dcb
to
2d6e9f9
Compare
What it does
Other fixes in Chat Input UI:
As the change set feature directly relates to another feature (context, work in progress), this change also already prepares for those changes in the chat UI:
Fixes #14749
How to test
To demonstrate the usage and simplify testing, this PR includes a test agent named
@ChangeSet
. This statically sets a somewhat random change set into the chat model, with one new, one modified, and one deleted file change set element.change-set.mp4
Follow-ups
Next steps include:
ChangeSetFileElement
how it applies and reverts changes, taking the monaco text model into account. I aimed at reusing the functionality to modify the monaco model if an editor is open without saving, and saving to disk directly if no editor is opened. But this should be tested for user experience and consistency.Breaking changes
Attribution
None
Review checklist
Reminder for reviewers