feat(pipes): v0.7.0 final release with native tool UI and CLI integration

- Core: Adapt to OpenWebUI native tool call UI and thinking process visualization
- Infra: Bundle Copilot CLI via pip package (no more background curl installation)
- Fix: Resolve "Error getting file content" on OpenWebUI v0.8.0+ via absolute paths
- i18n: Add native localization for status messages in 11 languages
- UX: Optimize reasoning status display logic and cleanup legacy code
This commit is contained in:
fujie
2026-02-23 02:33:59 +08:00
parent 272b959a44
commit fc9f1ccb43
11 changed files with 670 additions and 2028 deletions

View File

@@ -478,7 +478,117 @@ async def get_user_language(self):
**注意**: 即使插件有 `Valves` 配置,也应优先尝试自动探测,提升用户体验。
### 8. 智能代理文件交付规范 (Agent File Delivery Standards)
### 8. 国际化 (i18n) 适配规范 (Internationalization Standards)
开发供全球用户使用的插件时,必须预置多语言支持(如中文、英文等)。
#### i18n 字典定义
在文件顶部定义 `TRANSLATIONS` 字典存储多语言字符串:
```python
TRANSLATIONS = {
"en-US": {
"status_starting": "Smart Mind Map is starting...",
},
"zh-CN": {
"status_starting": "智能思维导图正在启动...",
},
# ... 其他语言
}
# 语言回退映射 (Fallback Map)
FALLBACK_MAP = {
"zh": "zh-CN",
"zh-TW": "zh-CN",
"zh-HK": "zh-CN",
"en": "en-US",
"en-GB": "en-US"
}
```
#### 获取当前用户真实语言 (Robust Language Detection)
Open WebUI 的前端localStorage并未自动同步语言设置到后端数据库或通过标准 API 参数传递。为了获取精准的用户偏好语言,**必须**使用多层级回退机制Multi-level Fallback
`JS 动态探测 (localStorage)` > `HTTP 浏览器头 (Accept-Language)` > `用户 Profile 默认设置` > `en-US`
> **注意!防卡死指南 (Anti-Deadlock Guide)**
> 在通过 `__event_call__` 执行前端 JS 脚本时,如果前端脚本不慎抛出异常 (`Exception`) 会导致回调函数 `cb()` 永不执行,这会让后端的 `asyncio` 永远阻塞并卡死整个请求队列!
> **必须**做两重防护:
> 1. JS 内部包裹 `try...catch` 保证必须有 `return`。
> 2. 后端使用 `asyncio.wait_for` 设置强制超时(建议 2 秒)。
```python
import asyncio
from fastapi import Request
async def _get_user_context(
self,
__user__: Optional[dict],
__event_call__: Optional[callable] = None,
__request__: Optional[Request] = None,
) -> dict:
user_language = __user__.get("language", "en-US") if __user__ else "en-US"
# 1st Fallback: HTTP Accept-Language header
if __request__ and hasattr(__request__, "headers") and "accept-language" in __request__.headers:
raw_lang = __request__.headers.get("accept-language", "")
if raw_lang:
user_language = raw_lang.split(",")[0].split(";")[0]
# 2nd Fallback (Best): Execute JS in frontend to read localStorage
if __event_call__:
try:
js_code = """
try {
return (
document.documentElement.lang ||
localStorage.getItem('locale') ||
navigator.language ||
'en-US'
);
} catch (e) {
return 'en-US';
}
"""
# 【致命!】必须设置 wait_for 防止前端无响应卡死后端
frontend_lang = await asyncio.wait_for(
__event_call__({"type": "execute", "data": {"code": js_code}}),
timeout=2.0
)
if frontend_lang and isinstance(frontend_lang, str):
user_language = frontend_lang
except Exception as e:
pass # fallback to accept-language or en-US
return {
"user_language": user_language,
# ... user_name, user_id etc.
}
```
#### 实际使用 (Usage in Action/Filter)
在 Action 或者 Filter 执行时引用这套上下文获取机制,然后传入映射器获取最终翻译:
```python
async def action(
self,
body: dict,
__user__: Optional[dict] = None,
__event_call__: Optional[callable] = None,
__request__: Optional[Request] = None,
**kwargs
) -> Optional[dict]:
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
user_lang = user_ctx["user_language"]
# 获取多语言文本 (通过你的 translation.get() 扩展)
# start_msg = self._get_translation(user_lang, "status_starting")
```
### 9. 智能代理文件交付规范 (Agent File Delivery Standards)
在开发具备文件生成能力的智能代理插件(如 GitHub Copilot SDK 集成)时,必须遵循以下标准流程,以确保文件在不同存储后端(本地/S3下的可用性并绕过不必要的 RAG 处理。
@@ -498,7 +608,7 @@ async def get_user_language(self):
- 代理应始终将“当前目录”视为其受保护所在的私有工作空间。
- `publish_file_from_workspace` 的参数 `filename` 仅需传入相对于当前目录的文件名。
### 9. Copilot SDK 插件工具定义规范 (Copilot SDK Tool Definition Standards)
### 10. Copilot SDK 插件工具定义规范 (Copilot SDK Tool Definition Standards)
在为 GitHub Copilot SDK 开发自定义工具时,为了确保大模型能正确识别参数(避免生成空的 `properties` Schema必须遵循以下定义模式
@@ -532,6 +642,63 @@ my_tool = define_tool(
2. **Field 描述**: 在 `BaseModel` 中使用 `Field(..., description="...")` 为每个参数提供详细的描述信息。
3. **Required vs Optional**: 明确标注必填项(无默认值)和可选项(带 `default`)。
### 11. Copilot SDK 流式渲染与工具卡片规范 (Streaming & Tool Card Standards)
在处理大模型的思维链Reasoning输出和工具调用Tool Calls为了确保能完美兼容 OpenWebUI 0.8.x 前端的 Markdown 解析器及原生折叠 UI 组件,必须遵循以下极度严格的输出格式规范。
#### 思维链流式渲染 (Reasoning Streaming)
为了让前端能够正确显示“Thinking...”的折叠框和 Spinner 动画,**必须**使用原生的 `<think>` 标签。
- **正确的标签包裹**:
```html
<think>
这里是思考过程...
</think>
```
- **关键细节**:
- **标签闭合检测**: 必须在代码内部维护状态(如 `state["thinking_started"]`。当1正文内容即将开始输出2工具调用触发 (`tool.execution_start`) 时,**必须优先输出 `\n</think>\n` 强制闭合标签**。如果不闭合,后续的正文或工具面板会被全部吞进思考框内,导致页面完全崩坏!
- **不要手动拼装**: 严禁通过手动输出 `<details type="reasoning">` 等大段 HTML 来模拟思考过程,这种方式极易在流式片段发送中破坏前端 DOM 树并导致错位。
#### 工具调用原生卡片 (Native Tool Calls Block)
为了在对话界面中生成标准、原生的下拉折叠“工具调用”卡片,当 `event_type == "tool.execution_complete"` 时,必须向队列输出如下严格格式的 HTML
```python
# 必须转义属性中的双引号为 &quot;
args_for_attr = args_json_str.replace('"', "&quot;")
result_for_attr = result_content.replace('"', "&quot;")
tool_block = (
f'\\n<details type="tool_calls"'
f' id="{tool_call_id}"'
f' name="{tool_name}"'
f' arguments="{args_for_attr}"'
f' result="{result_for_attr}"'
f' done="true">\\n'
f"<summary>Tool Executed</summary>\\n"
f"</details>\\n\\n"
)
queue.put_nowait(tool_block)
```
- **致命避坑点 (Critical Pitfalls)**:
1. **属性转义 (Extremely Important)**: `<details>` 内的 `arguments` 和 `result` 属性**必须**将内部的所有双引号 `"` 替换为 `&quot;`。因为 OpenWebUI 前端提取这些数据的 Regex 是严格的 `="([^"]*)"`,一旦内容中出现原生双引号,就会被瞬间截断,导致参数被渲染为空并引发解析错误!
2. **换行符要求**: `<details ...>` 尖括号闭合后紧接着的内容**必须换行**(即 `>\\n`),否则 Markdown 扩展引擎无法将其识别为独立的 UI Block。
3. **去除冗余通知**: 不要在 `tool.execution_start` 事件中提前向对话流输出普通的 `🔧 Executing...` 纯文本块,这会导致最终页面上同时出现两块工具提示(一个文本,一个折叠卡片)。
#### Debug 信息的解耦 (Decoupling Debug Logs)
对于连接建立、运行环境、缓存加载等属于 *脚本自身运行状态* 的 Debug 信息:
- **禁止**: 不要将这些内容 yield 到最终的回答数据流(或塞进 `<think>` 标签内),这会污染回答的纯粹性。
- **推荐**: 统一使用 OpenWebUI 顶部的原生状态反馈气泡Status Events
```python
await __event_emitter__({
"type": "status",
"data": {"description": "连接建立,正在等待响应...", "done": True}
})
```
---
## ⚡ Action 插件规范 (Action Plugin Standards)

View File

@@ -1,6 +1,6 @@
# GitHub Copilot SDK Pipe for OpenWebUI
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.6.2 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.7.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/open-webui) that integrates the official [GitHub Copilot SDK](https://github.com/github/copilot-sdk). It enables you to use **GitHub Copilot models** (e.g., `gpt-5.2-codex`, `claude-sonnet-4.5`,`gemini-3-pro`, `gpt-5-mini`) **AND** your own models via **BYOK** (OpenAI, Anthropic) directly within OpenWebUI, providing a unified agentic experience with **strict User & Chat-level Workspace Isolation**.
@@ -14,12 +14,13 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
---
## ✨ v0.6.2 Updates (What's New)
## ✨ v0.7.0 Updates (What's New)
- **🛠️ New Workspace Artifacts Tool**: Introduced `publish_file_from_workspace`. Agents can now generate files (e.g., Python-generated Excel/CSV) and provide direct download links for the user to click and save.
- **⚙️ Workflow Optimization**: Improved reliability of the internal agentic workspace management.
- **🛡️ Enhanced Security**: Refined access control for system resources within the isolated environment.
- **🔧 Performance Tuning**: Optimized stream processing for larger context windows.
- **🚀 Integrated CLI Management**: The Copilot CLI is now automatically managed and bundled via the `github-copilot-sdk` pip package. (v0.7.0)
- **🧠 Native Tool Call UI**: Full adaptation to **OpenWebUI's native tool call UI** and thinking process visualization. (v0.7.0)
- **🏠 OpenWebUI v0.8.0+ Fix**: Resolved "Error getting file content" download failure by switching to absolute path registration for published files. (v0.7.0)
- **🌐 Comprehensive Multi-language Support**: Native localization for status messages in 11 languages (EN, ZH, JA, KO, FR, DE, ES, IT, RU, VI, ID). (v0.7.0)
- **🧹 Architecture Cleanup**: Refactored core setup and optimized reasoning status display for a leaner experience. (v0.7.0)
---
@@ -31,8 +32,8 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
- **♾️ Infinite Session Management**: Smart context window management with automatic compaction for indefinite conversation capability.
- **🧠 Deep Database Integration**: Real-time persistence of TOD·O lists for long-running workflows.
- **🌊 Advanced Streaming**: Full support for thinking process/Chain of Thought visualization.
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support.
- **⚡ Full-Lifecycle File Agent**: Supports receiving uploaded files for raw bypass analysis and publishing results (Excel/reports) as downloadable links.
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support (bypasses RAG for direct binary access).
- **📤 Workspace Artifacts (`publish_file_from_workspace`)**: Agents can generate files (Excel, CSV, HTML reports, etc.) and provide **persistent download links** directly in the chat.
- **🖼️ Interactive Artifacts**: Automatically renders HTML/JS apps generated by the agent directly in the chat interface.
---
@@ -110,7 +111,7 @@ If this plugin has been useful, a **Star** on [OpenWebUI Extensions](https://git
- **Agent ignores files?**: Ensure the Files Filter is enabled, otherwise RAG will interfere with raw binaries.
- **No progress bar?**: The bar only appears when the Agent uses the `update_todo` tool.
- **Dependencies**: This Pipe automatically installs `github-copilot-sdk` (Python) and `github-copilot-cli` (Binary).
- **Dependencies**: This Pipe automatically manages `github-copilot-sdk` (Python) and utilizes the bundled binary CLI. No manual install required.
---

View File

@@ -1,6 +1,6 @@
# GitHub Copilot SDK 官方管道
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 0.6.2 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 0.7.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
这是一个用于 [OpenWebUI](https://github.com/open-webui/open-webui) 的高级 Pipe 函数,深度集成了 **GitHub Copilot SDK**。它不仅支持 **GitHub Copilot 官方模型**(如 `gpt-5.2-codex`, `claude-sonnet-4.5`, `gemini-3-pro`, `gpt-5-mini`),还支持 **BYOK (自带 Key)** 模式对接自定义服务商OpenAI, Anthropic并具备**严格的用户与会话级工作区隔离**能力,提供统一且安全的 Agent 交互体验。
@@ -14,12 +14,13 @@
---
## ✨ 0.6.2 更新内容 (What's New)
## ✨ 0.7.0 更新内容 (What's New)
- **🛠️ 新增工作区产物工具**: 引入 `publish_file_from_workspace`。Agent 现在可以生成物理文件(如使用 Python 生成的 Excel/CSV 报表),并直接在聊天界面提供点击下载链接。
- **⚙️ 工作流优化**: 提升了内部 Agent 物理工作区管理的可靠性与原子性。
- **🛡️ 安全增强**: 精细化了隔离环境下系统资源的访问控制策略。
- **🔧 性能微调**: 针对大上下文窗口优化了流式数据处理性能。
- **🚀 CLI 免维护集成**: Copilot CLI 现在通过 `github-copilot-sdk` pip 包自动同步管理,彻底告别手动 `curl | bash` 安装问题。(v0.7.0)
- **🧠 原生工具调用 UI**: 全面适配 **OpenWebUI 原生工具调用 UI** 与模型思考过程(思维链)展示。(v0.7.0)
- **🏠 OpenWebUI v0.8.0+ 兼容性修复**: 通过切换为绝对路径注册发布文件彻底解决了“Error getting file content”无法下载到本地的问题。(v0.7.0)
- **🌐 全面的多语言支持**: 针对状态消息进行了 11 国语言的原生本地化 (中/英/日/韩/法/德/西/意/俄/越/印尼)。(v0.7.0)
- **🧹 架构精简**: 重构了初始化逻辑并优化了推理状态显示,提供更轻量稳健的体验。(v0.7.0)
---
@@ -31,8 +32,8 @@
- **♾️ 无限会话管理**: 智能上下文窗口管理与自动压缩算法,支持无限时长的对话交互。
- **🧠 深度数据库集成**: 实时持久化 TOD·O 列表到 UI 进度条。
- **🌊 深度推理展示**: 完整支持模型思考过程 (Thinking Process) 的流式渲染。
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析。
- **⚡ 全生命周期文件 Agent**: 支持接收上传文件进行绕过 RAG 的深度分析,并将处理结果(如 Excel/报告)发布为下载链接实现闭环
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析(绕过 RAG 直接访问原始二进制内容)
- **📤 工作区产物工具 (`publish_file_from_workspace`)**: Agent 可生成文件Excel、CSV、HTML 报告等)并直接提供**持久化下载链接**。管理员还可额外获得通过 `/content/html` 接口的**聊天内 HTML 预览**链接
- **🖼️ 交互式伪影 (Artifacts)**: 自动渲染 Agent 生成的 HTML/JS 应用程序,直接在聊天界面交互。
---
@@ -95,7 +96,7 @@
### 1) 导入函数
1. 打开 OpenWebUI前往 **工作区** -> **函数**
2. 点击 **+** (创建函数),完整粘贴 `github_copilot_sdk_cn.py` 的内容。
2. 点击 **+** (创建函数),完整粘贴 `github_copilot_sdk.py` 的内容。
3. 点击保存并确保已启用。
### 2) 获取 Token (Get Token)
@@ -110,7 +111,7 @@
- **Agent 无法识别文件?**: 请确保已安装并启用了 Files Filter 插件,否则原始文件会被 RAG 干扰。
- **看不到 TODO 进度条?**: 进度条仅在 Agent 使用 `update_todo` 工具(通常是处理复杂任务)时出现。
- **依赖安装**: 本管道会自动尝试安装 `github-copilot-sdk` (Python 包) `github-copilot-cli` (官方二进制)
- **依赖安装**: 本管道会自动管理 `github-copilot-sdk` (Python 包) 并优先直接使用内置的二进制 CLI无需手动干预
---

View File

@@ -15,7 +15,7 @@ Pipes allow you to:
## Available Pipe Plugins
- [GitHub Copilot SDK](github-copilot-sdk.md) (v0.6.2) - Official GitHub Copilot SDK integration. Features **Workspace Isolation**, **Database Persistence**, **Zero-config OpenWebUI Tool Bridge**, **BYOK** support, and **dynamic MCP discovery**. Supports streaming, multimodal, and infinite sessions. [View Deep Dive](github-copilot-sdk-deep-dive.md) | [**View Advanced Tutorial**](github-copilot-sdk-tutorial.md).
- [GitHub Copilot SDK](github-copilot-sdk.md) (v0.7.0) - Official GitHub Copilot SDK integration. Features **Workspace Isolation**, **Database Persistence**, **Zero-config OpenWebUI Tool Bridge**, **BYOK** support, and **dynamic MCP discovery**. Supports streaming, multimodal, and infinite sessions. [View Deep Dive](github-copilot-sdk-deep-dive.md) | [**View Advanced Tutorial**](github-copilot-sdk-tutorial.md).
- **[Case Study: GitHub 100 Star Growth Analysis](star-prediction-example.md)** - Learn how to use the GitHub Copilot SDK Pipe with Minimax 2.1 to automatically analyze CSV data and generate project growth reports.
- **[Case Study: High-Quality Video to GIF Conversion](video-processing-example.md)** - See how the model uses system-level FFmpeg to accelerate, scale, and optimize colors for screen recordings.

View File

@@ -15,7 +15,7 @@ Pipes 可以用于:
## 可用的 Pipe 插件
- [GitHub Copilot SDK](github-copilot-sdk.zh.md) (v0.6.2) - GitHub Copilot SDK 官方集成。具备**工作区安全隔离**、**数据库持久化**、**零配置工具桥接**与**BYOK (自带 Key) 支持**。支持流式输出、打字机思考过程及无限会话。[查看深度架构解析](github-copilot-sdk-deep-dive.zh.md) | [**查看进阶实战教程**](github-copilot-sdk-tutorial.zh.md)。
- [GitHub Copilot SDK](github-copilot-sdk.zh.md) (v0.7.0) - GitHub Copilot SDK 官方集成。具备**工作区安全隔离**、**数据库持久化**、**零配置工具桥接**与**BYOK (自带 Key) 支持**。支持流式输出、打字机思考过程及无限会话。[查看深度架构解析](github-copilot-sdk-deep-dive.zh.md) | [**查看进阶实战教程**](github-copilot-sdk-tutorial.zh.md)。
- **[实战案例GitHub 100 Star 增长预测](star-prediction-example.zh.md)** - 展示如何使用 GitHub Copilot SDK Pipe 结合 Minimax 2.1 模型,自动编写脚本分析 CSV 数据并生成详细的项目增长报告。
- **[实战案例:视频高质量 GIF 转换与加速](video-processing-example.zh.md)** - 演示模型如何通过底层 FFmpeg 工具对录屏进行加速、缩放及双阶段色彩优化处理。

View File

@@ -9,6 +9,7 @@ icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAw
description: Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge.
"""
import asyncio
import logging
import os
import re
@@ -1514,6 +1515,7 @@ class Action:
self,
__user__: Optional[Dict[str, Any]],
__event_call__: Optional[Callable[[Any], Awaitable[None]]] = None,
__request__: Optional[Request] = None,
) -> Dict[str, str]:
"""Extract basic user context with safe fallbacks."""
if isinstance(__user__, (list, tuple)):
@@ -1528,20 +1530,36 @@ class Action:
# Default from profile
user_language = user_data.get("language", "en-US")
# Priority: Document Lang > LocalStorage (Frontend) > Browser > Profile (Default)
# Level 1 Fallback: Accept-Language from __request__ headers
if (
__request__
and hasattr(__request__, "headers")
and "accept-language" in __request__.headers
):
raw_lang = __request__.headers.get("accept-language", "")
if raw_lang:
user_language = raw_lang.split(",")[0].split(";")[0]
# Priority: Document Lang > LocalStorage (Frontend) > Browser > Request Header > Profile
if __event_call__:
try:
js_code = """
return (
document.documentElement.lang ||
localStorage.getItem('locale') ||
localStorage.getItem('language') ||
navigator.language ||
'en-US'
);
try {
return (
document.documentElement.lang ||
localStorage.getItem('locale') ||
localStorage.getItem('language') ||
navigator.language ||
'en-US'
);
} catch (e) {
return 'en-US';
}
"""
frontend_lang = await __event_call__(
{"type": "execute", "data": {"code": js_code}}
# Use asyncio.wait_for to prevent hanging if frontend fails to callback
frontend_lang = await asyncio.wait_for(
__event_call__({"type": "execute", "data": {"code": js_code}}),
timeout=2.0,
)
if frontend_lang and isinstance(frontend_lang, str):
user_language = frontend_lang
@@ -2387,7 +2405,7 @@ class Action:
__request__: Optional[Request] = None,
) -> Optional[dict]:
logger.info("Action: Smart Mind Map (v1.0.0) started")
user_ctx = await self._get_user_context(__user__, __event_call__)
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
user_language = user_ctx["user_language"]
user_name = user_ctx["user_name"]
user_id = user_ctx["user_id"]

View File

@@ -1,6 +1,6 @@
# GitHub Copilot SDK Pipe for OpenWebUI
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.6.2 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.7.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/open-webui) that integrates the official [GitHub Copilot SDK](https://github.com/github/copilot-sdk). It enables you to use **GitHub Copilot models** (e.g., `gpt-5.2-codex`, `claude-sonnet-4.5`,`gemini-3-pro`, `gpt-5-mini`) **AND** your own models via **BYOK** (OpenAI, Anthropic) directly within OpenWebUI, providing a unified agentic experience with **strict User & Chat-level Workspace Isolation**.
@@ -14,12 +14,13 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
---
## ✨ v0.6.2 Updates (What's New)
## ✨ v0.7.0 Updates (What's New)
- **🛠️ New Workspace Artifacts Tool**: Introduced `publish_file_from_workspace`. Agents can now generate files (e.g., Python-generated Excel/CSV) and provide direct download links for the user to click and save.
- **⚙️ Workflow Optimization**: Improved reliability of the internal agentic workspace management.
- **🛡️ Enhanced Security**: Refined access control for system resources within the isolated environment.
- **🔧 Performance Tuning**: Optimized stream processing for larger context windows.
- **🚀 Integrated CLI Management**: The Copilot CLI is now automatically managed and bundled via the `github-copilot-sdk` pip package. No more manual `curl | bash` installation or version mismatches. (v0.7.0)
- **🧠 Native Tool Call UI**: Full adaptation to **OpenWebUI's native tool call UI** and thinking process visualization. (v0.7.0)
- **🏠 OpenWebUI v0.8.0+ Fix**: Resolved "Error getting file content" download failure by switching to absolute path registration for published files. (v0.7.0)
- **🌐 Comprehensive Multi-language Support**: Native localization for status messages in 11 languages (EN, ZH, JA, KO, FR, DE, ES, IT, RU, VI, ID). (v0.7.0)
- **🧹 Architecture Cleanup**: Refactored core setup and optimized reasoning status display for a leaner experience. (v0.7.0)
---
@@ -31,8 +32,8 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
- **♾️ Infinite Session Management**: Smart context window management with automatic compaction for indefinite conversation capability.
- **🧠 Deep Database Integration**: Real-time persistence of TOD·O lists for long-running workflows.
- **🌊 Advanced Streaming**: Full support for thinking process/Chain of Thought visualization.
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support.
- **⚡ Full-Lifecycle File Agent**: Supports receiving uploaded files for raw bypass analysis and publishing results (Excel/reports) as downloadable links.
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support (bypasses RAG for direct binary access).
- **📤 Workspace Artifacts (`publish_file_from_workspace`)**: Agents can generate files (Excel, CSV, HTML reports, etc.) and provide **persistent download links** directly in the chat.
- **🖼️ Interactive Artifacts**: Automatically renders HTML/JS apps generated by the agent directly in the chat interface.
---
@@ -110,7 +111,7 @@ If this plugin has been useful, a **Star** on [OpenWebUI Extensions](https://git
- **Agent ignores files?**: Ensure the Files Filter is enabled, otherwise RAG will interfere with raw binaries.
- **No progress bar?**: The bar only appears when the Agent uses the `update_todo` tool.
- **Dependencies**: This Pipe automatically installs `github-copilot-sdk` (Python) and `github-copilot-cli` (Binary).
- **Dependencies**: This Pipe automatically manages `github-copilot-sdk` (Python) and utilizes the bundled binary CLI. No manual install required.
---

View File

@@ -1,6 +1,6 @@
# GitHub Copilot SDK 官方管道
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.6.2 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.7.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
这是一个用于 [OpenWebUI](https://github.com/open-webui/open-webui) 的高级 Pipe 函数,深度集成了 **GitHub Copilot SDK**。它不仅支持 **GitHub Copilot 官方模型**(如 `gpt-5.2-codex`, `claude-sonnet-4.5`, `gemini-3-pro`, `gpt-5-mini`),还支持 **BYOK (自带 Key)** 模式对接自定义服务商OpenAI, Anthropic并具备**严格的用户与会话级工作区隔离**能力,提供统一且安全的 Agent 交互体验。
@@ -14,12 +14,13 @@
---
## ✨ 0.6.2 更新内容 (What's New)
## ✨ 0.7.0 更新内容 (What's New)
- **🛠️ 新增工作区产物工具**: 引入 `publish_file_from_workspace`。Agent 现在可以生成物理文件(如使用 Python 生成的 Excel/CSV 报表),并直接在聊天界面提供点击下载链接。
- **⚙️ 工作流优化**: 提升了内部 Agent 物理工作区管理的可靠性与原子性。
- **🛡️ 安全增强**: 精细化了隔离环境下系统资源的访问控制策略。
- **🔧 性能微调**: 针对大上下文窗口优化了流式数据处理性能。
- **🚀 CLI 免维护集成**: Copilot CLI 现在通过 `github-copilot-sdk` pip 包自动同步管理,彻底告别手动 `curl | bash` 安装及版本不匹配问题。(v0.7.0)
- **🧠 原生工具调用 UI**: 全面适配 **OpenWebUI 原生工具调用 UI** 与模型思考过程(思维链)展示。(v0.7.0)
- **🏠 OpenWebUI v0.8.0+ 兼容性修复**: 通过切换为绝对路径注册发布文件彻底解决了“Error getting file content”无法下载到本地的问题。(v0.7.0)
- **🌐 全面的多语言支持**: 针对状态消息进行了 11 国语言的原生本地化 (中/英/日/韩/法/德/西/意/俄/越/印尼)。(v0.7.0)
- **🧹 架构精简**: 重构了初始化逻辑并优化了推理状态显示,提供更轻量稳健的体验。(v0.7.0)
---
@@ -31,8 +32,8 @@
- **♾️ 无限会话管理**: 智能上下文窗口管理与自动压缩算法,支持无限时长的对话交互。
- **🧠 深度数据库集成**: 实时持久化 TOD·O 列表到 UI 进度条。
- **🌊 深度推理展示**: 完整支持模型思考过程 (Thinking Process) 的流式渲染。
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析。
- **⚡ 全生命周期文件 Agent**: 支持接收上传文件进行绕过 RAG 的深度分析,并将处理结果(如 Excel/报告)发布为下载链接。
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析(绕过 RAG 直接访问原始二进制内容)
- **📤 工作区产物工具 (`publish_file_from_workspace`)**: Agent 可生成文件Excel、CSV、HTML 报告等)并直接在聊天中提供**持久化下载链接**
- **🖼️ 交互式伪影 (Artifacts)**: 自动渲染 Agent 生成的 HTML/JS 应用程序,直接在聊天界面交互。
---
@@ -95,7 +96,7 @@
### 1) 导入函数
1. 打开 OpenWebUI前往 **工作区** -> **函数**
2. 点击 **+** (创建函数),完整粘贴 `github_copilot_sdk_cn.py` 的内容。
2. 点击 **+** (创建函数),完整粘贴 `github_copilot_sdk.py` 的内容。
3. 点击保存并确保已启用。
### 2) 获取 Token (Get Token)
@@ -114,7 +115,7 @@
- **Agent 无法识别文件?**: 请确保已安装并启用了 Files Filter 插件,否则原始文件会被 RAG 干扰。
- **看不到 TODO 进度条?**: 进度条仅在 Agent 使用 `update_todo` 工具(通常是处理复杂任务)时出现。
- **依赖安装**: 本管道会自动尝试安装 `github-copilot-sdk` (Python 包) `github-copilot-cli` (官方二进制)
- **依赖安装**: 本管道会自动管理 `github-copilot-sdk` (Python 包) 并优先直接使用内置的二进制 CLI无需手动干预
---

View File

@@ -1,12 +1,12 @@
"""
title: GitHub Copilot Official SDK Pipe
author: Fu-Jie
author_url: https://github.com/Fu-Jie/awesome-openwebui
author_url: https://github.com/Fu-Jie/openwebui-extensions
funding_url: https://github.com/open-webui
openwebui_id: ce96f7b4-12fc-4ac3-9a01-875713e69359
description: Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.
version: 0.6.2
requirements: github-copilot-sdk==0.1.23
version: 0.7.0
requirements: github-copilot-sdk==0.1.25
"""
import os
@@ -226,10 +226,7 @@ class Pipe:
default=300,
description="Timeout for each stream chunk (seconds)",
)
COPILOT_CLI_VERSION: str = Field(
default="0.0.406",
description="Specific Copilot CLI version to install/enforce (e.g. '0.0.406'). Leave empty for latest.",
)
EXCLUDE_KEYWORDS: str = Field(
default="",
description="Exclude models containing these keywords (comma separated, e.g.: codex, haiku)",
@@ -360,6 +357,116 @@ class Pipe:
_env_setup_done = False # Track if env setup has been completed
_last_update_check = 0 # Timestamp of last CLI update check
TRANSLATIONS = {
"en-US": {
"status_conn_est": "Connection established, waiting for response...",
"status_reasoning_inj": "Reasoning Effort injected: {effort}",
"debug_agent_working_in": "Agent working in: {path}",
"debug_mcp_servers": "🔌 Connected MCP Servers: {servers}",
"publish_success": "File published successfully.",
"publish_hint_html": "Link: [View {filename}]({view_url}) | [Download]({download_url})",
"publish_hint_default": "Link: [Download {filename}]({download_url})",
},
"zh-CN": {
"status_conn_est": "已建立连接,等待响应...",
"status_reasoning_inj": "已注入推理级别:{effort}",
"debug_agent_working_in": "Agent 工作目录: {path}",
"debug_mcp_servers": "🔌 已连接 MCP 服务器: {servers}",
"publish_success": "文件发布成功。",
"publish_hint_html": "链接: [查看 {filename}]({view_url}) | [下载]({download_url})",
"publish_hint_default": "链接: [下载 {filename}]({download_url})",
},
"zh-HK": {
"status_conn_est": "已建立連接,等待響應...",
"status_reasoning_inj": "已注入推理級別:{effort}",
"debug_agent_working_in": "Agent 工作目錄: {path}",
"debug_mcp_servers": "🔌 已連接 MCP 伺服器: {servers}",
"publish_success": "文件發布成功。",
"publish_hint_html": "連結: [查看 {filename}]({view_url}) | [下載]({download_url})",
"publish_hint_default": "連結: [下載 {filename}]({download_url})",
},
"zh-TW": {
"status_conn_est": "已建立連接,等待響應...",
"status_reasoning_inj": "已注入推理級別:{effort}",
"debug_agent_working_in": "Agent 工作目錄: {path}",
"debug_mcp_servers": "🔌 已連接 MCP 伺服器: {servers}",
"publish_success": "文件發布成功。",
"publish_hint_html": "連結: [查看 {filename}]({view_url}) | [下載]({download_url})",
"publish_hint_default": "連結: [下載 {filename}]({download_url})",
},
"ja-JP": {
"status_conn_est": "接続が確立されました。応答を待っています...",
"status_reasoning_inj": "推論レベルが注入されました:{effort}",
"debug_agent_working_in": "Agent 作業ディレクトリ: {path}",
"debug_mcp_servers": "🔌 接続済み MCP サーバー: {servers}",
},
"ko-KR": {
"status_conn_est": "연결이 설정되었습니다. 응답을 기다리는 중...",
"status_reasoning_inj": "추론 수준 설정됨: {effort}",
"debug_agent_working_in": "Agent 작업 디렉토리: {path}",
"debug_mcp_servers": "🔌 연결된 MCP 서버: {servers}",
},
"fr-FR": {
"status_conn_est": "Connexion établie, en attente de réponse...",
"status_reasoning_inj": "Effort de raisonnement injecté : {effort}",
"debug_agent_working_in": "Répertoire de travail de l'Agent : {path}",
"debug_mcp_servers": "🔌 Serveurs MCP connectés : {servers}",
},
"de-DE": {
"status_conn_est": "Verbindung hergestellt, warte auf Antwort...",
"status_reasoning_inj": "Argumentationsaufwand injiziert: {effort}",
"debug_agent_working_in": "Agent-Arbeitsverzeichnis: {path}",
"debug_mcp_servers": "🔌 Verbundene MCP-Server: {servers}",
},
"es-ES": {
"status_conn_est": "Conexión establecida, esperando respuesta...",
"status_reasoning_inj": "Nivel de razonamiento inyectado: {effort}",
"debug_agent_working_in": "Directorio de trabajo del Agente: {path}",
"debug_mcp_servers": "🔌 Servidores MCP conectados: {servers}",
},
"it-IT": {
"status_conn_est": "Connessione stabilita, in attesa di risposta...",
"status_reasoning_inj": "Livello di ragionamento iniettato: {effort}",
"debug_agent_working_in": "Directory di lavoro dell'Agente: {path}",
"debug_mcp_servers": "🔌 Server MCP connessi: {servers}",
},
"ru-RU": {
"status_conn_est": "Соединение установлено, ожидание ответа...",
"status_reasoning_inj": "Уровень рассуждения внедрен: {effort}",
"debug_agent_working_in": "Рабочий каталог Агента: {path}",
"debug_mcp_servers": "🔌 Подключенные серверы MCP: {servers}",
},
"vi-VN": {
"status_conn_est": "Đã thiết lập kết nối, đang chờ phản hồi...",
"status_reasoning_inj": "Cấp độ suy luận đã được áp dụng: {effort}",
"debug_agent_working_in": "Thư mục làm việc của Agent: {path}",
"debug_mcp_servers": "🔌 Các máy chủ MCP đã kết nối: {servers}",
},
"id-ID": {
"status_conn_est": "Koneksi terjalin, menunggu respons...",
"status_reasoning_inj": "Tingkat penalaran diterapkan: {effort}",
"debug_agent_working_in": "Direktori kerja Agent: {path}",
"debug_mcp_servers": "🔌 Server MCP yang terhubung: {servers}",
},
}
FALLBACK_MAP = {
"zh": "zh-CN",
"zh-TW": "zh-TW",
"zh-HK": "zh-HK",
"en": "en-US",
"en-GB": "en-US",
"ja": "ja-JP",
"ko": "ko-KR",
"fr": "fr-FR",
"de": "de-DE",
"es": "es-ES",
"it": "it-IT",
"ru": "ru-RU",
"vi": "vi-VN",
"id": "id-ID",
}
def __init__(self):
self.type = "pipe"
self.id = "github_copilot_sdk"
@@ -390,6 +497,83 @@ class Pipe:
except Exception as e:
logger.error(f"[Database] ❌ Initialization failed: {str(e)}")
def _resolve_language(self, user_language: str) -> str:
"""Normalize user language code to a supported translation key."""
if not user_language:
return "en-US"
if user_language in self.TRANSLATIONS:
return user_language
lang_base = user_language.split("-")[0]
if user_language in self.FALLBACK_MAP:
return self.FALLBACK_MAP[user_language]
if lang_base in self.FALLBACK_MAP:
return self.FALLBACK_MAP[lang_base]
return "en-US"
def _get_translation(self, lang: str, key: str, **kwargs) -> str:
"""Helper function to get translated string for a key."""
lang_key = self._resolve_language(lang)
trans_map = self.TRANSLATIONS.get(lang_key, self.TRANSLATIONS["en-US"])
text = trans_map.get(key, self.TRANSLATIONS["en-US"].get(key, key))
if kwargs:
try:
text = text.format(**kwargs)
except Exception as e:
logger.warning(f"Translation formatting failed for {key}: {e}")
return text
async def _get_user_context(self, __user__, __event_call__=None, __request__=None):
"""Extract basic user context with safe fallbacks including JS localStorage."""
if isinstance(__user__, (list, tuple)):
user_data = __user__[0] if __user__ else {}
elif isinstance(__user__, dict):
user_data = __user__
else:
user_data = {}
user_id = user_data.get("id", "unknown_user")
user_name = user_data.get("name", "User")
user_language = user_data.get("language", "en-US")
if (
__request__
and hasattr(__request__, "headers")
and "accept-language" in __request__.headers
):
raw_lang = __request__.headers.get("accept-language", "")
if raw_lang:
user_language = raw_lang.split(",")[0].split(";")[0]
if __event_call__:
try:
js_code = """
try {
return (
document.documentElement.lang ||
localStorage.getItem('locale') ||
localStorage.getItem('language') ||
navigator.language ||
'en-US'
);
} catch (e) {
return 'en-US';
}
"""
frontend_lang = await asyncio.wait_for(
__event_call__({"type": "execute", "data": {"code": js_code}}),
timeout=2.0,
)
if frontend_lang and isinstance(frontend_lang, str):
user_language = frontend_lang
except Exception as e:
pass
return {
"user_id": user_id,
"user_name": user_name,
"user_language": user_language,
}
@contextlib.contextmanager
def _db_session(self):
"""Yield a database session using Open WebUI helpers with graceful fallbacks."""
@@ -611,6 +795,8 @@ class Pipe:
user_data = {}
user_id = user_data.get("id") or user_data.get("user_id")
user_lang = user_data.get("language") or "en-US"
is_admin = user_data.get("role") == "admin"
if not user_id:
return None
@@ -746,10 +932,7 @@ class Pipe:
dest_path = Path(UPLOAD_DIR) / f"{file_id}_{safe_filename}"
await asyncio.to_thread(shutil.copy2, target_path, dest_path)
try:
db_path = str(os.path.relpath(dest_path, DATA_DIR))
except:
db_path = str(dest_path)
db_path = str(dest_path)
file_form = FileForm(
id=file_id,
@@ -769,12 +952,37 @@ class Pipe:
# 5. Result
download_url = f"/api/v1/files/{file_id}/content"
view_url = download_url
is_html = safe_filename.lower().endswith(".html")
# For HTML files, if user is admin, provide a direct view link (/content/html)
if is_html and is_admin:
view_url = f"{download_url}/html"
# Localized output
msg = self._get_translation(user_lang, "publish_success")
if is_html and is_admin:
hint = self._get_translation(
user_lang,
"publish_hint_html",
filename=safe_filename,
view_url=view_url,
download_url=download_url,
)
else:
hint = self._get_translation(
user_lang,
"publish_hint_default",
filename=safe_filename,
download_url=download_url,
)
return {
"file_id": file_id,
"filename": safe_filename,
"download_url": download_url,
"message": "File published successfully.",
"hint": f"Link: [Download {safe_filename}]({download_url})",
"message": msg,
"hint": hint,
}
except Exception as e:
return {"error": str(e)}
@@ -1921,10 +2129,6 @@ class Pipe:
"on_post_tool_use": on_post_tool_use,
}
def _get_user_context(self):
"""Helper to get user context (placeholder for future use)."""
return {}
def _get_chat_context(
self,
body: dict,
@@ -2327,25 +2531,11 @@ class Pipe:
token: str = None,
enable_mcp: bool = True,
enable_cache: bool = True,
skip_cli_install: bool = False,
skip_cli_install: bool = False, # Kept for call-site compatibility, no longer used
__event_emitter__=None,
user_lang: str = "en-US",
):
"""Setup environment variables and verify Copilot CLI. Dynamic Token Injection."""
def emit_status_sync(description: str, done: bool = False):
if not __event_emitter__:
return
try:
loop = asyncio.get_running_loop()
loop.create_task(
__event_emitter__(
{
"type": "status",
"data": {"description": description, "done": done},
}
)
)
except Exception:
pass
"""Setup environment variables and resolve Copilot CLI path from SDK bundle."""
# 1. Real-time Token Injection (Always updates on each call)
effective_token = token or self.valves.GH_TOKEN
@@ -2353,8 +2543,6 @@ class Pipe:
os.environ["GH_TOKEN"] = os.environ["GITHUB_TOKEN"] = effective_token
if self._env_setup_done:
# If done, we only sync MCP if called explicitly or in debug mode
# To improve speed, we avoid redundant file I/O here for regular requests
if debug_enabled:
self._sync_mcp_config(
__event_call__,
@@ -2365,186 +2553,46 @@ class Pipe:
return
os.environ["COPILOT_AUTO_UPDATE"] = "false"
self._emit_debug_log_sync(
"Disabled CLI auto-update (COPILOT_AUTO_UPDATE=false)",
__event_call__,
debug_enabled=debug_enabled,
)
# 2. CLI Path Discovery
cli_path = "/usr/local/bin/copilot"
if os.environ.get("COPILOT_CLI_PATH"):
cli_path = os.environ["COPILOT_CLI_PATH"]
target_version = self.valves.COPILOT_CLI_VERSION.strip()
found = False
current_version = None
def get_cli_version(path):
try:
output = (
subprocess.check_output(
[path, "--version"], stderr=subprocess.STDOUT
)
.decode()
.strip()
)
import re
match = re.search(r"(\d+\.\d+\.\d+)", output)
return match.group(1) if match else output
except Exception:
return None
# Check existing version
if os.path.exists(cli_path):
found = True
current_version = get_cli_version(cli_path)
# 2. CLI Path Discovery (priority: env var > PATH > SDK bundle)
cli_path = os.environ.get("COPILOT_CLI_PATH", "")
found = bool(cli_path and os.path.exists(cli_path))
if not found:
sys_path = shutil.which("copilot")
if sys_path:
cli_path = sys_path
found = True
current_version = get_cli_version(cli_path)
if not found:
pkg_path = os.path.join(os.path.dirname(__file__), "bin", "copilot")
if os.path.exists(pkg_path):
cli_path = pkg_path
found = True
current_version = get_cli_version(cli_path)
# 3. Installation/Update Logic
should_install = not found
install_reason = "CLI not found"
if found and target_version:
norm_target = target_version.lstrip("v")
norm_current = current_version.lstrip("v") if current_version else ""
# Only install if target version is GREATER than current version
try:
from packaging.version import parse as parse_version
from copilot.client import _get_bundled_cli_path
if parse_version(norm_target) > parse_version(norm_current):
should_install = True
install_reason = (
f"Upgrade needed ({current_version} -> {target_version})"
)
elif parse_version(norm_target) < parse_version(norm_current):
self._emit_debug_log_sync(
f"Current version ({current_version}) is newer than specified ({target_version}). Skipping downgrade.",
__event_call__,
debug_enabled=debug_enabled,
)
except Exception as e:
# Fallback to string comparison if packaging is not available
if norm_target != norm_current:
should_install = True
install_reason = (
f"Version mismatch ({current_version} != {target_version})"
)
bundled_path = _get_bundled_cli_path()
if bundled_path and os.path.exists(bundled_path):
cli_path = bundled_path
found = True
except ImportError:
pass
if should_install and not skip_cli_install:
self._emit_debug_log_sync(
f"Installing/Updating Copilot CLI: {install_reason}...",
__event_call__,
debug_enabled=debug_enabled,
)
emit_status_sync(
"🔧 正在安装/更新 Copilot CLI首次可能需要 1-3 分钟)...",
done=False,
)
try:
env = os.environ.copy()
if target_version:
env["VERSION"] = target_version
proc = subprocess.Popen(
"curl -fsSL https://gh.io/copilot-install | bash",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env,
)
progress_percent = -1
line_count = 0
while True:
raw_line = proc.stdout.readline() if proc.stdout else ""
if raw_line == "" and proc.poll() is not None:
break
line = (raw_line or "").strip()
if not line:
continue
line_count += 1
percent_match = re.search(r"(\d{1,3})%", line)
if percent_match:
try:
pct = int(percent_match.group(1))
if pct >= progress_percent + 5:
progress_percent = pct
emit_status_sync(
f"📦 Copilot CLI 安装中:{pct}%", done=False
)
except Exception:
pass
elif line_count % 20 == 0:
emit_status_sync(
f"📦 Copilot CLI 安装中:{line[:120]}", done=False
)
return_code = proc.wait()
if return_code != 0:
raise subprocess.CalledProcessError(
return_code,
"curl -fsSL https://gh.io/copilot-install | bash",
)
# Re-verify
current_version = get_cli_version(cli_path)
emit_status_sync(
f"✅ Copilot CLI 安装完成v{current_version or target_version or 'latest'}",
done=False,
)
except Exception as e:
self._emit_debug_log_sync(
f"CLI installation failed: {e}",
__event_call__,
debug_enabled=debug_enabled,
)
emit_status_sync(
f"❌ Copilot CLI 安装失败:{str(e)[:120]}",
done=True,
)
elif should_install and skip_cli_install:
self._emit_debug_log_sync(
f"Skipping CLI install during model listing: {install_reason}",
__event_call__,
debug_enabled=debug_enabled,
)
# 4. Finalize
cli_ready = bool(cli_path and os.path.exists(cli_path))
# 3. Finalize
cli_ready = found
if cli_ready:
os.environ["COPILOT_CLI_PATH"] = cli_path
# Add the CLI's parent directory to PATH so subprocesses can invoke `copilot` directly
cli_bin_dir = os.path.dirname(cli_path)
current_path = os.environ.get("PATH", "")
if cli_bin_dir and cli_bin_dir not in current_path.split(os.pathsep):
os.environ["PATH"] = cli_bin_dir + os.pathsep + current_path
self.__class__._env_setup_done = cli_ready
self.__class__._last_update_check = datetime.now().timestamp()
self._emit_debug_log_sync(
f"Environment setup complete. CLI ready={cli_ready}. Path: {cli_path} (v{current_version})",
f"Environment setup complete. CLI ready={cli_ready}. Path: {cli_path}",
__event_call__,
debug_enabled=debug_enabled,
)
if not skip_cli_install:
if cli_ready:
emit_status_sync("✅ Copilot CLI 已就绪", done=True)
else:
emit_status_sync("⚠️ Copilot CLI 尚未就绪,请稍后重试。", done=True)
def _process_attachments(
self,
@@ -2822,6 +2870,9 @@ class Pipe:
effective_mcp = user_valves.ENABLE_MCP_SERVER
effective_cache = user_valves.ENABLE_TOOL_CACHE
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
user_lang = user_ctx["user_language"]
# 2. Setup environment with effective settings
self._setup_env(
__event_call__,
@@ -2830,11 +2881,12 @@ class Pipe:
enable_mcp=effective_mcp,
enable_cache=effective_cache,
__event_emitter__=__event_emitter__,
user_lang=user_lang,
)
cwd = self._get_workspace_dir(user_id=user_id, chat_id=chat_id)
await self._emit_debug_log(
f"Agent working in: {cwd} (Admin: {is_admin}, MCP: {effective_mcp})",
f"{self._get_translation(user_lang, 'debug_agent_working_in', path=cwd)} (Admin: {is_admin}, MCP: {effective_mcp})",
__event_call__,
debug_enabled=effective_debug,
)
@@ -3269,9 +3321,9 @@ class Pipe:
if body.get("stream", False):
init_msg = ""
if effective_debug:
init_msg = f"> [Debug] Agent working in: {self._get_workspace_dir(user_id=user_id, chat_id=chat_id)}\n"
init_msg = f"> [Debug] {self._get_translation(user_lang, 'debug_agent_working_in', path=self._get_workspace_dir(user_id=user_id, chat_id=chat_id))}\n"
if mcp_server_names:
init_msg += f"> [Debug] 🔌 Connected MCP Servers: {', '.join(mcp_server_names)}\n"
init_msg += f"> [Debug] {self._get_translation(user_lang, 'debug_mcp_servers', servers=', '.join(mcp_server_names))}\n"
# Transfer client ownership to stream_response
should_stop_client = False
@@ -3284,9 +3336,14 @@ class Pipe:
init_message=init_msg,
__event_call__=__event_call__,
__event_emitter__=__event_emitter__,
reasoning_effort=effective_reasoning_effort,
reasoning_effort=(
effective_reasoning_effort
if (is_reasoning and not is_byok_model)
else "off"
),
show_thinking=show_thinking,
debug_enabled=effective_debug,
user_lang=user_lang,
)
else:
try:
@@ -3332,6 +3389,7 @@ class Pipe:
reasoning_effort: str = "",
show_thinking: bool = True,
debug_enabled: bool = False,
user_lang: str = "en-US",
) -> AsyncGenerator:
"""
Stream response from Copilot SDK, handling various event types.
@@ -3476,14 +3534,8 @@ class Pipe:
queue.put_nowait("\n</think>\n")
state["thinking_started"] = False
# Display tool call with improved formatting
if tool_args:
tool_args_json = json.dumps(tool_args, indent=2, ensure_ascii=False)
tool_display = f"\n\n<details>\n<summary>🔧 Executing Tool: {tool_name}</summary>\n\n**Parameters:**\n\n```json\n{tool_args_json}\n```\n\n</details>\n\n"
else:
tool_display = f"\n\n<details>\n<summary>🔧 Executing Tool: {tool_name}</summary>\n\n*No parameters*\n\n</details>\n\n"
queue.put_nowait(tool_display)
# Note: We do NOT emit a done="false" card here to avoid card duplication
# (unless we have a way to update text which SSE content stream doesn't)
self._emit_debug_log_sync(
f"Tool Start: {tool_name}",
@@ -3600,31 +3652,55 @@ class Pipe:
)
# ------------------------
# Try to detect content type for better formatting
is_json = False
try:
json_obj = (
json.loads(result_content)
if isinstance(result_content, str)
else result_content
# --- Build native OpenWebUI 0.8.3 tool_calls block ---
# Serialize input args (from execution_start)
tool_args_for_block = {}
if tool_call_id and tool_call_id in active_tools:
tool_args_for_block = active_tools[tool_call_id].get(
"arguments", {}
)
if isinstance(json_obj, (dict, list)):
result_content = json.dumps(
json_obj, indent=2, ensure_ascii=False
)
is_json = True
except:
pass
# Format based on content type
if is_json:
# JSON content: use code block with syntax highlighting
result_display = f"\n<details>\n<summary>{status_icon} Tool Result: {tool_name}</summary>\n\n```json\n{result_content}\n```\n\n</details>\n\n"
else:
# Plain text: use text code block to preserve formatting and add line breaks
result_display = f"\n<details>\n<summary>{status_icon} Tool Result: {tool_name}</summary>\n\n```text\n{result_content}\n```\n\n</details>\n\n"
try:
args_json_str = json.dumps(
tool_args_for_block, ensure_ascii=False
)
except Exception:
args_json_str = "{}"
queue.put_nowait(result_display)
def escape_html_attr(s: str) -> str:
if not isinstance(s, str):
return ""
return (
str(s)
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace('"', "&quot;")
.replace("\n", "&#10;")
.replace("\r", "&#13;")
)
# MUST escape both arguments and result with &quot; and &#10; to satisfy OpenWebUI's strict regex /="([^"]*)"/
# OpenWebUI `marked` extension does not match multiline attributes properly without &#10;
args_for_attr = (
escape_html_attr(args_json_str) if args_json_str else "{}"
)
result_for_attr = escape_html_attr(result_content)
# Emit the unified native tool_calls block:
# OpenWebUI 0.8.3 frontend regex explicitly expects: name="xxx" arguments="..." result="..." done="true"
# CRITICAL: <details> tag MUST be followed immediately by \n for the frontend Markdown extension to parse it!
tool_block = (
f'\n<details type="tool_calls"'
f' id="{tool_call_id}"'
f' name="{tool_name}"'
f' arguments="{args_for_attr}"'
f' result="{result_for_attr}"'
f' done="true">\n'
f"<summary>Tool Executed</summary>\n"
f"</details>\n\n"
)
queue.put_nowait(tool_block)
elif event_type == "tool.execution_progress":
# Tool execution progress update (for long-running tools)
@@ -3725,20 +3801,42 @@ class Pipe:
# Safe initial yield with error handling
try:
if debug_enabled and show_thinking:
yield "<think>\n"
if debug_enabled and __event_emitter__:
# Emit debug info as UI status rather than reasoning block
async def _emit_status(key: str, desc: str = None, **kwargs):
try:
final_desc = (
desc
if desc
else self._get_translation(user_lang, key, **kwargs)
)
await __event_emitter__(
{
"type": "status",
"data": {"description": final_desc, "done": True},
}
)
except:
pass
if init_message:
yield init_message
for line in init_message.split("\n"):
if line.strip():
clean_msg = line.replace("> [Debug] ", "").strip()
asyncio.create_task(_emit_status("custom", desc=clean_msg))
if reasoning_effort and reasoning_effort != "off":
yield f"> [Debug] Reasoning Effort injected: {reasoning_effort.upper()}\n"
asyncio.create_task(
_emit_status(
"status_reasoning_inj", effort=reasoning_effort.upper()
)
)
yield "> [Debug] Connection established, waiting for response...\n"
state["thinking_started"] = True
asyncio.create_task(_emit_status("status_conn_est"))
except Exception as e:
# If initial yield fails, log but continue processing
self._emit_debug_log_sync(
f"Initial yield warning: {e}",
f"Initial status warning: {e}",
__event_call__,
debug_enabled=debug_enabled,
)
@@ -3766,12 +3864,21 @@ class Pipe:
except asyncio.TimeoutError:
if done.is_set():
break
if state["thinking_started"]:
if __event_emitter__ and debug_enabled:
try:
yield f"> [Debug] Waiting for response ({self.valves.TIMEOUT}s exceeded)...\n"
asyncio.create_task(
__event_emitter__(
{
"type": "status",
"data": {
"description": f"Waiting for response ({self.valves.TIMEOUT}s exceeded)...",
"done": True,
},
}
)
)
except:
# If yield fails during timeout, connection is gone
break
pass
continue
while not queue.empty():

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
# GitHub Copilot SDK Pipe v0.7.0
**GitHub Copilot SDK Pipe v0.7.0** — A major infrastructure and UX upgrade. This release eliminates manual CLI management, fully embraces OpenWebUI's native tool calling interface, and ensures seamless compatibility with the latest OpenWebUI versions.
---
## 📦 Quick Installation
- **GitHub Copilot SDK (Pipe)**: [Install v0.7.0](https://openwebui.com/posts/ce96f7b4-12fc-4ac3-9a01-875713e69359)
- **GitHub Copilot SDK (Filter)**: [Install v0.1.2](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)
---
## 🚀 What's New in v0.7.0
### 1. Zero-Maintenance CLI Integration
The most significant infrastructure change: you no longer need to worry about CLI versions or background downloads.
| Before (v0.6.x) | After (v0.7.0) |
| :--- | :--- |
| CLI installed via background `curl \| bash` | CLI bundled inside the `github-copilot-sdk` pip package |
| Version mismatches between SDK and CLI | Versions are always in sync automatically |
| Fails in restricted networks | Works everywhere `pip install` works |
**How it works**: When you install `github-copilot-sdk==0.1.25`, the matching `copilot-cli v0.0.411` is included. The plugin auto-discovers the path and injects it into the environment—zero configuration required.
### 2. Native OpenWebUI Tool Call UI
Tool calls from Copilot agents now render using **OpenWebUI's built-in tool call UI**.
- Tool execution status is displayed natively in the chat interface.
- Thinking processes (Chain of Thought) are visualized with the standard collapsible UI.
- Improved visual consistency and integration with the main OpenWebUI interface.
### 3. OpenWebUI v0.8.0+ Compatibility Fix (Bug Fix)
Resolved the **"Error getting file content"** failure that affected users on OpenWebUI v0.8.0 and later.
- **The Issue**: Relative path registration for published files was rejected by the latest OpenWebUI versions.
- **The Fix**: Switched to **absolute path registration**, restoring the ability to download generated artifacts to your local machine.
### 4. Comprehensive Multi-language Support (i18n)
Native localization for status messages and UI hints in **11 languages**:
*English, Chinese (Simp/Trad/HK/TW), Japanese, Korean, French, German, Spanish, Italian, Russian, Vietnamese, and Indonesian.*
### 5. Reasoning Status & UX Optimizations
- **Intelligent Status Display**: `Reasoning Effort injected` status is now only shown for native Copilot reasoning models.
- **Clean UI**: Removed redundant debug/status noise for BYOK and standard models.
- **Architecture Cleanup**: Refactored core setup and removed legacy installation code for a robust "one-click" experience.
---
## 🛠️ Key Capabilities
| Feature | Description |
| :--- | :--- |
| **Universal Tool Protocol** | Native support for **MCP**, **OpenAPI**, and **OpenWebUI built-in tools**. |
| **Native Tool Call UI** | Adapted to OpenWebUI's built-in tool call rendering. |
| **Workspace Isolation** | Strict sandboxing for per-session data privacy and security. |
| **Workspace Artifacts** | Agents generate files (Excel/CSV/HTML) with persistent download links via `publish_file_from_workspace`. |
| **Tool Execution** | Direct access to system binaries (Python, FFmpeg, Git, etc.). |
| **11-Language Localization** | Auto-detected, native status messages for global users. |
| **OpenWebUI v0.8.0+ Support** | Robust file handling for the latest OpenWebUI platform versions. |
---
## 📥 Import Chat Templates
- [📥 Star Prediction Chat log](https://fu-jie.github.io/awesome-openwebui/plugins/pipes/star-prediction-chat.json)
- [📥 Video Processing Chat log](https://fu-jie.github.io/awesome-openwebui/plugins/pipes/video-processing-chat.json)
*Settings -> Data -> Import Chats.*
---
## 🔗 Resources
- **GitHub Repository**: [openwebui-extensions](https://github.com/Fu-Jie/openwebui-extensions)
- **Full Changelog**: [README.md](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/pipes/github-copilot-sdk/README.md)