Skip to content

Commit c08e407

Browse files
committed
Initial streaming approach
1 parent ee79b01 commit c08e407

File tree

1 file changed

+117
-2
lines changed

1 file changed

+117
-2
lines changed

src/github/copilotRemoteAgent.ts

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,8 +770,8 @@ export class CopilotRemoteAgentManager extends Disposable {
770770
return this.createEmptySession();
771771
}
772772

773-
// Get session logs
774-
const sessionLogs = await this.getSessionLogFromPullRequest(pullRequest);
773+
// Get session logs (including in-progress sessions)
774+
const sessionLogs = await this.getSessionLogFromPullRequest(pullRequest, 0, false);
775775
if (!sessionLogs) {
776776
Logger.warn(`No session logs found for pull request ${pullRequestId}`, CopilotRemoteAgentManager.ID);
777777
return this.createEmptySession();
@@ -780,8 +780,15 @@ export class CopilotRemoteAgentManager extends Disposable {
780780
// Parse logs and create chat history
781781
const history = await this.parseChatHistoryFromLogs(sessionLogs.logs, pullRequest.title);
782782

783+
// Check if the session is in progress and provide activeResponseCallback if needed
784+
const isInProgress = sessionLogs.info.state === 'in_progress';
785+
const activeResponseCallback = isInProgress
786+
? this.createActiveResponseCallback(pullRequest, sessionLogs.info.id)
787+
: undefined;
788+
783789
return {
784790
history,
791+
activeResponseCallback,
785792
requestHandler: undefined // Read-only session
786793
};
787794
} catch (error) {
@@ -797,6 +804,114 @@ export class CopilotRemoteAgentManager extends Disposable {
797804
};
798805
}
799806

807+
private createActiveResponseCallback(pullRequest: PullRequestModel, _sessionId: string): (stream: vscode.ChatResponseStream, token: vscode.CancellationToken) => Thenable<void> {
808+
return async (stream: vscode.ChatResponseStream, token: vscode.CancellationToken) => {
809+
const capi = await this.copilotApi;
810+
if (!capi || token.isCancellationRequested) {
811+
return;
812+
}
813+
814+
let lastLogLength = 0;
815+
let lastProcessedLength = 0;
816+
const pollingInterval = 3000; // 3 seconds
817+
818+
return new Promise<void>((resolve, reject) => {
819+
const pollForUpdates = async (): Promise<void> => {
820+
try {
821+
if (token.isCancellationRequested) {
822+
resolve();
823+
return;
824+
}
825+
826+
// Get latest session logs
827+
const sessionLogs = await this.getSessionLogFromPullRequest(pullRequest, 0, false);
828+
if (!sessionLogs || token.isCancellationRequested) {
829+
resolve();
830+
return;
831+
}
832+
833+
// Check if session is still in progress
834+
if (sessionLogs.info.state !== 'in_progress') {
835+
// Session completed, parse any remaining logs and stream final content
836+
if (sessionLogs.logs.length > lastProcessedLength) {
837+
const newLogContent = sessionLogs.logs.slice(lastProcessedLength);
838+
await this.streamNewLogContent(stream, newLogContent);
839+
}
840+
resolve(); // Resolve the promise when session is complete
841+
return;
842+
}
843+
844+
// Stream new content if logs have grown
845+
if (sessionLogs.logs.length > lastLogLength) {
846+
const newLogContent = sessionLogs.logs.slice(lastProcessedLength);
847+
await this.streamNewLogContent(stream, newLogContent);
848+
lastProcessedLength = sessionLogs.logs.length;
849+
}
850+
851+
lastLogLength = sessionLogs.logs.length;
852+
853+
// Schedule next poll if still in progress and not cancelled
854+
if (!token.isCancellationRequested && sessionLogs.info.state === 'in_progress') {
855+
setTimeout(pollForUpdates, pollingInterval);
856+
} else {
857+
resolve();
858+
}
859+
} catch (error) {
860+
Logger.error(`Error polling for session updates: ${error}`, CopilotRemoteAgentManager.ID);
861+
// Continue polling despite errors
862+
if (!token.isCancellationRequested) {
863+
setTimeout(pollForUpdates, pollingInterval);
864+
} else {
865+
reject(error);
866+
}
867+
}
868+
};
869+
870+
// Start polling
871+
setTimeout(pollForUpdates, pollingInterval);
872+
});
873+
};
874+
}
875+
876+
private async streamNewLogContent(stream: vscode.ChatResponseStream, newLogContent: string): Promise<void> {
877+
try {
878+
if (!newLogContent.trim()) {
879+
return;
880+
}
881+
882+
// Parse the new log content
883+
const logChunks = parseSessionLogs(newLogContent);
884+
885+
for (const chunk of logChunks) {
886+
for (const choice of chunk.choices) {
887+
const delta = choice.delta;
888+
889+
if (delta.role === 'assistant') {
890+
// Stream assistant content
891+
if (delta.content) {
892+
stream.markdown(delta.content);
893+
}
894+
895+
// Handle tool calls
896+
if (delta.tool_calls) {
897+
for (const toolCall of delta.tool_calls) {
898+
const toolDetails = parseToolCallDetails(toolCall, '');
899+
stream.push(new vscode.ChatToolInvocationPart(toolCall.id, toolDetails.toolName));
900+
}
901+
}
902+
}
903+
904+
// Handle finish reasons
905+
if (choice.finish_reason && choice.finish_reason !== 'null') {
906+
Logger.appendLine(`Streaming finish_reason: ${choice.finish_reason}`, CopilotRemoteAgentManager.ID);
907+
}
908+
}
909+
}
910+
} catch (error) {
911+
Logger.error(`Error streaming new log content: ${error}`, CopilotRemoteAgentManager.ID);
912+
}
913+
}
914+
800915
private findPullRequestById(id: number): PullRequestModel | undefined {
801916
for (const folderManager of this.repositoriesManager.folderManagers) {
802917
for (const githubRepo of folderManager.gitHubRepositories) {

0 commit comments

Comments
 (0)