Skip to content

Conversation

@suluyana
Copy link
Collaborator

@suluyana suluyana commented Jan 26, 2026

  1. websocket连接终止优化;后端 卡死、退出、结束,前端不再仍processing
  2. 增加retry按钮,当任务失败(尤其是因为api限流等情况失败)时,可直接retry继续运行
  3. 本地文件隔离:按session_id隔离,旧任务不再被新任务覆盖
  4. 对话中文件预览增加流式传输,支持图片、视频预览
  5. 生成的视频支持在对话中显示
  6. 文件列表支持显示
  7. 优化后端output读取,避免因超长output失败
image image image image image

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @suluyana, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求主要聚焦于提升Web UI的用户体验和后端代理的健壮性。通过优化WebSocket连接处理,前端能够更准确地反映后端状态,避免假性“处理中”。新增的任务重试和会话文件隔离功能,极大地提高了开发效率和数据安全性。此外,对话中对多媒体文件的直接预览和文件列表的显示,使得用户能够更直观地与代理交互并查看生成结果。

Highlights

  • WebSocket连接优化: 优化了WebSocket连接的终止逻辑,确保后端进程卡死、退出或结束时,前端不再显示“处理中”状态,提升了用户体验。
  • 任务重试功能: 为失败的任务增加了“重试”按钮,特别是针对API限流等原因导致的失败,用户可以直接重试,无需重新输入指令。
  • 本地文件隔离: 实现了按session_id隔离本地文件,避免了新任务覆盖旧任务生成的文件,确保了不同会话之间的数据独立性。
  • 对话中文件预览增强: 在对话中增加了文件预览的流式传输支持,现在可以直接预览图片、视频和音频文件,并支持在对话中显示生成的视频。
  • 文件列表功能: 前端现在支持显示文件列表,并提供了更灵活的文件路径解析和安全检查机制。
  • 后端输出读取健壮性: 重构了后端agent_runner的输出读取逻辑,采用分块读取而非逐行读取,解决了readline可能导致的阻塞问题,提高了稳定性。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

此拉取请求引入了多项重要改进,显著提升了WebUI的稳定性、可用性和功能性。主要亮点包括:

  1. 会话专用文件隔离:本地文件现在按会话ID隔离,有效避免了多用户环境下的文件冲突。
  2. 健壮的WebSocket处理:前端现在能更优雅地处理意外的WebSocket断开连接和非JSON消息,防止UI卡在“处理中”状态。
  3. 对话中的媒体预览:UI现在支持在对话视图和文件输出对话框中直接流式传输和显示图像及视频文件。
  4. 重试机制:为用户消息添加了重试按钮,方便用户重新运行失败的任务。
  5. 改进的错误处理和日志记录:后端日志记录得到增强,前端错误状态管理更加一致。

总的来说,这些变更对用户体验和系统可靠性都有积极影响。


resolved_root = os.path.normpath(os.path.abspath(resolved_root))

# TODO: Security check: root must be within allowed roots
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

此处的 TODO: Security check: root must be within allowed roots 指示了一个重要的安全漏洞。未对 resolved_root 进行充分的安全检查可能会导致路径遍历漏洞,允许用户访问或修改系统上的任意文件。这应该被优先解决。


full_path = os.path.normpath(full_path)

# TODO: security path check
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

此处的 TODO: security path check 指示了一个重要的安全漏洞。未对 full_path 进行充分的安全检查可能会导致路径遍历漏洞,允许用户访问或修改系统上的任意文件。这应该被优先解决。


# Normalize path
full_path = os.path.normpath(full_path)
# TODO: Security: file must be within root_dir_abs
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

此处的 TODO: Security: file must be within root_dir_abs 指示了一个重要的安全漏洞。未对 full_path 进行充分的安全检查可能会导致路径遍历漏洞,允许用户访问或修改系统上的任意文件。这应该被优先解决。

child = os.path.normpath(os.path.abspath(child))
return child == parent or child.startswith(parent + os.sep)

# TODO: security dir check
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

此处的 TODO: security dir check 指示了一个重要的安全漏洞。未对 resolved 路径进行充分的安全检查可能会导致路径遍历漏洞,允许用户访问或修改系统上的任意文件。这应该被优先解决。

Comment on lines 245 to +277
while self.is_running and self.process and self.process.stdout:
line = await self.process.stdout.readline()
if not line:
chunk = await self.process.stdout.read(CHUNK_SIZE)
if not chunk:
print('[Runner] No more output, breaking...')
break

