Conversation
- Fix thinking visibility by passing user overrides into streaming - Harden UserValves handling for mapping/instance inputs - Update bilingual README with per-user valves and troubleshooting
✅ Plugin Version Check / 插件版本检查版本更新检测通过!PR 包含版本变化和更新说明。 Version check passed! PR contains version changes and update description. 版本变化 / Version Changes插件更新 / Plugin Updates
This comment was generated automatically. / 此评论由自动生成。 |
Summary of ChangesHello @Fu-Jie, 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! This pull request delivers a comprehensive update to the GitHub Copilot SDK plugin, focusing on improved user experience through clearer documentation of new features and configuration options. It also strengthens development practices by introducing cost-conscious testing standards and enhances the plugin's operational stability with more reliable publishing mechanisms and a refactored, more maintainable core codebase. Highlights
Ignored Files
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.
Pull request overview
This pull request enhances the GitHub Copilot SDK plugin with several new features and improvements, updating both English and Chinese versions to v0.2.3. The main changes focus on per-user configuration overrides, improved thinking output reliability, formatting enforcement, custom tools support, and enhanced publishing reliability.
Changes:
- Added
UserValvesclass enabling per-user overrides for reasoning effort, CLI path, debug mode, thinking visibility, and model ID - Implemented custom tools support with example random number generator tool
- Added formatting enforcement features to improve output readability
- Enhanced publishing script to prevent blank image issues during updates
- Updated workflow to automatically fix plugin icon URLs for releases
- Added testing guidelines to enforce use of free/low-cost models for Copilot SDK tests
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 60 comments.
Show a summary per file
| File | Description |
|---|---|
plugins/pipes/github-copilot-sdk/github_copilot_sdk.py |
English version: Added UserValves, custom tools, formatting enforcement, improved debug logging and event handling |
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py |
Chinese version: Same features as English but contains duplicate method definitions and undefined variable issues |
plugins/pipes/github-copilot-sdk/README.md |
Updated English documentation with new features, configuration options, and troubleshooting |
plugins/pipes/github-copilot-sdk/README_CN.md |
Updated Chinese documentation with same information as English version |
scripts/openwebui_community_client.py |
Enhanced to check for existing remote images before uploading to prevent overwrites |
.github/workflows/release.yml |
Added step to update plugin icon URLs to use absolute GitHub URLs |
.github/copilot-instructions.md |
Added testing standards requiring use of free models for Copilot SDK tests |
Comments suppressed due to low confidence (12)
plugins/pipes/github-copilot-sdk/github_copilot_sdk.py:565
- Variable m_name is not used.
m_name = (
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py:702
- Variable m_name is not used.
m_name = (
plugins/pipes/github-copilot-sdk/github_copilot_sdk.py:151
- Overly complex 'del' method.
def __del__(self):
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py:155
- Overly complex 'del' method.
def __del__(self):
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py:13
- Import of 'time' is not used.
import time
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py:21
- Import of 'sys' is not used.
import sys
scripts/openwebui_community_client.py:24
- Import of 'Any' is not used.
from typing import Optional, Dict, List, Any, Tuple
plugins/pipes/github-copilot-sdk/github_copilot_sdk.py:679
- Except block directly handles BaseException.
except:
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py:833
- Except block directly handles BaseException.
except:
plugins/pipes/github-copilot-sdk/github_copilot_sdk.py:679
- 'except' clause does nothing but pass and there is no explanatory comment.
except:
plugins/pipes/github-copilot-sdk/github_copilot_sdk_cn.py:833
- 'except' clause does nothing but pass and there is no explanatory comment.
except:
scripts/openwebui_community_client.py:70
- 'except' clause does nothing but pass and there is no explanatory comment.
except Exception:
| if ( | ||
| "gpt-5.2-codex" | ||
| not in self._collect_model_ids( | ||
| body={}, | ||
| request_model=self.valves.MODEL_ID, | ||
| real_model_id=self.valves.MODEL_ID, | ||
| )[0].lower() | ||
| ): |
There was a problem hiding this comment.
Potential IndexError. The code attempts to access [0] on the result of _collect_model_ids() without checking if the list is empty. If no model IDs are collected, this will raise an IndexError. Add a check to ensure the list is not empty before accessing the first element, or use a safer approach like next(iter(...), "").lower().
| if ( | |
| "gpt-5.2-codex" | |
| not in self._collect_model_ids( | |
| body={}, | |
| request_model=self.valves.MODEL_ID, | |
| real_model_id=self.valves.MODEL_ID, | |
| )[0].lower() | |
| ): | |
| model_ids = self._collect_model_ids( | |
| body={}, | |
| request_model=self.valves.MODEL_ID, | |
| real_model_id=self.valves.MODEL_ID, | |
| ) | |
| first_model_id = (model_ids[0] if model_ids else "").lower() | |
| if "gpt-5.2-codex" not in first_model_id: |
| python3 -c " | ||
| import sys | ||
| import re | ||
|
|
||
| file_path = '$file' | ||
| icon_url = '$TARGET_ICON_URL' | ||
|
|
||
| try: | ||
| with open(file_path, 'r', encoding='utf-8') as f: | ||
| content = f.read() | ||
|
|
||
| # Replace icon_url: ... with new url | ||
| # Matches 'icon_url: ...' and replaces it | ||
| new_content = re.sub(r'^icon_url:.*$', f'icon_url: {icon_url}', content, flags=re.MULTILINE) | ||
|
|
||
| with open(file_path, 'w', encoding='utf-8') as f: | ||
| f.write(new_content) | ||
| print(f'Successfully updated icon_url in {file_path}') | ||
| except Exception as e: | ||
| print(f'Error updating {file_path}: {e}', file=sys.stderr) | ||
| sys.exit(1) | ||
| " |
There was a problem hiding this comment.
Shell variable injection vulnerability in Python inline script. The variables $file and $TARGET_ICON_URL are directly interpolated into the Python string without proper escaping, which could lead to command injection if filenames contain special characters like single quotes. Use a safer approach by passing these as environment variables or arguments to avoid potential security issues.
| # 恢复失败,磁盘上可能不存在该会话 | ||
| self._emit_debug_log( | ||
| f"会话 {chat_id} 不存在或已过期,将创建新会话。" | ||
| reasoning_effort = (effective_reasoning_effort,) |
There was a problem hiding this comment.
Incorrect assignment creates a tuple instead of a string. Line 1058 assigns reasoning_effort = (effective_reasoning_effort,) which creates a single-element tuple, but this variable is expected to be a string. This will likely cause type errors later when the value is used. Remove the parentheses and comma to assign the string value directly: reasoning_effort = effective_reasoning_effort. Note: this line appears to be debug/leftover code that may not even be necessary since reasoning_effort is not used in this scope.
| reasoning_effort = (effective_reasoning_effort,) | |
| reasoning_effort = effective_reasoning_effort |
| if chat_id: | ||
| try: | ||
| # 尝试直接使用 chat_id 作为 session_id 恢复会话 | ||
| session = await client.resume_session(chat_id) | ||
| self._emit_debug_log(f"已通过 ChatID 恢复会话: {chat_id}") | ||
| await self._emit_debug_log( | ||
| f"已通过 ChatID 恢复会话: {chat_id}", __event_call__ | ||
| ) | ||
|
|
||
| # 显示工作空间信息(如果可用) | ||
| if self.valves.DEBUG and self.valves.SHOW_WORKSPACE_INFO: | ||
| if session.workspace_path: | ||
| await self._emit_debug_log( | ||
| f"会话工作空间: {session.workspace_path}", | ||
| __event_call__, | ||
| ) | ||
|
|
||
| is_new_session = False | ||
| except Exception: | ||
| except Exception as e: | ||
| # 恢复失败,磁盘上可能不存在该会话 | ||
| self._emit_debug_log( | ||
| f"会话 {chat_id} 不存在或已过期,将创建新会话。" | ||
| reasoning_effort = (effective_reasoning_effort,) | ||
| await self._emit_debug_log( | ||
| f"会话 {chat_id} 不存在或已过期 ({str(e)}),将创建新会话。", | ||
| __event_call__, | ||
| ) | ||
| session = None | ||
|
|
||
| if session is None: | ||
| # 创建新会话 | ||
| from copilot.types import SessionConfig, InfiniteSessionConfig | ||
|
|
||
| # 无限会话配置 | ||
| infinite_session_config = None | ||
| if self.valves.INFINITE_SESSION: | ||
| infinite_session_config = InfiniteSessionConfig( | ||
| enabled=True, | ||
| background_compaction_threshold=self.valves.COMPACTION_THRESHOLD, | ||
| buffer_exhaustion_threshold=self.valves.BUFFER_THRESHOLD, | ||
| session_config = self._build_session_config( | ||
| chat_id, | ||
| real_model_id, | ||
| custom_tools, | ||
| system_prompt_content, | ||
| is_streaming, | ||
| ) | ||
| if system_prompt_content: | ||
| await self._emit_debug_log( | ||
| f"配置系统消息(模式: append)", | ||
| __event_call__, | ||
| ) | ||
|
|
||
| session_config = SessionConfig( | ||
| session_id=( | ||
| chat_id if chat_id else None | ||
| ), # 使用 chat_id 作为 session_id | ||
| model=real_model_id, | ||
| streaming=body.get("stream", False), | ||
| infinite_sessions=infinite_session_config, | ||
| ) | ||
| # 显示系统配置预览 | ||
| if system_prompt_content or self.valves.ENFORCE_FORMATTING: | ||
| preview_parts = [] | ||
| if system_prompt_content: | ||
| preview_parts.append( | ||
| f"自定义提示词: {system_prompt_content[:100]}..." | ||
| ) | ||
| if self.valves.ENFORCE_FORMATTING: | ||
| preview_parts.append("格式化指导: 已启用") | ||
|
|
||
| if isinstance(session_config, dict): | ||
| system_config = session_config.get("system_message", {}) | ||
| else: | ||
| system_config = getattr(session_config, "system_message", None) | ||
|
|
||
| if isinstance(system_config, dict): | ||
| full_content = system_config.get("content", "") | ||
| else: | ||
| full_content = "" | ||
|
|
||
| await self._emit_debug_log( | ||
| f"系统消息配置 - {', '.join(preview_parts)} (总长度: {len(full_content)} 字符)", | ||
| __event_call__, | ||
| ) | ||
|
|
||
| session = await client.create_session(config=session_config) | ||
|
|
||
| # 获取新会话 ID | ||
| new_sid = getattr(session, "session_id", getattr(session, "id", None)) | ||
| self._emit_debug_log(f"创建了新会话: {new_sid}") | ||
|
|
||
| # 构建 Prompt | ||
| if is_new_session: | ||
| # 新会话,发送完整历史 | ||
| full_conversation = [] | ||
| for msg in messages[:-1]: | ||
| role = msg.get("role", "user").upper() | ||
| content = msg.get("content", "") | ||
| if isinstance(content, list): | ||
| content = " ".join( | ||
| [ | ||
| c.get("text", "") | ||
| for c in content | ||
| if c.get("type") == "text" | ||
| ] | ||
| await self._emit_debug_log(f"创建了新会话: {new_sid}", __event_call__) | ||
|
|
||
| # 显示新会话的工作空间信息 | ||
| if self.valves.DEBUG and self.valves.SHOW_WORKSPACE_INFO: | ||
| if session.workspace_path: | ||
| await self._emit_debug_log( | ||
| f"会话工作空间: {session.workspace_path}", | ||
| __event_call__, | ||
| ) | ||
| full_conversation.append(f"{role}: {content}") | ||
| full_conversation.append(f"User: {last_text}") | ||
| prompt = "\n\n".join(full_conversation) | ||
| else: | ||
| # 恢复的会话,只发送最后一条消息 | ||
| prompt = last_text | ||
|
|
||
| # 构建 Prompt(基于会话:仅发送最新用户输入) | ||
| prompt = self._apply_formatting_hint(last_text) | ||
|
|
There was a problem hiding this comment.
Undefined variable usage. The variable is_new_session is used at line 1129 in the init_msg logic, but it's only defined within the if chat_id: block. If chat_id is None or empty, is_new_session will be undefined when accessed, causing a NameError. The variable should be initialized before the if chat_id: block to ensure it's always defined. This issue exists in the Chinese version but the English version handles this differently by not using is_new_session in the same way.
| real_model_id, | ||
| custom_tools, | ||
| system_prompt_content, | ||
| is_streaming, |
There was a problem hiding this comment.
Missing parameter in method call. The _build_session_config method is defined with a reasoning_effort parameter (line 421), but when it's called at line 955, this parameter is not passed. This means the reasoning effort setting won't be applied to new sessions. Add reasoning_effort=effective_reasoning_effort to the method call arguments to ensure the user's reasoning effort setting is properly applied.
| is_streaming, | |
| is_streaming, | |
| reasoning_effort=effective_reasoning_effort, |
| json_obj, indent=2, ensure_ascii=False | ||
| ) | ||
| is_json = True | ||
| except: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| done.set() | ||
| try: | ||
| queue.put_nowait(SENTINEL) | ||
| except: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| done.set() | ||
| try: | ||
| queue.put_nowait(SENTINEL) | ||
| except: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| input_tokens = safe_get_data_attr(event, "input_tokens", 0) | ||
| output_tokens = safe_get_data_attr(event, "output_tokens", 0) | ||
| total_tokens = safe_get_data_attr(event, "total_tokens", 0) | ||
| pass |
There was a problem hiding this comment.
Unnecessary 'pass' statement.
| input_tokens = safe_get_data_attr(event, "input_tokens", 0) | ||
| output_tokens = safe_get_data_attr(event, "output_tokens", 0) | ||
| total_tokens = safe_get_data_attr(event, "total_tokens", 0) | ||
| pass |
There was a problem hiding this comment.
Unnecessary 'pass' statement.
There was a problem hiding this comment.
Code Review
This pull request significantly updates the GitHub Copilot SDK integration for OpenWebUI, bumping the version to 0.2.3. Key changes include the introduction of per-user overrides for settings like reasoning effort, CLI path, debug mode, thinking display, and model ID. It adds support for custom tools, with an example generate_random_number tool, and enhances output formatting by adding automatic hints to the system prompt for better readability. The update also refines debug logging to output to the browser console, adds a LOG_LEVEL setting, and includes SHOW_WORKSPACE_INFO. The internal Python code has been refactored to centralize configuration, improve system prompt extraction from various sources, and enhance streaming event handling for messages, reasoning, and detailed tool execution events. Additionally, a new testing standard document has been added, recommending gpt-5-mini and gpt-4.1 for Copilot SDK related tests to manage costs. Review comments highlighted the need to catch more specific exceptions instead of bare except: blocks for better error handling and debugging, and pointed out a redundant line assigning reasoning_effort within an except block.
| except: | ||
| pass |
There was a problem hiding this comment.
Catching a bare except: is too broad and can hide unexpected errors, making debugging difficult. It's best practice to catch specific exceptions that you anticipate, or at least Exception if you intend to catch all standard errors.
| except: | |
| pass | |
| except json.JSONDecodeError: | |
| self._emit_debug_log_sync("Warning: CUSTOM_ENV_VARS is not valid JSON.", __event_call__) | |
| except Exception as e: | |
| self._emit_debug_log_sync(f"Error processing CUSTOM_ENV_VARS: {e}", __event_call__) |
| session, | ||
| send_payload, | ||
| init_message: str = "", | ||
| __event_call__=None, |
| except: | ||
| pass |
There was a problem hiding this comment.
Catching a bare except: is too broad and can hide unexpected errors, making debugging difficult. It's best practice to catch specific exceptions that you anticipate, or at least Exception if you intend to catch all standard errors.
| except: | |
| pass | |
| except json.JSONDecodeError: | |
| self._emit_debug_log_sync("Warning: CUSTOM_ENV_VARS is not valid JSON.", __event_call__) | |
| except Exception as e: | |
| self._emit_debug_log_sync(f"Error processing CUSTOM_ENV_VARS: {e}", __event_call__) |
| # 恢复失败,磁盘上可能不存在该会话 | ||
| self._emit_debug_log( | ||
| f"会话 {chat_id} 不存在或已过期,将创建新会话。" | ||
| reasoning_effort = (effective_reasoning_effort,) |
This pull request introduces several documentation and workflow improvements for the GitHub Copilot SDK plugin, focusing on enhanced test standards, plugin usability, and publishing reliability. The most important changes are grouped below.
Documentation Updates:
README.md) and Chinese (README_CN.md) documentation to version 0.2.3, highlighting new features such as per-user overrides, improved thinking output reliability, formatting enforcement, and custom tools support. [1] [2]Testing and Workflow Enhancements:
.github/copilot-instructions.mdspecifying mandatory use of free/low-cost models (gpt-5-mini,gpt-4.1) for Copilot SDK test scripts, prohibiting expensive models unless explicitly requested.release.yml) by adding a step that automatically updates plugin icon URLs to use absolute GitHub URLs, ensuring icons are correctly referenced in plugin files.Publishing Reliability:
openwebui_community_client.py) to avoid overwriting existing post images during updates, preventing blank image issues. The script now checks for existing remote media before uploading new images and ensures remote post info is up-to-date for version checks. [1] [2] [3]