-
Notifications
You must be signed in to change notification settings - Fork 446
Feat/webui video #836
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
base: main
Are you sure you want to change the base?
Feat/webui video #836
Conversation
Summary of ChangesHello @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
🧠 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 AssistThe 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
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 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
|
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.
Code Review
此拉取请求引入了多项重要改进,显著提升了WebUI的稳定性、可用性和功能性。主要亮点包括:
- 会话专用文件隔离:本地文件现在按会话ID隔离,有效避免了多用户环境下的文件冲突。
- 健壮的WebSocket处理:前端现在能更优雅地处理意外的WebSocket断开连接和非JSON消息,防止UI卡在“处理中”状态。
- 对话中的媒体预览:UI现在支持在对话视图和文件输出对话框中直接流式传输和显示图像及视频文件。
- 重试机制:为用户消息添加了重试按钮,方便用户重新运行失败的任务。
- 改进的错误处理和日志记录:后端日志记录得到增强,前端错误状态管理更加一致。
总的来说,这些变更对用户体验和系统可靠性都有积极影响。
|
|
||
| resolved_root = os.path.normpath(os.path.abspath(resolved_root)) | ||
|
|
||
| # TODO: Security check: root must be within allowed roots |
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.
|
|
||
| full_path = os.path.normpath(full_path) | ||
|
|
||
| # TODO: security path check |
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.
|
|
||
| # Normalize path | ||
| full_path = os.path.normpath(full_path) | ||
| # TODO: Security: file must be within root_dir_abs |
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.
| child = os.path.normpath(os.path.abspath(child)) | ||
| return child == parent or child.startswith(parent + os.sep) | ||
|
|
||
| # TODO: security dir check |
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.
| 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) |
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.
| print( | ||
| f'project_discovery.discover_projects(): {project_discovery.discover_projects()}' | ||
| ) |
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.
| 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. | ||
| } |
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.
| self.is_running = True | ||
| self._stop_requested = False | ||
| self.is_running = True | ||
| sent_terminal_event = 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.
| 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.'); | ||
| }; |
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.
| 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]); |
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.
Uh oh!
There was an error while loading. Please reload this page.