-
Notifications
You must be signed in to change notification settings - Fork 10
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
Add export action #1096
Add export action #1096
Conversation
a7e31c9
to
51414fb
Compare
if (result != null) { | ||
onSuccess.invoke(result) | ||
} else { | ||
throw Error("Request timed out") // todo: handle it |
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.
@pkukielka, this is a scenario when we time out. Do you have any suggestions how we should handle that? 🙏
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.
We are timing out because of .completeOnTimeout(null, 15, TimeUnit.SECONDS)
or something else? I do not think we should have restrictive timeout there. If we are concerned about some arbitrary big number of chat being exported (e.g. > 1000) we can check that at the very start and notify user that e.g. only last 1000 chats will be exported.
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 have not encountered the timeout. I can imagine that someone can lose their connection during the export.
If we are considering some constraints we may need to find some more sophisticated way to determine the limit. One can have a single chat with 2000 messages while the other can have 1000 chats with only two messages each (human question, assistant response).
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 just tried with a chat that has over 5000 messages (the number of assistant messages) - it fails 🔴
2024-03-21 16:42:32,131 [ 342766] WARN - #c.i.d.PerformanceWatcherImpl - UI was frozen for 5191ms, details saved to /Users/mkondratek/IdeaProjects/jetbrains/build/idea-sandbox/system/log/threadDumps-freeze-20240321-164231-IC-221.5080.210-5sec
java.lang.Throwable: Invocation timed out (1sec)
at java.desktop/sun.lwawt.macosx.LWCToolkit.invokeAndWait(LWCToolkit.java:818)
at java.desktop/sun.lwawt.macosx.LWCToolkit.invokeAndWait(LWCToolkit.java:664)
at java.desktop/sun.lwawt.macosx.CAccessibility.invokeAndWait(CAccessibility.java:114)
at java.desktop/sun.lwawt.macosx.CAccessibility.invokeAndWait(CAccessibility.java:108)
at java.desktop/sun.lwawt.macosx.CAccessibility.getFocusOwner(CAccessibility.java:568)
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Java heap space
java.lang.Throwable: Invocation timed out (1sec)
at java.desktop/sun.lwawt.macosx.LWCToolkit.invokeAndWait(LWCToolkit.java:818)
at java.desktop/sun.lwawt.macosx.LWCToolkit.invokeAndWait(LWCToolkit.java:664)
at java.desktop/sun.lwawt.macosx.CAccessibility.invokeAndWait(CAccessibility.java:114)
at java.desktop/sun.lwawt.macosx.CAccessibility.invokeAndWait(CAccessibility.java:108)
at java.desktop/sun.lwawt.macosx.CAccessibility.getFocusOwner(CAccessibility.java:568)
Exception in thread "HttpClient-1-SelectorManager" java.lang.OutOfMemoryError: Java heap space
I tried with ~1500 messages (the number of assistant messages) - it works (I was able to export) but IntelliJ complains about running low on memory.
I tried with 1000 messages (the number of assistant messages) - it works - no complaints.
1d29096
to
0087a31
Compare
We found one bug btw - our restored chats have (Issue: #1152) |
.filter { it.messages.isNotEmpty() } | ||
|
||
chats.forEachIndexed { index, chatState -> | ||
AgentChatSessionService.getInstance(project).getOrCreateFromState(chatState) |
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.
TBH I do not like that approach although I know it's easiest from the perspective of the code reuse.
We are creating there a lot more than we need, including all UI panels, fetching LLM models, and what not, while all we really want is to call agent.server.chatRestore
. Maybe we should refactor that bit of logic out and reuse (it will be very close to existing AgentChatSession::restoreAgentSession
method, no)?
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.
What's the purpose of reviving the chat here? chat/export
below gives you all the chats in local history for the authed account.
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.
Some account may not be available in the local history in the agent. We need to restore them from our persistency state so the agent is aware of them.
} | ||
} | ||
}, | ||
onFinished = { isRunning = false }) |
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.
isRunning
is accessed from multiple threads? Please mark it as volatile
.
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's always AWT
val json = gson.toJson(chatHistory) | ||
invokeLater { | ||
WriteAction.run<RuntimeException> { | ||
saveTextToFile(json.toByteArray(), result.file) |
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.
Use getVirtualFile
instead of file
, so you don't need to convert it back and forth risking conversion issues.
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.
good point - thanks
I ve just found some designs (#405) 😅 |
.filter { it.messages.isNotEmpty() } | ||
|
||
chats.forEachIndexed { index, chatState -> | ||
AgentChatSessionService.getInstance(project).getOrCreateFromState(chatState) |
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.
What's the purpose of reviving the chat here? chat/export
below gives you all the chats in local history for the authed account.
316b04d
to
2143d6f
Compare
6d78a71
to
6e34e48
Compare
2143d6f
to
ed9df6a
Compare
.filter { it.internalId != null } | ||
.filter { chat -> if (internalId != null) chat.internalId == internalId else true } | ||
|
||
AgentChatSession.createNew(project) { _ -> |
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.
Why do you even need that call? You are not using connectionId generated by it in any way.
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.
whoops - that are some leftover - good point, thanks
} | ||
} | ||
|
||
val result = agent.server.chatExport().completeOnTimeout(null, 15, TimeUnit.SECONDS).get() |
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 do not like that random timeout values in the code.
Maybe we can use one default in the agent itself, or if we need a few tiers just have one object which defines them.
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 do not understand what do you mean by default
. I tried .getNow(null)
but it gives null
.
if (singleChatHistory != null) { | ||
onSuccess.invoke(singleChatHistory) | ||
} else { | ||
// todo: handle error |
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.
TODO in the code
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.
timeout | error |
---|---|
@danielmarquespt, can you pls take a look? 🙏
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.
Note that it is a "tool window" notification (the balloon points to the tool window tab - Cody
, it's less aggressive than the usual notifications)
} | ||
} | ||
}, | ||
onFinished = { isRunning = false }) |
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 do not like passing state variable as general purpose callback.
We already have domain purpose class exactly for what you need there: CancellationToken
This is a minor refactor needed for #1096. It extracts the chat restore logic so it is doable to send the restore request without restoring the full session (with panels, etc). ## Test plan 1. Restore works properly
a3893cb
to
a020e25
Compare
restoreChatSession(agent, chatState) | ||
indicator.fraction = ((index + 1.0) / (chats.size + 1.0)) | ||
|
||
Thread.sleep(3000) |
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.
What is that? :O
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.
whoops, sorry - that was for testing only
private val agent: CodyAgent, | ||
private val internalId: String?, | ||
private val onSuccess: (Any) -> Unit, | ||
private val cancellationToken: CancellationToken |
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.
Thank you for changing it to use CancellationToken
!
|
||
companion object { | ||
val gson: Gson = GsonBuilder().create() | ||
var isRunning = AtomicReference(false) |
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 see what you mean talking about AtomicReference
.
It seems that if you need to check it externally there really is no easier way (although that probably could be just volatile boolean).
52c833a
to
92e9999
Compare
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.
LGTM
Fixes #405.
chatRestore
instead ofcreateFromState
Related PR to the agent: sourcegraph/cody#3487
Test plan