Skip to content
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

Merged
merged 20 commits into from
Mar 28, 2024
Merged

Add export action #1096

merged 20 commits into from
Mar 28, 2024

Conversation

mkondratek
Copy link
Contributor

@mkondratek mkondratek commented Mar 15, 2024

Fixes #405.

Related PR to the agent: sourcegraph/cody#3487

--- ---
image image

Test plan

  1. tbd

@mkondratek mkondratek self-assigned this Mar 15, 2024
@mkondratek mkondratek marked this pull request as draft March 15, 2024 14:19
@mkondratek mkondratek force-pushed the mkondratek/feat/chat-export branch 2 times, most recently from a7e31c9 to 51414fb Compare March 21, 2024 00:32
@mkondratek mkondratek marked this pull request as ready for review March 21, 2024 11:03
if (result != null) {
onSuccess.invoke(result)
} else {
throw Error("Request timed out") // todo: handle it
Copy link
Contributor Author

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? 🙏

Copy link
Contributor

@pkukielka pkukielka Mar 21, 2024

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.

Copy link
Contributor Author

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).

Copy link
Contributor Author

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.

@mkondratek mkondratek force-pushed the mkondratek/feat/chat-export branch 2 times, most recently from 1d29096 to 0087a31 Compare March 21, 2024 11:08
@mkondratek
Copy link
Contributor Author

mkondratek commented Mar 21, 2024

We found one bug btw - our restored chats have lastInteractionTimestamp as the chat id we assign to the chats (e.g. 0c912af5-f2a9-4d60-b8b0-3a75e472a1da). While VS Code generates and export chats with a "proper" timestamp (e.g. Tue, 27 Feb 2024 23:55:50 GMT).

(Issue: #1152)

.filter { it.messages.isNotEmpty() }

chats.forEachIndexed { index, chatState ->
AgentChatSessionService.getInstance(project).getOrCreateFromState(chatState)
Copy link
Contributor

@pkukielka pkukielka Mar 21, 2024

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)?

Copy link
Contributor

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.

Copy link
Contributor Author

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 })
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point - thanks

@mkondratek
Copy link
Contributor Author

I ve just found some designs (#405) 😅

image image

.filter { it.messages.isNotEmpty() }

chats.forEachIndexed { index, chatState ->
AgentChatSessionService.getInstance(project).getOrCreateFromState(chatState)
Copy link
Contributor

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.

@mkondratek mkondratek changed the base branch from main to mkondratek/chore/restore March 25, 2024 23:10
.filter { it.internalId != null }
.filter { chat -> if (internalId != null) chat.internalId == internalId else true }

AgentChatSession.createNew(project) { _ ->
Copy link
Contributor

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.

Copy link
Contributor Author

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()
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO in the code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timeout error
image image

@danielmarquespt, can you pls take a look? 🙏

Copy link
Contributor Author

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 })
Copy link
Contributor

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

Base automatically changed from mkondratek/chore/restore to main March 26, 2024 15:21
mkondratek added a commit that referenced this pull request Mar 26, 2024
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
@mkondratek mkondratek force-pushed the mkondratek/feat/chat-export branch 2 times, most recently from a3893cb to a020e25 Compare March 27, 2024 14:34
restoreChatSession(agent, chatState)
indicator.fraction = ((index + 1.0) / (chats.size + 1.0))

Thread.sleep(3000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is that? :O

Copy link
Contributor Author

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
Copy link
Contributor

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)
Copy link
Contributor

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).

@mkondratek
Copy link
Contributor Author

tested on windows - works

image

Copy link
Contributor

@pkukielka pkukielka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mkondratek mkondratek enabled auto-merge (squash) March 28, 2024 13:29
@mkondratek mkondratek enabled auto-merge (squash) March 28, 2024 13:46
@mkondratek mkondratek merged commit e94ce34 into main Mar 28, 2024
5 checks passed
@mkondratek mkondratek deleted the mkondratek/feat/chat-export branch March 28, 2024 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Export chat history to JSON & CSV
3 participants