text = line.decode('utf-8', errors='replace').rstrip()
print(f'[Runner] Output: {text[:200]}'
if len(text) > 200 else f'[Runner] Output: {text}')
buf.extend(chunk)

# Split out complete lines (delimited by '\n').
while True:
nl = buf.find(b'\n')
if nl == -1:
# No newline but the buffer is too large:
# force-flush it as a "line" to _process_line to avoid unbounded growth.
if len(buf) >= MAX_LINE_BUFFER:
part = bytes(buf[:MAX_LINE_BUFFER])
del buf[:MAX_LINE_BUFFER]
text = part.decode(
'utf-8', errors='replace').rstrip()
print(f'[Runner] Output(partial): {text[:200]}'
if len(text) > 200 else
f'[Runner] Output(partial): {text}')
await self._process_line(text)
break

line_bytes = bytes(buf[:nl + 1])
del buf[:nl + 1]

text = line_bytes.decode(
'utf-8', errors='replace').rstrip()
print(f'[Runner] Output: {text[:200]}'
if len(text) > 200 else f'[Runner] Output: {text}')
await self._process_line(text)
Copy link
Contributor

Choose a reason for hiding this comment

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

high

readline() 替换为 read(CHUNK_SIZE) 并手动处理行分割,显著提高了处理子进程输出的鲁棒性。这可以避免因长行或不带换行符的输出导致的潜在阻塞问题,提升了系统的可靠性。

Comment on lines +87 to +89
print(
f'project_discovery.discover_projects(): {project_discovery.discover_projects()}'
)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

此处的 print 语句看起来是调试信息。在生产环境中,应将其移除或替换为适当的日志记录,以避免不必要的输出。

        # logger.debug(f'project_discovery.discover_projects(): {project_discovery.discover_projects()}')

Comment on lines +212 to +224
try {
const data = JSON.parse(event.data);
handleWebSocketMessage(data);
} catch (e) {
console.error('[WS] Non-JSON message:', event.data, e);

// Fallback: stop the loading state to avoid being stuck in "Processing".
endRunningState('error', typeof event.data === 'string'
? event.data
: 'Agent failed with non-JSON output');

// Don't throw again to avoid a blank screen.
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

socket.onmessage 中添加 try-catch 块,以处理非JSON消息,这使得WebSocket处理更加健壮。当接收到格式不正确的消息时,UI不会崩溃,而是能够优雅地处理并显示错误信息。

self.is_running = True
self._stop_requested = False
self.is_running = True
sent_terminal_event = False
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

此变量 sent_terminal_event 的初始化是好的,它确保了在 start 方法的 finally 块中,无论 try 块中发生什么,都能正确地向前端发送终止事件,从而避免UI卡住。

Comment on lines 236 to 241
socket.onerror = (error) => {
console.error('WebSocket error:', error);

// onerror may sometimes be followed by onclose, but adding a fallback here doesn't hurt.
endRunningState('error', 'WebSocket error occurred. Please retry.');
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

socket.onerror 也调用 endRunningState,这与 onclose 的处理方式一致,进一步增强了WebSocket错误处理的健壮性,确保在任何连接错误发生时,UI都能恢复到稳定状态。

Comment on lines +378 to +416
const sendMessage = useCallback((content: string, opts?: { reuseMessageId?: string }) => {
if (!currentSession || !ws || ws.readyState !== WebSocket.OPEN) return;

const clientRequestId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;

// Reuse mode: update the existing message instead of adding a new one.
if (opts?.reuseMessageId) {
setMessages(prev => prev.map(m => {
if (m.id !== opts.reuseMessageId) return m;
return {
...m,
content,
status: 'running',
client_request_id: clientRequestId,
};
}));
} else {
// Default: add a new user message.
setMessages(prev => [...prev, {
id: Date.now().toString(),
role: 'user',
content,
type: 'text',
timestamp: new Date().toISOString(),
status: 'running',
client_request_id: clientRequestId,
}]);
}

// Send to server
ws.send(JSON.stringify({
action: 'start',
query: content,
}));
ws.send(JSON.stringify({
action: 'start',
query: content,
client_request_id: clientRequestId, // If the backend can pass it back unchanged, that would be better.
}));

setIsLoading(true);
}, [currentSession, ws]);
setIsStreaming(false);
setStreamingContent('');
setIsLoading(true);
}, [currentSession, ws]);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

sendMessage 函数的重构以支持 reuseMessageId 参数,是实现重试机制的关键。它允许在重试时更新现有消息而不是创建新消息,同时生成 clientRequestId 并更新消息状态,提供了更灵活和用户友好的交互。

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.

1 participant