feat(plugins): release Copilot SDK Pipe v0.8.0 and Files Filter v0.1.3 (#50)
* feat(plugins): release copilot sdk pipe v0.8.0 and files filter v0.1.3 - Add P1~P4 conditional tool filtering and admin/server gating behavior - Fix artifact publishing reliability, strict /api file URLs, and HTML preview/download delivery - Update bilingual README/docs, release notes, and filter matching/debug improvements * fix(docs): remove duplicate code block in tool-filtering zh doc - Remove incorrectly placed duplicate 'if not is_enabled: continue' block outside code fence on line 161-163 of copilot-sdk-tool-filtering.zh.md - Addresses review comment from gemini-code-assist (#50)
This commit is contained in:
@@ -35,7 +35,7 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
|
|||||||
|
|
||||||
| Status | Plugin | Version | Downloads | Views | 📅 Updated |
|
| Status | Plugin | Version | Downloads | Views | 📅 Updated |
|
||||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
||||||
|
|
||||||
### 📈 Total Downloads Trend
|
### 📈 Total Downloads Trend
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
|||||||
|
|
||||||
| 状态 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
|
| 状态 | 插件 | 版本 | 下载 | 浏览 | 📅 更新 |
|
||||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
| 🆕 | [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) |  |  |  |  |
|
||||||
|
|
||||||
### 📈 总下载量累计趋势
|
### 📈 总下载量累计趋势
|
||||||
|
|
||||||
|
|||||||
126
docs/development/copilot-sdk-tool-filtering.md
Normal file
126
docs/development/copilot-sdk-tool-filtering.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# GitHub Copilot SDK Tool Filtering Logic Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The tool filtering logic ensures that changes made in the **OpenWebUI admin panel take effect on the very next chat message** — no restart or cache flush required. The design balances three goals: administrator control, user autonomy, and built-in feature availability.
|
||||||
|
|
||||||
|
## Priority Hierarchy
|
||||||
|
|
||||||
|
Filtering is applied top-to-bottom. A higher layer can fully block a lower one:
|
||||||
|
|
||||||
|
| Priority | Layer | Controls |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 (Highest) | **Plugin Valve toggles** | `ENABLE_OPENWEBUI_TOOLS`, `ENABLE_MCP_SERVER`, `ENABLE_OPENAPI_SERVER` — category master switches |
|
||||||
|
| 2 | **Admin backend server toggle** | Per-server `config.enable` in OpenWebUI Connections panel — blocks specific servers |
|
||||||
|
| 3 (Lowest) | **User Chat menu selection** | `tool_ids` from the chat UI — selects which enabled items to use |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Decision Logic (Flowchart)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[New message arrives] --> V{Plugin Valve enabled\nfor this category?}
|
||||||
|
V -- No --> VX[Drop all tools in this category]
|
||||||
|
V -- Yes --> B{Admin backend:\nconfig.enable = True?}
|
||||||
|
B -- No --> C[Skip this server]
|
||||||
|
B -- Yes --> F{Built-in or Custom/Server tool?}
|
||||||
|
|
||||||
|
F -- Built-in --> G{Any builtin: IDs\nselected in Chat?}
|
||||||
|
G -- Yes --> H[Enable ONLY the mapped categories\nunselected categories set to False]
|
||||||
|
G -- No --> I[Enable default 4 categories:\nweb_search, image_generation,\ncode_interpreter, memory]
|
||||||
|
|
||||||
|
F -- Custom / Server --> J{Any custom IDs\nselected in Chat?}
|
||||||
|
J -- Yes --> K[Load ONLY the selected IDs]
|
||||||
|
J -- No --> L[Load ALL admin-enabled custom tools]
|
||||||
|
|
||||||
|
H & I & K & L --> M[Always inject: publish_file_from_workspace]
|
||||||
|
M --> N[Start / Resume Copilot SDK Session]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scenario Reference Table
|
||||||
|
|
||||||
|
| User selects in Chat | Custom tools loaded | Built-in tools loaded |
|
||||||
|
|---|---|---|
|
||||||
|
| Nothing | All admin-enabled | Default 4 (search, image, code, memory) |
|
||||||
|
| Only `builtin:xxx` | All admin-enabled (unaffected) | Only selected categories |
|
||||||
|
| Only custom/server IDs | Only selected IDs | Default 4 |
|
||||||
|
| Both builtin and custom | Only selected custom IDs | Only selected builtin categories |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Implementation Details
|
||||||
|
|
||||||
|
### 1. Real-time Admin Sync (No Caching)
|
||||||
|
|
||||||
|
Every request re-reads `TOOL_SERVER_CONNECTIONS.value` live. There is **no in-memory cache** for server state. As a result:
|
||||||
|
|
||||||
|
- Enable a server in the admin panel → it appears on the **next message**.
|
||||||
|
- Disable a server → it is dropped on the **next message**.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Read live on every request — no cache
|
||||||
|
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||||
|
raw_connections = TOOL_SERVER_CONNECTIONS.value
|
||||||
|
|
||||||
|
for server in connections:
|
||||||
|
is_enabled = config.get("enable", False) # checked per-server, per-request
|
||||||
|
if not is_enabled:
|
||||||
|
continue # skipped immediately — hard block
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Built-in Tool Category Mapping
|
||||||
|
|
||||||
|
The plugin maps individual `builtin:func_name` IDs to one of 9 categories understood by `get_builtin_tools`. When the user selects specific builtins, **only those categories are enabled; unselected categories are explicitly set to `False`** (not omitted) to prevent OpenWebUI's default-`True` fallback:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if builtin_selected:
|
||||||
|
# Strict mode: set every category explicitly
|
||||||
|
for cat in all_builtin_categories: # all 9
|
||||||
|
is_enabled = cat in enabled_categories # only selected ones are True
|
||||||
|
builtin_tools_meta[cat] = is_enabled # unselected are explicitly False
|
||||||
|
else:
|
||||||
|
# Default mode: only the 4 core categories
|
||||||
|
default_builtin_categories = [
|
||||||
|
"web_search", "image_generation", "code_interpreter", "memory"
|
||||||
|
]
|
||||||
|
for cat in all_builtin_categories:
|
||||||
|
builtin_tools_meta[cat] = cat in default_builtin_categories
|
||||||
|
features.update(req_features) # merge backend feature flags
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Custom Tool "Select-All" Fallback
|
||||||
|
|
||||||
|
The whitelist is activated **only when the user explicitly selects custom/server IDs**. Selecting only `builtin:` IDs does not trigger the custom whitelist, so all admin-enabled servers remain accessible:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# custom_selected contains only non-builtin: IDs
|
||||||
|
if custom_selected:
|
||||||
|
# Whitelist active: keep only what the user picked
|
||||||
|
tool_ids = [tid for tid in available_ids if tid in custom_selected]
|
||||||
|
else:
|
||||||
|
# No custom selection: load everything enabled in backend
|
||||||
|
tool_ids = available_ids
|
||||||
|
```
|
||||||
|
|
||||||
|
The same rule applies to MCP servers in `_parse_mcp_servers`.
|
||||||
|
|
||||||
|
### 4. Admin Backend Strict Validation
|
||||||
|
|
||||||
|
Applied uniformly to both OpenAPI and MCP servers, handling both dict and Pydantic object shapes:
|
||||||
|
|
||||||
|
```python
|
||||||
|
is_enabled = False
|
||||||
|
config = server.get("config", {}) if isinstance(server, dict) else getattr(server, "config", {})
|
||||||
|
is_enabled = config.get("enable", False) if isinstance(config, dict) else getattr(config, "enable", False)
|
||||||
|
|
||||||
|
if not is_enabled:
|
||||||
|
continue # hard skip — no user or valve setting can override this
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **SDK Internal Tools**: `available_tools = None` is passed to the session so SDK-native capabilities (`read_file`, `shell`, etc.) are never accidentally blocked by the custom tool list.
|
||||||
|
- **Persistent Tool**: `publish_file_from_workspace` is always injected after all filtering — it is required for the file delivery workflow regardless of any toggle.
|
||||||
206
docs/development/copilot-sdk-tool-filtering.zh.md
Normal file
206
docs/development/copilot-sdk-tool-filtering.zh.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# GitHub Copilot SDK 工具过滤逻辑开发文档
|
||||||
|
|
||||||
|
## 核心需求
|
||||||
|
|
||||||
|
**管理员在后台修改工具服务的启用状态后,用户发送下一条消息时立即生效,无需重启服务或刷新缓存。**
|
||||||
|
|
||||||
|
过滤逻辑同时兼顾两个目标:管理员管控权、用户自主选择权。内置工具则完全独立,仅由模型配置决定。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工具分类说明
|
||||||
|
|
||||||
|
本文档涉及两类完全独立的工具,权限控制机制不同:
|
||||||
|
|
||||||
|
| 工具类型 | 说明 | 权限控制来源 |
|
||||||
|
|---|---|---|
|
||||||
|
| **内置工具(Builtin Tools)** | OpenWebUI 原生能力:时间、知识库、记忆、联网搜索、图像生成、代码解释器等 | 仅由模型配置 `meta.builtinTools` 决定,**与 Chat 前端选择无关** |
|
||||||
|
| **OpenWebUI Tools** | 用户安装的 Python 工具插件 | 插件 Valve + Chat 工具选择(tool_ids) |
|
||||||
|
| **工具服务器(OpenAPI / MCP)** | 外部 OpenAPI Server、MCP Server | 插件 Valve + 管理员 `config.enable` + `function_name_filter_list` + Chat 工具选择(tool_ids) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 内置工具权限控制(模型配置驱动,与前端无关)
|
||||||
|
|
||||||
|
内置工具**完全由模型配置决定**,Chat 界面的工具选择对其没有任何影响。
|
||||||
|
|
||||||
|
### 模型 `meta.builtinTools` 字段
|
||||||
|
|
||||||
|
在模型(自定义模型或基础模型)的 `meta` 字段中有一个可选的 `builtinTools` 对象:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"capabilities": { "builtin_tools": true },
|
||||||
|
"builtinTools": {
|
||||||
|
"time": false,
|
||||||
|
"memory": true,
|
||||||
|
"chats": true,
|
||||||
|
"notes": true,
|
||||||
|
"knowledge": true,
|
||||||
|
"channels": true,
|
||||||
|
"web_search": true,
|
||||||
|
"image_generation": true,
|
||||||
|
"code_interpreter": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**判定规则(源码 `utils/tools.py`):**
|
||||||
|
|
||||||
|
```python
|
||||||
|
def is_builtin_tool_enabled(category: str) -> bool:
|
||||||
|
builtin_tools = model.get("info", {}).get("meta", {}).get("builtinTools", {})
|
||||||
|
return builtin_tools.get(category, True) # 缺省时默认 True
|
||||||
|
```
|
||||||
|
|
||||||
|
- `builtinTools` 字段**不存在** → 所有内置工具类别默认全部开启
|
||||||
|
- `builtinTools` 字段**存在** → 仅值为 `true` 的类别开启,其余关闭
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OpenWebUI Tools 和工具服务器的优先级层级
|
||||||
|
|
||||||
|
这两类工具的过滤从上到下依次执行,**受 Chat 前端选择影响**:
|
||||||
|
|
||||||
|
| 优先级 | 层级 | 控制范围 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1(最高) | **插件 Valve 开关** | `ENABLE_OPENWEBUI_TOOLS` / `ENABLE_MCP_SERVER` / `ENABLE_OPENAPI_SERVER` — 类别总开关 |
|
||||||
|
| 2 | **管理员后端服务器开关** | OpenWebUI 连接面板中每个服务器的 `config.enable` — 控制具体服务器是否启用 |
|
||||||
|
| 3 | **管理员函数名过滤列表** | 工具服务器 `config.function_name_filter_list` — 限制该服务器对外暴露的函数列表(逗号分隔) |
|
||||||
|
| 4(最低) | **用户 Chat 工具选择** | Chat 界面的 `tool_ids` — 在已启用范围内进一步筛选:未选则全选,有选则仅选中的 |
|
||||||
|
|
||||||
|
### 管理员函数名过滤列表说明
|
||||||
|
|
||||||
|
OpenWebUI 后台的工具服务器连接配置中支持设置 `function_name_filter_list` 字段(逗号分隔的函数名),用于限制该服务器对外暴露的函数。源码逻辑:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# utils/tools.py
|
||||||
|
function_name_filter_list = tool_server_connection.get("config", {}).get("function_name_filter_list", "")
|
||||||
|
if isinstance(function_name_filter_list, str):
|
||||||
|
function_name_filter_list = function_name_filter_list.split(",")
|
||||||
|
|
||||||
|
for spec in specs:
|
||||||
|
function_name = spec["name"]
|
||||||
|
if function_name_filter_list:
|
||||||
|
if not is_string_allowed(function_name, function_name_filter_list):
|
||||||
|
continue # 不在列表中的函数被跳过
|
||||||
|
```
|
||||||
|
|
||||||
|
- 列表**为空** → 该服务器所有函数均可用
|
||||||
|
- 列表**有值** → 只有名称匹配的函数会被暴露给用户
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心判定流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[新消息到来] --> BT[内置工具:读模型 meta.builtinTools]
|
||||||
|
BT --> BT2{builtinTools 字段存在?}
|
||||||
|
BT2 -- 否 --> BT3[开启全部内置工具]
|
||||||
|
BT2 -- 是 --> BT4[仅开启值为 true 的类别]
|
||||||
|
|
||||||
|
A --> CT[OpenWebUI Tools / 工具服务器]
|
||||||
|
CT --> V{插件 Valve 开启了该类别?}
|
||||||
|
V -- 否 --> VX[丢弃该类别]
|
||||||
|
V -- 是 --> B{后端 config.enable = True?}
|
||||||
|
B -- 否 --> C[跳过该服务器]
|
||||||
|
B -- 是 --> FL{function_name_filter_list 有值?}
|
||||||
|
FL -- 是 --> FL2[过滤掉不在列表中的函数]
|
||||||
|
FL -- 否 --> J
|
||||||
|
FL2 --> J{Chat tool_ids 有勾选?}
|
||||||
|
J -- 有 --> K[仅加载勾选的 ID]
|
||||||
|
J -- 无 --> L[加载所有后台已启用工具]
|
||||||
|
|
||||||
|
BT3 & BT4 & K & L --> M[始终注入: publish_file_from_workspace]
|
||||||
|
M --> N[启动/恢复 Copilot SDK 会话]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 场景速查表
|
||||||
|
|
||||||
|
### 内置工具(与前端选择无关)
|
||||||
|
|
||||||
|
| 模型配置 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| `meta.builtinTools` 字段不存在 | 全部内置工具类别开启 |
|
||||||
|
| `meta.builtinTools` 字段存在 | 仅 `true` 的类别开启 |
|
||||||
|
|
||||||
|
### OpenWebUI Tools / 工具服务器(受 Chat 前端选择影响)
|
||||||
|
|
||||||
|
| Chat 工具选择情况 | 加载逻辑 |
|
||||||
|
|---|---|
|
||||||
|
| 什么都没选 | 加载所有 Valve 开启且后台已启用的工具(Python Tools + OpenAPI + MCP) |
|
||||||
|
| 选了部分 tool_ids | 仅加载勾选的 ID(必须同时通过 Valve 和 config.enable 校验) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 代码实现详述
|
||||||
|
|
||||||
|
### 1. 管理员后台变更即时同步
|
||||||
|
|
||||||
|
OpenWebUI 通过 `PersistentConfig` + Redis 保证多 worker 之间的配置同步,插件直接读取 `request.app.state.config.TOOL_SERVER_CONNECTIONS` 即可获取最新值:
|
||||||
|
|
||||||
|
- 后台**启用**一个服务器 → **下一条消息**就出现。
|
||||||
|
- 后台**禁用**一个服务器 → **下一条消息**就消失。
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 直接读取 OpenWebUI 的配置对象,有 Redis 时每次读取都会同步最新值
|
||||||
|
connections = request.app.state.config.TOOL_SERVER_CONNECTIONS # list
|
||||||
|
|
||||||
|
for server in connections:
|
||||||
|
config = server.get("config", {})
|
||||||
|
is_enabled = config.get("enable", False) # 每条服务器、每次请求都检查
|
||||||
|
if not is_enabled:
|
||||||
|
continue # 立即跳过,硬性拦截
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 内置工具直接透传给 OpenWebUI 处理
|
||||||
|
|
||||||
|
插件调用 OpenWebUI 的 `get_builtin_tools(request, extra_params, model)` 时,将 `model` 原样传入即可。OpenWebUI 内部会自动读取 `model.info.meta.builtinTools` 来决定哪些内置工具生效:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# OpenWebUI 源码 utils/tools.py 中的判定逻辑(开发参考,非插件代码)
|
||||||
|
def is_builtin_tool_enabled(category: str) -> bool:
|
||||||
|
builtin_tools = model.get("info", {}).get("meta", {}).get("builtinTools", {})
|
||||||
|
return builtin_tools.get(category, True) # 缺省值 True:未配置时全部开启
|
||||||
|
```
|
||||||
|
|
||||||
|
插件无需自行维护内置工具分类映射,也不需要向 `builtinTools` 注入任何值。
|
||||||
|
|
||||||
|
### 3. 自定义工具"默认全选"(白名单仅在显式勾选时激活)
|
||||||
|
|
||||||
|
白名单**只有在用户明确勾选了 tool_ids 时才启用**。未勾选任何工具时,加载所有后台已启用工具:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# tool_ids 来自 Chat 请求体
|
||||||
|
if tool_ids:
|
||||||
|
# 白名单模式:严格只保留勾选项
|
||||||
|
available_ids = [tid for tid in available_ids if tid in tool_ids]
|
||||||
|
else:
|
||||||
|
# 无勾选:加载所有后台已启用工具(免配置可用)
|
||||||
|
pass # available_ids 保持不变
|
||||||
|
```
|
||||||
|
|
||||||
|
MCP 服务器的 `_parse_mcp_servers` 遵循同样规则。
|
||||||
|
|
||||||
|
### 4. 后端状态硬校验(OpenAPI 和 MCP 统一处理)
|
||||||
|
|
||||||
|
```python
|
||||||
|
is_enabled = False
|
||||||
|
config = server.get("config", {}) if isinstance(server, dict) else getattr(server, "config", {})
|
||||||
|
is_enabled = config.get("enable", False) if isinstance(config, dict) else getattr(config, "enable", False)
|
||||||
|
|
||||||
|
if not is_enabled:
|
||||||
|
continue # 硬性跳过,任何用户或 Valve 设置都无法绕过
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- **SDK 内部工具**:向会话传 `available_tools = None`,保证 `read_file`、`shell` 等 SDK 原生能力不被自定义工具列表意外屏蔽。
|
||||||
|
- **始终注入的工具**:`publish_file_from_workspace` 在所有过滤完成后硬性追加,是文件交付工作流的必要依赖,不受任何开关影响。
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
# GitHub Copilot SDK Files Filter (v0.1.2)
|
# GitHub Copilot SDK Files Filter (v0.1.3)
|
||||||
|
|
||||||
This is a dedicated **companion filter plugin** designed specifically for the [GitHub Copilot SDK Pipe](../pipes/github-copilot-sdk.md).
|
This is a dedicated **companion filter plugin** designed specifically for the [GitHub Copilot SDK Pipe](../pipes/github-copilot-sdk.md).
|
||||||
|
|
||||||
Its core mission is to **protect user-uploaded files from being "pre-processed" by the OpenWebUI core system, ensuring that the Copilot Agent receives the raw files for autonomous analysis.**
|
Its core mission is to **protect user-uploaded files from being "pre-processed" by the OpenWebUI core system, ensuring that the Copilot Agent receives the raw files for autonomous analysis.**
|
||||||
|
|
||||||
|
## ✨ v0.1.3 Updates (What's New)
|
||||||
|
|
||||||
|
- **🔍 BYOK Model ID Matching Fixed**: Now correctly identifies models in `github_copilot_official_sdk_pipe.xxx` format via prefix matching, in addition to keyword fallback for backward compatibility. (v0.1.3)
|
||||||
|
- **🐛 Dual-channel Debug Log**: Added `show_debug_log` valve. When enabled, logs are written to both server-side logger and browser console (`console.group`). (v0.1.3)
|
||||||
|
|
||||||
## 🎯 Why is this needed?
|
## 🎯 Why is this needed?
|
||||||
|
|
||||||
In OpenWebUI's default workflow, when you upload a file (e.g., PDF, Excel, Python script), OpenWebUI automatically initiates a **RAG (Retrieval-Augmented Generation)** process: parsing the file, vectorizing it, extracting text, and injecting it into the prompt.
|
In OpenWebUI's default workflow, when you upload a file (e.g., PDF, Excel, Python script), OpenWebUI automatically initiates a **RAG (Retrieval-Augmented Generation)** process: parsing the file, vectorizing it, extracting text, and injecting it into the prompt.
|
||||||
@@ -48,6 +53,6 @@ Default settings work for most users unless you have specific needs:
|
|||||||
|
|
||||||
## ⚠️ Important Notes
|
## ⚠️ Important Notes
|
||||||
|
|
||||||
* **Must be used with Copilot SDK Pipe**: If you install this plugin without the main Pipe plugin, uploaded files will simply "disappear" (as no subsequent plugin will look for them in `copilot_files`).
|
- **Must be used with Copilot SDK Pipe**: If you install this plugin without the main Pipe plugin, uploaded files will simply "disappear" (as no subsequent plugin will look for them in `copilot_files`).
|
||||||
* **Gemini Filter Compatibility**: This plugin is fully compatible with the Gemini Multimodal Filter. As long as the priority is set correctly (This Plugin < Gemini Plugin), they can coexist without interference.
|
- **Gemini Filter Compatibility**: This plugin is fully compatible with the Gemini Multimodal Filter. As long as the priority is set correctly (This Plugin < Gemini Plugin), they can coexist without interference.
|
||||||
* **Physical File Path**: Ensure the `OPENWEBUI_UPLOAD_PATH` is correctly set in the Pipe plugin Valves for the actual file transport to work.
|
- **Physical File Path**: Ensure the `OPENWEBUI_UPLOAD_PATH` is correctly set in the Pipe plugin Valves for the actual file transport to work.
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
# GitHub Copilot SDK 文件过滤器 (v0.1.2)
|
# GitHub Copilot SDK 文件过滤器 (v0.1.3)
|
||||||
|
|
||||||
这是一个专门为 [GitHub Copilot SDK Pipe](../pipes/github-copilot-sdk.zh.md) 设计的**伴侣过滤器插件**。
|
这是一个专门为 [GitHub Copilot SDK Pipe](../pipes/github-copilot-sdk.zh.md) 设计的**伴侣过滤器插件**。
|
||||||
|
|
||||||
它的核心使命是:**保护用户上传的文件不被 OpenWebUI 核心系统“抢先处理”,确保 Copilot Agent 能够接收到原始文件并进行自主分析。**
|
它的核心使命是:**保护用户上传的文件不被 OpenWebUI 核心系统“抢先处理”,确保 Copilot Agent 能够接收到原始文件并进行自主分析。**
|
||||||
|
|
||||||
|
## ✨ 0.1.3 更新内容 (What's New)
|
||||||
|
|
||||||
|
- **🔍 BYOK 模型 ID 匹配修复**: 新增前缀匹配(`github_copilot_official_sdk_pipe.xxx` 格式),修复 BYOK 模型无法被正确识别的问题,关键词兜底保持向后兼容。(v0.1.3)
|
||||||
|
- **🐛 双通道调试日志**: 新增 `show_debug_log` 配置项,启用后同时向后端日志和浏览器控制台(`console.group`)输出调试信息。(v0.1.3)
|
||||||
|
|
||||||
## 🎯 为什么需要它?
|
## 🎯 为什么需要它?
|
||||||
|
|
||||||
在 OpenWebUI 的默认流程中,当你上传一个文件(如 PDF、Excel、Python 脚本)时,OpenWebUI 会自动启动 **RAG(检索增强生成)** 流程:解析文件、向量化、提取文本并注入到提示词中。
|
在 OpenWebUI 的默认流程中,当你上传一个文件(如 PDF、Excel、Python 脚本)时,OpenWebUI 会自动启动 **RAG(检索增强生成)** 流程:解析文件、向量化、提取文本并注入到提示词中。
|
||||||
@@ -48,6 +53,6 @@
|
|||||||
|
|
||||||
## ⚠️ 注意事项
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
* **必须配合 Copilot SDK Pipe 使用**:如果你没有安装主 Pipe 插件,本插件将导致上传的文件“凭空消失”(因为没有后续插件去 `copilot_files` 里找它们)。
|
- **必须配合 Copilot SDK Pipe 使用**:如果你没有安装主 Pipe 插件,本插件将导致上传的文件“凭空消失”(因为没有后续插件去 `copilot_files` 里找它们)。
|
||||||
* **Gemini Filter 兼容性**:本插件已完美兼容 Gemini 多模态过滤器。只要优先级设置正确(本插件 < Gemini 插件),它们可以共存互不干扰。
|
- **Gemini Filter 兼容性**:本插件已完美兼容 Gemini 多模态过滤器。只要优先级设置正确(本插件 < Gemini 插件),它们可以共存互不干扰。
|
||||||
* **物理文件路径**: 确保在 Pipe 插件的 Valves 中正确设置了 `OPENWEBUI_UPLOAD_PATH`,以便文件自动搬运功能正常工作。
|
- **物理文件路径**: 确保在 Pipe 插件的 Valves 中正确设置了 `OPENWEBUI_UPLOAD_PATH`,以便文件自动搬运功能正常工作。
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ Filters act as middleware in the message pipeline:
|
|||||||
|
|
||||||
A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It ensures the Agent receives raw files for autonomous analysis.
|
A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It ensures the Agent receives raw files for autonomous analysis.
|
||||||
|
|
||||||
**Version:** 0.1.2
|
**Version:** 0.1.3
|
||||||
|
|
||||||
[:octicons-arrow-right-24: Documentation](github-copilot-sdk-files-filter.md)
|
[:octicons-arrow-right-24: Documentation](github-copilot-sdk-files-filter.md)
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Filter 充当消息管线中的中间件:
|
|||||||
|
|
||||||
专门用于绕过 OpenWebUI 默认 RAG 机制的过滤器,针对 GitHub Copilot SDK 模型。确保 Agent 能够接收到原始文件进行自主分析。
|
专门用于绕过 OpenWebUI 默认 RAG 机制的过滤器,针对 GitHub Copilot SDK 模型。确保 Agent 能够接收到原始文件进行自主分析。
|
||||||
|
|
||||||
**版本:** 0.1.2
|
**版本:** 0.1.3
|
||||||
|
|
||||||
[:octicons-arrow-right-24: 查看文档](github-copilot-sdk-files-filter.zh.md)
|
[:octicons-arrow-right-24: 查看文档](github-copilot-sdk-files-filter.zh.md)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GitHub Copilot SDK Pipe for OpenWebUI
|
# GitHub Copilot SDK Pipe for OpenWebUI
|
||||||
|
|
||||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.7.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.8.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**.
|
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,13 +14,23 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✨ v0.7.0 Updates (What's New)
|
## ✨ v0.8.0 Updates (What's New)
|
||||||
|
|
||||||
- **🚀 Integrated CLI Management**: The Copilot CLI is now automatically managed and bundled via the `github-copilot-sdk` pip package. (v0.7.0)
|
- **🎛️ Conditional Tool Filtering (P1~P4)**: Four-priority tool permission system. **Default ON**: If no tools are selected in Chat UI (P4), all enabled tools are active. **Whitelist Mode**: Once specific tools are checked, the whitelist strictly filters both OpenWebUI tools and MCP servers. Admin-level `config.enable` (P2) allows global server disabling. (v0.8.0)
|
||||||
- **🧠 Native Tool Call UI**: Full adaptation to **OpenWebUI's native tool call UI** and thinking process visualization. (v0.7.0)
|
- **🔧 File Publish Reliability**: Fixed `Error getting file content` across all storage backends (local/S3/GCS/Azure) by using `Storage.upload_file()` directly in the fallback path. HTML files are no longer blocked by `ALLOWED_FILE_EXTENSIONS` (`?process=false` always applied). (v0.8.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)
|
- **🌐 HTML Direct Access Link**: When `publish_file_from_workspace` publishes an HTML file, the plugin also provides a directly accessible HTML link for instant in-chat preview/opening. (v0.8.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)
|
- **🔒 Strict File URL Format**: Published file links must be relative paths starting with `/api/v1/files/` (e.g., `/api/v1/files/{id}/content/html`). Do not use `api/...` and do not prepend any domain. (v0.8.0)
|
||||||
- **🧹 Architecture Cleanup**: Refactored core setup and optimized reasoning status display for a leaner experience. (v0.7.0)
|
- **🛠️ CLI Built-in Tools Always Available**: `available_tools` is now always `None`, ensuring Copilot CLI built-ins (e.g. `bash`, `create_file`) are never silently blocked regardless of MCP configuration. (v0.8.0)
|
||||||
|
- **📌 Publish Tool Always Injected**: `publish_file_from_workspace` is no longer lost when `ENABLE_OPENWEBUI_TOOLS` is disabled. (v0.8.0)
|
||||||
|
- **⚠️ Code Interpreter Limitation**: The `code_interpreter` tool runs in a remote, ephemeral environment. A system prompt warning now clarifies that it cannot access local files or persist changes. (v0.8.0)
|
||||||
|
|
||||||
|
### 🐞 Bug Fixes in v0.8.0
|
||||||
|
|
||||||
|
- Fixed `{"detail":"[ERROR: Error getting file content]"}` when publishing files under object storage backends by replacing fallback manual copy/DB writes with `Storage.upload_file()`.
|
||||||
|
- Fixed HTML artifact upload being rejected by `ALLOWED_FILE_EXTENSIONS` by always appending `?process=false` on file upload API calls.
|
||||||
|
- Fixed invalid artifact links generated as `api/...` or domain-prefixed absolute URLs; links are now constrained to `/api/v1/files/...` relative paths.
|
||||||
|
- Fixed Copilot CLI built-ins being silently unavailable when no server tools were configured/loaded (which resulted in `available_tools=[]`); now `available_tools` remains `None`.
|
||||||
|
- Fixed `publish_file_from_workspace` disappearing when `ENABLE_OPENWEBUI_TOOLS` was disabled.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -33,11 +43,23 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
|
|||||||
- **🧠 Deep Database Integration**: Real-time persistence of TOD·O lists for long-running workflows.
|
- **🧠 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.
|
- **🌊 Advanced Streaming**: Full support for thinking process/Chain of Thought visualization.
|
||||||
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support (bypasses RAG for direct binary access).
|
- **🖼️ 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.
|
- **📤 Workspace Artifacts (`publish_file_from_workspace`)**: Agents can generate files (Excel, CSV, HTML reports, etc.) and provide **persistent download links** directly in the chat. For HTML files, a direct-access HTML link is also provided.
|
||||||
- **🖼️ Interactive Artifacts**: Automatically renders HTML/JS apps generated by the agent directly in the chat interface.
|
- **🖼️ Interactive Artifacts**: Automatically renders HTML/JS apps generated by the agent directly in the chat interface.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🧩 Companion Files Filter (Required for raw files)
|
||||||
|
|
||||||
|
`GitHub Copilot SDK Files Filter` is the companion plugin that prevents OpenWebUI's default RAG pre-processing from consuming uploaded files before the Pipe receives them.
|
||||||
|
|
||||||
|
- **What it does**: Moves uploaded files to `copilot_files` so the Pipe can access raw binaries directly.
|
||||||
|
- **Why it matters**: Without it, uploaded files may be parsed/vectorized early and the Agent may lose direct raw-file access.
|
||||||
|
- **v0.1.3 highlights**:
|
||||||
|
- BYOK model-id matching fix (supports `github_copilot_official_sdk_pipe.xxx` prefixes).
|
||||||
|
- Optional dual-channel debug log (`show_debug_log`) to backend logger + browser console.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚙️ Core Configuration (Valves)
|
## ⚙️ Core Configuration (Valves)
|
||||||
|
|
||||||
### 1. Administrator Settings (Base)
|
### 1. Administrator Settings (Base)
|
||||||
@@ -105,6 +127,15 @@ If this plugin has been useful, a **Star** on [OpenWebUI Extensions](https://git
|
|||||||
2. Create **Fine-grained token**, granting **Account permissions** -> **Copilot Requests** access.
|
2. Create **Fine-grained token**, granting **Account permissions** -> **Copilot Requests** access.
|
||||||
3. Paste the generated Token into the `GH_TOKEN` field in Valves.
|
3. Paste the generated Token into the `GH_TOKEN` field in Valves.
|
||||||
|
|
||||||
|
### 3) Authentication Requirement (Mandatory)
|
||||||
|
|
||||||
|
You MUST configure **at least one** credential source:
|
||||||
|
|
||||||
|
- `GH_TOKEN` (GitHub Copilot subscription route), or
|
||||||
|
- `BYOK_API_KEY` (OpenAI/Anthropic route).
|
||||||
|
|
||||||
|
If neither is configured, the model list will not appear.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 Troubleshooting & Dependencies
|
## 📋 Troubleshooting & Dependencies
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GitHub Copilot SDK 官方管道
|
# GitHub Copilot SDK 官方管道
|
||||||
|
|
||||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 0.7.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
|
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 0.8.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 交互体验。
|
这是一个用于 [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,13 +14,23 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✨ 0.7.0 更新内容 (What's New)
|
## ✨ 0.8.0 更新内容 (What's New)
|
||||||
|
|
||||||
- **🚀 CLI 免维护集成**: Copilot CLI 现在通过 `github-copilot-sdk` pip 包自动同步管理,彻底告别手动 `curl | bash` 安装问题。(v0.7.0)
|
- **🎛️ 条件工具过滤 (P1~P4)**: 四优先级工具权限体系。**默认全开**: 若未在 Chat UI (P4) 勾选任何工具,则默认启用所有工具;**白名单模式**: 一旦勾选特定工具,即刻进入严格过滤模式,且 MCP server 同步受控;管理员亦可通过 `config.enable` (P2) 全局禁用工具服务器。(v0.8.0)
|
||||||
- **🧠 原生工具调用 UI**: 全面适配 **OpenWebUI 原生工具调用 UI** 与模型思考过程(思维链)展示。(v0.7.0)
|
- **🔧 文件发布全面修复**: 通过在回退路径直接调用 `Storage.upload_file()`,彻底修复了所有存储后端(local/S3/GCS/Azure)下的 `Error getting file content` 问题;同时上传时自动携带 `?process=false`,HTML 文件不再被 `ALLOWED_FILE_EXTENSIONS` 拦截。(v0.8.0)
|
||||||
- **🏠 OpenWebUI v0.8.0+ 兼容性修复**: 通过切换为绝对路径注册发布文件,彻底解决了“Error getting file content”无法下载到本地的问题。(v0.7.0)
|
- **🌐 HTML 直达链接**: 当 `publish_file_from_workspace` 发布的是 HTML 文件时,插件会额外提供可直接访问的 HTML 链接,便于在聊天中即时预览/打开。(v0.8.0)
|
||||||
- **🌐 全面的多语言支持**: 针对状态消息进行了 11 国语言的原生本地化 (中/英/日/韩/法/德/西/意/俄/越/印尼)。(v0.7.0)
|
- **🔒 文件链接格式严格约束**: 发布链接必须是以 `/api/v1/files/` 开头的相对路径(例如 `/api/v1/files/{id}/content/html`)。禁止使用 `api/...`,也禁止拼接任何域名。(v0.8.0)
|
||||||
- **🧹 架构精简**: 重构了初始化逻辑并优化了推理状态显示,提供更轻量稳健的体验。(v0.7.0)
|
- **🛠️ CLI 内置工具始终可用**: `available_tools` 统一设为 `None`,Copilot CLI 内置工具(如 `bash`、`create_file`)无论 MCP 配置如何都不会被静默屏蔽。(v0.8.0)
|
||||||
|
- **📌 发布工具始终注入**: 即使 `ENABLE_OPENWEBUI_TOOLS` 关闭,`publish_file_from_workspace` 工具也不再丢失。(v0.8.0)
|
||||||
|
- **⚠️ 代码解释器限制**: `code_interpreter` 工具运行在远程临时环境中。系统提示词现已包含警告,明确指出该工具无法访问本地文件或持久化更改。(v0.8.0)
|
||||||
|
|
||||||
|
### 🐞 v0.8.0 Bug 修复说明
|
||||||
|
|
||||||
|
- 修复了对象存储后端发布文件时出现的 `{"detail":"[ERROR: Error getting file content]"}`,回退路径从手动复制/写库改为 `Storage.upload_file()`。
|
||||||
|
- 修复了 HTML 产物被 `ALLOWED_FILE_EXTENSIONS` 拦截的问题,上传接口统一追加 `?process=false`。
|
||||||
|
- 修复了产物链接偶发被生成成 `api/...` 或带域名绝对 URL 的问题,现统一限制为 `/api/v1/files/...` 相对路径。
|
||||||
|
- 修复了在未配置/未加载任何 server 工具时(最终出现 `available_tools=[]`)Copilot CLI 内置工具被静默禁用的问题,现统一保持 `available_tools=None`。
|
||||||
|
- 修复了 `ENABLE_OPENWEBUI_TOOLS` 关闭时 `publish_file_from_workspace` 工具丢失的问题。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -38,6 +48,18 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🧩 配套 Files Filter(原始文件必备)
|
||||||
|
|
||||||
|
`GitHub Copilot SDK Files Filter` 是本 Pipe 的配套插件,用于阻止 OpenWebUI 默认 RAG 在 Pipe 接手前抢先处理上传文件。
|
||||||
|
|
||||||
|
- **作用**: 将上传文件移动到 `copilot_files`,让 Pipe 能直接读取原始二进制。
|
||||||
|
- **必要性**: 若未安装,文件可能被提前解析/向量化,Agent 难以拿到原始文件。
|
||||||
|
- **v0.1.3 重点**:
|
||||||
|
- 修复 BYOK 模型 ID 识别(支持 `github_copilot_official_sdk_pipe.xxx` 前缀匹配)。
|
||||||
|
- 新增双通道调试日志(`show_debug_log`):后端 logger + 浏览器控制台。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚙️ 核心配置参数 (Valves)
|
## ⚙️ 核心配置参数 (Valves)
|
||||||
|
|
||||||
### 1. 管理员配置 (基础设置)
|
### 1. 管理员配置 (基础设置)
|
||||||
@@ -105,6 +127,15 @@
|
|||||||
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 访问权限。
|
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 访问权限。
|
||||||
3. 将生成的 Token 填入插件的 `GH_TOKEN` 配置项中。
|
3. 将生成的 Token 填入插件的 `GH_TOKEN` 配置项中。
|
||||||
|
|
||||||
|
### 3) 认证配置要求(必填)
|
||||||
|
|
||||||
|
你必须至少配置以下一种凭据:
|
||||||
|
|
||||||
|
- `GH_TOKEN`(GitHub Copilot 官方订阅路径),或
|
||||||
|
- `BYOK_API_KEY`(OpenAI/Anthropic 自带 Key 路径)。
|
||||||
|
|
||||||
|
如果两者都未配置,模型列表将不会出现。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 常见问题与依赖 (Troubleshooting)
|
## 📋 常见问题与依赖 (Troubleshooting)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Pipes allow you to:
|
|||||||
|
|
||||||
## Available Pipe Plugins
|
## Available Pipe Plugins
|
||||||
|
|
||||||
- [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).
|
- [GitHub Copilot SDK](github-copilot-sdk.md) (v0.8.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: 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.
|
- **[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.
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Pipes 可以用于:
|
|||||||
|
|
||||||
## 可用的 Pipe 插件
|
## 可用的 Pipe 插件
|
||||||
|
|
||||||
- [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 Copilot SDK](github-copilot-sdk.zh.md) (v0.8.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 数据并生成详细的项目增长报告。
|
- **[实战案例:GitHub 100 Star 增长预测](star-prediction-example.zh.md)** - 展示如何使用 GitHub Copilot SDK Pipe 结合 Minimax 2.1 模型,自动编写脚本分析 CSV 数据并生成详细的项目增长报告。
|
||||||
- **[实战案例:视频高质量 GIF 转换与加速](video-processing-example.zh.md)** - 演示模型如何通过底层 FFmpeg 工具对录屏进行加速、缩放及双阶段色彩优化处理。
|
- **[实战案例:视频高质量 GIF 转换与加速](video-processing-example.zh.md)** - 演示模型如何通过底层 FFmpeg 工具对录屏进行加速、缩放及双阶段色彩优化处理。
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
# GitHub Copilot SDK Files Filter
|
# GitHub Copilot SDK Files Filter
|
||||||
|
|
||||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.1.2 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **Version:** 0.1.3 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
||||||
|
|
||||||
This is a dedicated **companion filter plugin** designed specifically for the [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4).
|
This is a dedicated **companion filter plugin** designed specifically for the [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4).
|
||||||
|
|
||||||
Its core mission is to **protect user-uploaded files from being "pre-processed" by the OpenWebUI core system, ensuring that the Copilot Agent receives the raw files for autonomous analysis.**
|
Its core mission is to **protect user-uploaded files from being "pre-processed" by the OpenWebUI core system, ensuring that the Copilot Agent receives the raw files for autonomous analysis.**
|
||||||
|
|
||||||
|
## ✨ v0.1.3 Updates (What's New)
|
||||||
|
|
||||||
|
- **🔍 BYOK Model ID Matching Fixed**: Now correctly identifies models in `github_copilot_official_sdk_pipe.xxx` format via prefix matching, in addition to keyword fallback for backward compatibility. (v0.1.3)
|
||||||
|
- **🐛 Dual-channel Debug Log**: Added `show_debug_log` valve. When enabled, logs are written to both server-side logger and browser console (`console.group`). (v0.1.3)
|
||||||
|
|
||||||
## 🎯 Why is this needed?
|
## 🎯 Why is this needed?
|
||||||
|
|
||||||
In OpenWebUI's default workflow, when you upload a file (e.g., PDF, Excel, Python script), OpenWebUI automatically initiates a **RAG (Retrieval-Augmented Generation)** process: parsing the file, vectorizing it, extracting text, and injecting it into the prompt.
|
In OpenWebUI's default workflow, when you upload a file (e.g., PDF, Excel, Python script), OpenWebUI automatically initiates a **RAG (Retrieval-Augmented Generation)** process: parsing the file, vectorizing it, extracting text, and injecting it into the prompt.
|
||||||
@@ -49,5 +54,5 @@ Default settings work for most users:
|
|||||||
|
|
||||||
## ⚠️ Important Notes
|
## ⚠️ Important Notes
|
||||||
|
|
||||||
* **Must be used with Copilot SDK Pipe**: If you install this plugin without the main Pipe plugin, uploaded files will simply "disappear" (as no subsequent plugin will look for them).
|
- **Must be used with Copilot SDK Pipe**: If you install this plugin without the main Pipe plugin, uploaded files will simply "disappear" (as no subsequent plugin will look for them).
|
||||||
* **Gemini Filter Compatibility**: Fully compatible with the Gemini Multimodal Filter. Just ensure priorities don't conflict.
|
- **Gemini Filter Compatibility**: Fully compatible with the Gemini Multimodal Filter. Just ensure priorities don't conflict.
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
# GitHub Copilot SDK 文件过滤器
|
# GitHub Copilot SDK 文件过滤器
|
||||||
|
|
||||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.1.2 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
|
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.1.3 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
|
||||||
|
|
||||||
这是一个专门为 [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) 设计的**伴侣过滤器插件**。
|
这是一个专门为 [GitHub Copilot SDK Pipe](https://openwebui.com/posts/github_copilot_official_sdk_pipe_ce96f7b4) 设计的**伴侣过滤器插件**。
|
||||||
|
|
||||||
它的核心使命是:**保护用户上传的文件不被 OpenWebUI 核心系统“抢先处理”,确保 Copilot Agent 能够接收到原始文件并进行自主分析。**
|
它的核心使命是:**保护用户上传的文件不被 OpenWebUI 核心系统“抢先处理”,确保 Copilot Agent 能够接收到原始文件并进行自主分析。**
|
||||||
|
|
||||||
|
## ✨ 0.1.3 更新内容 (What's New)
|
||||||
|
|
||||||
|
- **🔍 BYOK 模型 ID 匹配修复**: 新增前缀匹配(`github_copilot_official_sdk_pipe.xxx` 格式),修复 BYOK 模型无法被正确识别的问题,关键词兜底保持向后兼容。(v0.1.3)
|
||||||
|
- **🐛 双通道调试日志**: 新增 `show_debug_log` 配置项,启用后同时向后端日志和浏览器控制台(`console.group`)输出调试信息。(v0.1.3)
|
||||||
|
|
||||||
## 🎯 为什么需要它?
|
## 🎯 为什么需要它?
|
||||||
|
|
||||||
在 OpenWebUI 的默认流程中,当你上传一个文件(如 PDF、Excel、Python 脚本)时,OpenWebUI 会自动启动 **RAG(检索增强生成)** 流程:解析文件、向量化、提取文本并注入到提示词中。
|
在 OpenWebUI 的默认流程中,当你上传一个文件(如 PDF、Excel、Python 脚本)时,OpenWebUI 会自动启动 **RAG(检索增强生成)** 流程:解析文件、向量化、提取文本并注入到提示词中。
|
||||||
@@ -47,5 +52,5 @@
|
|||||||
|
|
||||||
## ⚠️ 注意事项
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
* **必须配合 Copilot SDK Pipe 使用**:如果你没有安装主 Pipe 插件,本插件将导致上传的文件“凭空消失”。
|
- **必须配合 Copilot SDK Pipe 使用**:如果你没有安装主 Pipe 插件,本插件将导致上传的文件“凭空消失”。
|
||||||
* **Gemini Filter 兼容性**:已完美兼容 Gemini 多模态过滤器。只要优先级设置正确,它们可以共存互不干扰。
|
- **Gemini Filter 兼容性**:已完美兼容 Gemini 多模态过滤器。只要优先级设置正确,它们可以共存互不干扰。
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ id: github_copilot_sdk_files_filter
|
|||||||
author: Fu-Jie
|
author: Fu-Jie
|
||||||
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
funding_url: https://github.com/open-webui
|
funding_url: https://github.com/open-webui
|
||||||
version: 0.1.2
|
version: 0.1.3
|
||||||
openwebui_id: 403a62ee-a596-45e7-be65-fab9cc249dd6
|
openwebui_id: 403a62ee-a596-45e7-be65-fab9cc249dd6
|
||||||
description: A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It moves uploaded files to a safe location ('copilot_files') so the Copilot Pipe can process them natively without interference.
|
description: A specialized filter to bypass OpenWebUI's default RAG for GitHub Copilot SDK models. It moves uploaded files to a safe location ('copilot_files') so the Copilot Pipe can process them natively without interference.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import Optional, Callable, Awaitable
|
from typing import Optional, Callable, Awaitable
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Filter:
|
class Filter:
|
||||||
@@ -23,10 +28,62 @@ class Filter:
|
|||||||
default="copilot_sdk",
|
default="copilot_sdk",
|
||||||
description="Keyword to identify Copilot models (e.g., 'copilot_sdk').",
|
description="Keyword to identify Copilot models (e.g., 'copilot_sdk').",
|
||||||
)
|
)
|
||||||
|
target_model_prefixes: str = Field(
|
||||||
|
default="github_copilot_official_sdk_pipe.,github_copilot_sdk_pipe.",
|
||||||
|
description="Comma-separated model id prefixes to identify Copilot SDK models.",
|
||||||
|
)
|
||||||
|
show_debug_log: bool = Field(
|
||||||
|
default=False,
|
||||||
|
description="Whether to print model matching debug logs in backend console.",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.valves = self.Valves()
|
self.valves = self.Valves()
|
||||||
|
|
||||||
|
def _is_copilot_model(self, model_id: str) -> bool:
|
||||||
|
if not isinstance(model_id, str) or not model_id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
current = model_id.strip().lower()
|
||||||
|
if not current:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 1) Prefix match (most reliable for OpenWebUI model id formats)
|
||||||
|
raw_prefixes = self.valves.target_model_prefixes or ""
|
||||||
|
prefixes = [p.strip().lower() for p in raw_prefixes.split(",") if p.strip()]
|
||||||
|
if any(current.startswith(prefix) for prefix in prefixes):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 2) Keyword fallback for backward compatibility
|
||||||
|
keyword = (self.valves.target_model_keyword or "").strip().lower()
|
||||||
|
return bool(keyword and keyword in current)
|
||||||
|
|
||||||
|
async def _emit_debug_log(
|
||||||
|
self,
|
||||||
|
__event_emitter__: Optional[Callable[[dict], Awaitable[None]]],
|
||||||
|
title: str,
|
||||||
|
data: dict,
|
||||||
|
):
|
||||||
|
if not self.valves.show_debug_log:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("[Copilot Files Filter] %s: %s", title, data)
|
||||||
|
|
||||||
|
if not __event_emitter__:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
js_code = f"""
|
||||||
|
(async function() {{
|
||||||
|
console.group('🧩 Copilot Files Filter: {title}');
|
||||||
|
console.log({json.dumps(data, ensure_ascii=False)});
|
||||||
|
console.groupEnd();
|
||||||
|
}})();
|
||||||
|
"""
|
||||||
|
await __event_emitter__({"type": "execute", "data": {"code": js_code}})
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug("[Copilot Files Filter] frontend debug emit failed: %s", e)
|
||||||
|
|
||||||
async def inlet(
|
async def inlet(
|
||||||
self,
|
self,
|
||||||
body: dict,
|
body: dict,
|
||||||
@@ -45,8 +102,30 @@ class Filter:
|
|||||||
|
|
||||||
current_model = base_model_id if base_model_id else body.get("model", "")
|
current_model = base_model_id if base_model_id else body.get("model", "")
|
||||||
|
|
||||||
|
await self._emit_debug_log(
|
||||||
|
__event_emitter__,
|
||||||
|
"model-debug",
|
||||||
|
{
|
||||||
|
"body_model": body.get("model", ""),
|
||||||
|
"base_model_id": base_model_id,
|
||||||
|
"current_model": current_model,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# Check if it's a Copilot model
|
# Check if it's a Copilot model
|
||||||
if self.valves.target_model_keyword.lower() in current_model.lower():
|
is_copilot_model = self._is_copilot_model(current_model)
|
||||||
|
|
||||||
|
await self._emit_debug_log(
|
||||||
|
__event_emitter__,
|
||||||
|
"match-result",
|
||||||
|
{
|
||||||
|
"is_copilot_model": is_copilot_model,
|
||||||
|
"prefixes": self.valves.target_model_prefixes,
|
||||||
|
"keyword": self.valves.target_model_keyword,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_copilot_model:
|
||||||
# If files exist, move them to 'copilot_files' and clear 'files'
|
# If files exist, move them to 'copilot_files' and clear 'files'
|
||||||
# This prevents OpenWebUI from triggering RAG on these files
|
# This prevents OpenWebUI from triggering RAG on these files
|
||||||
if "files" in body and body["files"]:
|
if "files" in body and body["files"]:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GitHub Copilot SDK Pipe for OpenWebUI
|
# GitHub Copilot SDK Pipe for OpenWebUI
|
||||||
|
|
||||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.7.0 | **Project:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **License:** MIT
|
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 0.8.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**.
|
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,13 +14,23 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✨ v0.7.0 Updates (What's New)
|
## ✨ v0.8.0 Updates (What's New)
|
||||||
|
|
||||||
- **🚀 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)
|
- **🎛️ Conditional Tool Filtering (P1~P4)**: Four-priority tool permission system. **Default ON**: If no tools are selected in Chat UI (P4), all enabled tools are active. **Whitelist Mode**: Once specific tools are checked, the whitelist strictly filters both OpenWebUI tools and MCP servers. Admin-level `config.enable` (P2) allows global server disabling. (v0.8.0)
|
||||||
- **🧠 Native Tool Call UI**: Full adaptation to **OpenWebUI's native tool call UI** and thinking process visualization. (v0.7.0)
|
- **🔧 File Publish Reliability**: Fixed `Error getting file content` across all storage backends (local/S3/GCS/Azure) by using `Storage.upload_file()` directly in the fallback path. HTML files are no longer blocked by `ALLOWED_FILE_EXTENSIONS` (`?process=false` always applied). (v0.8.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)
|
- **🌐 HTML Direct Access Link**: When `publish_file_from_workspace` publishes an HTML file, the plugin also provides a directly accessible HTML link for instant in-chat preview/opening. (v0.8.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)
|
- **🔒 Strict File URL Format**: Published file links must be relative paths starting with `/api/v1/files/` (e.g., `/api/v1/files/{id}/content/html`). Do not use `api/...` and do not prepend any domain. (v0.8.0)
|
||||||
- **🧹 Architecture Cleanup**: Refactored core setup and optimized reasoning status display for a leaner experience. (v0.7.0)
|
- **🛠️ CLI Built-in Tools Always Available**: `available_tools` is now always `None`, ensuring Copilot CLI built-ins (e.g. `bash`, `create_file`) are never silently blocked regardless of MCP configuration. (v0.8.0)
|
||||||
|
- **📌 Publish Tool Always Injected**: `publish_file_from_workspace` is no longer lost when `ENABLE_OPENWEBUI_TOOLS` is disabled. (v0.8.0)
|
||||||
|
- **⚠️ Code Interpreter Limitation**: The `code_interpreter` tool runs in a remote, ephemeral environment. A system prompt warning now clarifies that it cannot access local files or persist changes. (v0.8.0)
|
||||||
|
|
||||||
|
### 🐞 Bug Fixes in v0.8.0
|
||||||
|
|
||||||
|
- Fixed `{"detail":"[ERROR: Error getting file content]"}` when publishing files under object storage backends by replacing fallback manual copy/DB writes with `Storage.upload_file()`.
|
||||||
|
- Fixed HTML artifact upload being rejected by `ALLOWED_FILE_EXTENSIONS` by always appending `?process=false` on file upload API calls.
|
||||||
|
- Fixed invalid artifact links generated as `api/...` or domain-prefixed absolute URLs; links are now constrained to `/api/v1/files/...` relative paths.
|
||||||
|
- Fixed Copilot CLI built-ins being silently unavailable when no server tools were configured/loaded (which resulted in `available_tools=[]`); now `available_tools` remains `None`.
|
||||||
|
- Fixed `publish_file_from_workspace` disappearing when `ENABLE_OPENWEBUI_TOOLS` was disabled.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -33,11 +43,23 @@ This is an advanced Pipe function for [OpenWebUI](https://github.com/open-webui/
|
|||||||
- **🧠 Deep Database Integration**: Real-time persistence of TOD·O lists for long-running workflows.
|
- **🧠 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.
|
- **🌊 Advanced Streaming**: Full support for thinking process/Chain of Thought visualization.
|
||||||
- **🖼️ Intelligent Multimodal**: Vision capabilities and raw file analysis support (bypasses RAG for direct binary access).
|
- **🖼️ 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.
|
- **📤 Workspace Artifacts (`publish_file_from_workspace`)**: Agents can generate files (Excel, CSV, HTML reports, etc.) and provide **persistent download links** directly in the chat. For HTML files, a direct-access HTML link is also provided.
|
||||||
- **🖼️ Interactive Artifacts**: Automatically renders HTML/JS apps generated by the agent directly in the chat interface.
|
- **🖼️ Interactive Artifacts**: Automatically renders HTML/JS apps generated by the agent directly in the chat interface.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🧩 Companion Files Filter (Required for raw files)
|
||||||
|
|
||||||
|
`GitHub Copilot SDK Files Filter` is the companion plugin that prevents OpenWebUI's default RAG pre-processing from consuming uploaded files before the Pipe receives them.
|
||||||
|
|
||||||
|
- **What it does**: Moves uploaded files to `copilot_files` so the Pipe can access raw binaries directly.
|
||||||
|
- **Why it matters**: Without it, uploaded files may be parsed/vectorized early and the Agent may lose direct raw-file access.
|
||||||
|
- **v0.1.3 highlights**:
|
||||||
|
- BYOK model-id matching fix (supports `github_copilot_official_sdk_pipe.xxx` prefixes).
|
||||||
|
- Optional dual-channel debug log (`show_debug_log`) to backend logger + browser console.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚙️ Core Configuration (Valves)
|
## ⚙️ Core Configuration (Valves)
|
||||||
|
|
||||||
### 1. Administrator Settings (Base)
|
### 1. Administrator Settings (Base)
|
||||||
@@ -105,6 +127,15 @@ If this plugin has been useful, a **Star** on [OpenWebUI Extensions](https://git
|
|||||||
2. Create **Fine-grained token**, granting **Account permissions** -> **Copilot Requests** access.
|
2. Create **Fine-grained token**, granting **Account permissions** -> **Copilot Requests** access.
|
||||||
3. Paste the generated Token into the `GH_TOKEN` field in Valves.
|
3. Paste the generated Token into the `GH_TOKEN` field in Valves.
|
||||||
|
|
||||||
|
### 3) Authentication Requirement (Mandatory)
|
||||||
|
|
||||||
|
You MUST configure **at least one** credential source:
|
||||||
|
|
||||||
|
- `GH_TOKEN` (GitHub Copilot subscription route), or
|
||||||
|
- `BYOK_API_KEY` (OpenAI/Anthropic route).
|
||||||
|
|
||||||
|
If neither is configured, the model list will not appear.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 Troubleshooting & Dependencies
|
## 📋 Troubleshooting & Dependencies
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GitHub Copilot SDK 官方管道
|
# GitHub Copilot SDK 官方管道
|
||||||
|
|
||||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.7.0 | **项目:** [OpenWebUI Extensions](https://github.com/Fu-Jie/openwebui-extensions) | **许可证:** MIT
|
**作者:** [Fu-Jie](https://github.com/Fu-Jie/openwebui-extensions) | **版本:** 0.8.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 交互体验。
|
这是一个用于 [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,13 +14,23 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✨ 0.7.0 更新内容 (What's New)
|
## ✨ 0.8.0 更新内容 (What's New)
|
||||||
|
|
||||||
- **🚀 CLI 免维护集成**: Copilot CLI 现在通过 `github-copilot-sdk` pip 包自动同步管理,彻底告别手动 `curl | bash` 安装及版本不匹配问题。(v0.7.0)
|
- **🎛️ 条件工具过滤 (P1~P4)**: 四优先级工具权限体系。**默认全开**: 若未在 Chat UI (P4) 勾选任何工具,则默认启用所有工具;**白名单模式**: 一旦勾选特定工具,即刻进入严格过滤模式,且 MCP server 同步受控;管理员亦可通过 `config.enable` (P2) 全局禁用工具服务器。(v0.8.0)
|
||||||
- **🧠 原生工具调用 UI**: 全面适配 **OpenWebUI 原生工具调用 UI** 与模型思考过程(思维链)展示。(v0.7.0)
|
- **🔧 文件发布全面修复**: 通过在回退路径直接调用 `Storage.upload_file()`,彻底修复了所有存储后端(local/S3/GCS/Azure)下的 `Error getting file content` 问题;同时上传时自动携带 `?process=false`,HTML 文件不再被 `ALLOWED_FILE_EXTENSIONS` 拦截。(v0.8.0)
|
||||||
- **🏠 OpenWebUI v0.8.0+ 兼容性修复**: 通过切换为绝对路径注册发布文件,彻底解决了“Error getting file content”无法下载到本地的问题。(v0.7.0)
|
- **🌐 HTML 直达链接**: 当 `publish_file_from_workspace` 发布的是 HTML 文件时,插件会额外提供可直接访问的 HTML 链接,便于在聊天中即时预览/打开。(v0.8.0)
|
||||||
- **🌐 全面的多语言支持**: 针对状态消息进行了 11 国语言的原生本地化 (中/英/日/韩/法/德/西/意/俄/越/印尼)。(v0.7.0)
|
- **🔒 文件链接格式严格约束**: 发布链接必须是以 `/api/v1/files/` 开头的相对路径(例如 `/api/v1/files/{id}/content/html`)。禁止使用 `api/...`,也禁止拼接任何域名。(v0.8.0)
|
||||||
- **🧹 架构精简**: 重构了初始化逻辑并优化了推理状态显示,提供更轻量稳健的体验。(v0.7.0)
|
- **🛠️ CLI 内置工具始终可用**: `available_tools` 统一设为 `None`,Copilot CLI 内置工具(如 `bash`、`create_file`)无论 MCP 配置如何都不会被静默屏蔽。(v0.8.0)
|
||||||
|
- **📌 发布工具始终注入**: 即使 `ENABLE_OPENWEBUI_TOOLS` 关闭,`publish_file_from_workspace` 工具也不再丢失。(v0.8.0)
|
||||||
|
- **⚠️ 代码解释器限制**: `code_interpreter` 工具运行在远程临时环境中。系统提示词现已包含警告,明确指出该工具无法访问本地文件或持久化更改。(v0.8.0)
|
||||||
|
|
||||||
|
### 🐞 v0.8.0 Bug 修复说明
|
||||||
|
|
||||||
|
- 修复了对象存储后端发布文件时出现的 `{"detail":"[ERROR: Error getting file content]"}`,回退路径从手动复制/写库改为 `Storage.upload_file()`。
|
||||||
|
- 修复了 HTML 产物被 `ALLOWED_FILE_EXTENSIONS` 拦截的问题,上传接口统一追加 `?process=false`。
|
||||||
|
- 修复了产物链接偶发被生成成 `api/...` 或带域名绝对 URL 的问题,现统一限制为 `/api/v1/files/...` 相对路径。
|
||||||
|
- 修复了在未配置/未加载任何 server 工具时(最终出现 `available_tools=[]`)Copilot CLI 内置工具被静默禁用的问题,现统一保持 `available_tools=None`。
|
||||||
|
- 修复了 `ENABLE_OPENWEBUI_TOOLS` 关闭时 `publish_file_from_workspace` 工具丢失的问题。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -33,11 +43,23 @@
|
|||||||
- **🧠 深度数据库集成**: 实时持久化 TOD·O 列表到 UI 进度条。
|
- **🧠 深度数据库集成**: 实时持久化 TOD·O 列表到 UI 进度条。
|
||||||
- **🌊 深度推理展示**: 完整支持模型思考过程 (Thinking Process) 的流式渲染。
|
- **🌊 深度推理展示**: 完整支持模型思考过程 (Thinking Process) 的流式渲染。
|
||||||
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析(绕过 RAG 直接访问原始二进制内容)。
|
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析(绕过 RAG 直接访问原始二进制内容)。
|
||||||
- **📤 工作区产物工具 (`publish_file_from_workspace`)**: Agent 可生成文件(Excel、CSV、HTML 报告等)并直接在聊天中提供**持久化下载链接**。
|
- **📤 工作区产物工具 (`publish_file_from_workspace`)**: Agent 可生成文件(Excel、CSV、HTML 报告等)并直接在聊天中提供**持久化下载链接**。若为 HTML 文件,还会额外提供可直接访问的 HTML 链接。
|
||||||
- **🖼️ 交互式伪影 (Artifacts)**: 自动渲染 Agent 生成的 HTML/JS 应用程序,直接在聊天界面交互。
|
- **🖼️ 交互式伪影 (Artifacts)**: 自动渲染 Agent 生成的 HTML/JS 应用程序,直接在聊天界面交互。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🧩 配套 Files Filter(原始文件必备)
|
||||||
|
|
||||||
|
`GitHub Copilot SDK Files Filter` 是本 Pipe 的配套插件,用于阻止 OpenWebUI 默认 RAG 在 Pipe 接手前抢先处理上传文件。
|
||||||
|
|
||||||
|
- **作用**: 将上传文件移动到 `copilot_files`,让 Pipe 能直接读取原始二进制。
|
||||||
|
- **必要性**: 若未安装,文件可能被提前解析/向量化,Agent 难以拿到原始文件。
|
||||||
|
- **v0.1.3 重点**:
|
||||||
|
- 修复 BYOK 模型 ID 识别(支持 `github_copilot_official_sdk_pipe.xxx` 前缀匹配)。
|
||||||
|
- 新增双通道调试日志(`show_debug_log`):后端 logger + 浏览器控制台。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚙️ 核心配置参数 (Valves)
|
## ⚙️ 核心配置参数 (Valves)
|
||||||
|
|
||||||
### 1. 管理员配置 (基础设置)
|
### 1. 管理员配置 (基础设置)
|
||||||
@@ -105,7 +127,16 @@
|
|||||||
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 访问权限。
|
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 访问权限。
|
||||||
3. 将生成的 Token 填入插件的 `GH_TOKEN` 配置项中。
|
3. 将生成的 Token 填入插件的 `GH_TOKEN` 配置项中。
|
||||||
|
|
||||||
### 3) 配套插件 (强烈推荐)
|
### 3) 认证配置要求(必填)
|
||||||
|
|
||||||
|
你必须至少配置以下一种凭据:
|
||||||
|
|
||||||
|
- `GH_TOKEN`(GitHub Copilot 官方订阅路径),或
|
||||||
|
- `BYOK_API_KEY`(OpenAI/Anthropic 自带 Key 路径)。
|
||||||
|
|
||||||
|
如果两者都未配置,模型列表将不会出现。
|
||||||
|
|
||||||
|
### 4) 配套插件 (强烈推荐)
|
||||||
|
|
||||||
为了获得最佳的文件处理体验,请安装 [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)。
|
为了获得最佳的文件处理体验,请安装 [GitHub Copilot SDK Files Filter](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)。
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ author_url: https://github.com/Fu-Jie/openwebui-extensions
|
|||||||
funding_url: https://github.com/open-webui
|
funding_url: https://github.com/open-webui
|
||||||
openwebui_id: ce96f7b4-12fc-4ac3-9a01-875713e69359
|
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.
|
description: Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.
|
||||||
version: 0.7.0
|
version: 0.8.0
|
||||||
requirements: github-copilot-sdk==0.1.25
|
requirements: github-copilot-sdk==0.1.25
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -17,11 +17,9 @@ import tempfile
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import contextlib
|
import contextlib
|
||||||
import traceback
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Union, AsyncGenerator, List, Any, Dict, Literal, Tuple
|
from typing import Optional, Union, AsyncGenerator, List, Any, Dict, Literal, Tuple
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
@@ -46,6 +44,7 @@ from open_webui.models.tools import Tools
|
|||||||
from open_webui.models.users import Users
|
from open_webui.models.users import Users
|
||||||
from open_webui.models.files import Files, FileForm
|
from open_webui.models.files import Files, FileForm
|
||||||
from open_webui.config import UPLOAD_DIR, DATA_DIR
|
from open_webui.config import UPLOAD_DIR, DATA_DIR
|
||||||
|
from open_webui.storage.provider import Storage
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import uuid
|
import uuid
|
||||||
import shutil
|
import shutil
|
||||||
@@ -138,7 +137,8 @@ BASE_GUIDELINES = (
|
|||||||
" - **Philosophy**: Visual Artifacts (HTML/Mermaid) and Downloadable Files are **COMPLEMENTARY**. Always aim to provide BOTH: instant visual insight in the chat AND a persistent file for the user to keep.\n"
|
" - **Philosophy**: Visual Artifacts (HTML/Mermaid) and Downloadable Files are **COMPLEMENTARY**. Always aim to provide BOTH: instant visual insight in the chat AND a persistent file for the user to keep.\n"
|
||||||
" - **The Rule**: When the user needs to *possess* data (download/export), you MUST publish it. Creating a local file alone is useless because the user cannot access your container.\n"
|
" - **The Rule**: When the user needs to *possess* data (download/export), you MUST publish it. Creating a local file alone is useless because the user cannot access your container.\n"
|
||||||
" - **Implicit Requests**: If asked to 'export', 'get link', or 'save', automatically trigger this sequence.\n"
|
" - **Implicit Requests**: If asked to 'export', 'get link', or 'save', automatically trigger this sequence.\n"
|
||||||
" - **Execution Sequence**: 1. **Write Local**: Create file in `.` (current directory). 2. **Publish**: Call `publish_file_from_workspace(filename='your_file.ext')`. 3. **Link**: Present the `download_url` as a Markdown link.\n"
|
" - **Execution Sequence**: 1. **Write Local**: Create file in `.` (current directory). 2. **Publish**: Call `publish_file_from_workspace(filename='your_file.ext')`. 3. **Link**: Present the result based on file type. **For HTML files**: the tool returns both `download_url` (raw file) and `view_url` (`/content/html` format). You MUST present `view_url` as a **[Preview]** link that opens directly in the browser, AND `download_url` as a **[Download]** link. **For image files** (.png, .jpg, .gif, .svg, etc.): embed directly using `` — NEVER use a text link for images. For all other files: present `download_url` as a single download link.\n"
|
||||||
|
" - **URL Format is STRICT**: File links MUST be relative paths starting with `/api/v1/files/` (for example: `/api/v1/files/{id}/content` or `/api/v1/files/{id}/content/html`). NEVER output `api/...` (missing leading slash). NEVER prepend any domain (such as `https://example.com/...`) even if a domain appears in conversation context.\n"
|
||||||
" - **Bypass RAG**: This protocol automatically handles S3 storage and bypasses RAG, ensuring 100% accurate data delivery.\n"
|
" - **Bypass RAG**: This protocol automatically handles S3 storage and bypasses RAG, ensuring 100% accurate data delivery.\n"
|
||||||
"6. **TODO Visibility**: Every time you call the `update_todo` tool, you **MUST** immediately follow up with a beautifully formatted **Markdown summary** of the current TODO list. Use task checkboxes (`- [ ]`), progress indicators, and clear headings so the user can see the status directly in the chat.\n"
|
"6. **TODO Visibility**: Every time you call the `update_todo` tool, you **MUST** immediately follow up with a beautifully formatted **Markdown summary** of the current TODO list. Use task checkboxes (`- [ ]`), progress indicators, and clear headings so the user can see the status directly in the chat.\n"
|
||||||
"7. **Python Execution Standard**: For ANY task requiring Python logic (not just data analysis), you **MUST NOT** embed multi-line code directly in a shell command (e.g., using `python -c` or `<< 'EOF'`).\n"
|
"7. **Python Execution Standard**: For ANY task requiring Python logic (not just data analysis), you **MUST NOT** embed multi-line code directly in a shell command (e.g., using `python -c` or `<< 'EOF'`).\n"
|
||||||
@@ -197,10 +197,6 @@ class Pipe:
|
|||||||
default=True,
|
default=True,
|
||||||
description="Enable Direct MCP Client connection (Recommended).",
|
description="Enable Direct MCP Client connection (Recommended).",
|
||||||
)
|
)
|
||||||
ENABLE_TOOL_CACHE: bool = Field(
|
|
||||||
default=True,
|
|
||||||
description="Cache OpenWebUI tools and MCP servers (performance optimization).",
|
|
||||||
)
|
|
||||||
REASONING_EFFORT: Literal["low", "medium", "high", "xhigh"] = Field(
|
REASONING_EFFORT: Literal["low", "medium", "high", "xhigh"] = Field(
|
||||||
default="medium",
|
default="medium",
|
||||||
description="Reasoning effort level (low, medium, high). Only affects standard Copilot models (not BYOK).",
|
description="Reasoning effort level (low, medium, high). Only affects standard Copilot models (not BYOK).",
|
||||||
@@ -251,6 +247,10 @@ class Pipe:
|
|||||||
default="/app/backend/data/uploads",
|
default="/app/backend/data/uploads",
|
||||||
description="Path to OpenWebUI uploads directory (for file processing).",
|
description="Path to OpenWebUI uploads directory (for file processing).",
|
||||||
)
|
)
|
||||||
|
MODEL_CACHE_TTL: int = Field(
|
||||||
|
default=3600,
|
||||||
|
description="Model list cache TTL in seconds. Set to 0 to disable cache (always fetch). Default: 3600 (1 hour).",
|
||||||
|
)
|
||||||
|
|
||||||
BYOK_TYPE: Literal["openai", "anthropic"] = Field(
|
BYOK_TYPE: Literal["openai", "anthropic"] = Field(
|
||||||
default="openai",
|
default="openai",
|
||||||
@@ -314,10 +314,6 @@ class Pipe:
|
|||||||
default=True,
|
default=True,
|
||||||
description="Enable dynamic MCP server loading (overrides global).",
|
description="Enable dynamic MCP server loading (overrides global).",
|
||||||
)
|
)
|
||||||
ENABLE_TOOL_CACHE: bool = Field(
|
|
||||||
default=True,
|
|
||||||
description="Enable Tool/MCP configuration caching for this user.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# BYOK User Overrides
|
# BYOK User Overrides
|
||||||
BYOK_API_KEY: str = Field(
|
BYOK_API_KEY: str = Field(
|
||||||
@@ -352,8 +348,7 @@ class Pipe:
|
|||||||
_model_cache: List[dict] = [] # Model list cache
|
_model_cache: List[dict] = [] # Model list cache
|
||||||
_standard_model_ids: set = set() # Track standard model IDs
|
_standard_model_ids: set = set() # Track standard model IDs
|
||||||
_last_byok_config_hash: str = "" # Track BYOK config for cache invalidation
|
_last_byok_config_hash: str = "" # Track BYOK config for cache invalidation
|
||||||
_tool_cache = None # Cache for converted OpenWebUI tools
|
_last_model_cache_time: float = 0 # Timestamp of last model cache refresh
|
||||||
_mcp_server_cache = None # Cache for MCP server config
|
|
||||||
_env_setup_done = False # Track if env setup has been completed
|
_env_setup_done = False # Track if env setup has been completed
|
||||||
_last_update_check = 0 # Timestamp of last CLI update check
|
_last_update_check = 0 # Timestamp of last CLI update check
|
||||||
|
|
||||||
@@ -717,46 +712,32 @@ class Pipe:
|
|||||||
uv = self._get_user_valves(__user__)
|
uv = self._get_user_valves(__user__)
|
||||||
enable_tools = uv.ENABLE_OPENWEBUI_TOOLS
|
enable_tools = uv.ENABLE_OPENWEBUI_TOOLS
|
||||||
enable_openapi = uv.ENABLE_OPENAPI_SERVER
|
enable_openapi = uv.ENABLE_OPENAPI_SERVER
|
||||||
enable_cache = uv.ENABLE_TOOL_CACHE
|
|
||||||
|
|
||||||
# 2. If all tool types are disabled, return empty immediately
|
# 2. Publish tool is always injected, regardless of other settings
|
||||||
if not enable_tools and not enable_openapi:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# 3. Check Cache
|
|
||||||
if enable_cache and self._tool_cache is not None:
|
|
||||||
await self._emit_debug_log(
|
|
||||||
"ℹ️ Using cached OpenWebUI tools.", __event_call__
|
|
||||||
)
|
|
||||||
# Create a shallow copy to append user-specific tools without polluting cache
|
|
||||||
tools = list(self._tool_cache)
|
|
||||||
|
|
||||||
# Inject File Publish Tool
|
|
||||||
chat_ctx = self._get_chat_context(body, __metadata__)
|
chat_ctx = self._get_chat_context(body, __metadata__)
|
||||||
chat_id = chat_ctx.get("chat_id")
|
chat_id = chat_ctx.get("chat_id")
|
||||||
file_tool = self._get_publish_file_tool(__user__, chat_id, __request__)
|
file_tool = self._get_publish_file_tool(__user__, chat_id, __request__)
|
||||||
if file_tool:
|
final_tools = [file_tool] if file_tool else []
|
||||||
tools.append(file_tool)
|
|
||||||
|
|
||||||
return tools
|
# 3. If all OpenWebUI tool types are disabled, skip loading and return early
|
||||||
|
if not enable_tools and not enable_openapi:
|
||||||
|
return final_tools
|
||||||
|
|
||||||
# Load OpenWebUI tools dynamically
|
# 4. Extract chat-level tool selection (P4: user selection from Chat UI)
|
||||||
|
chat_tool_ids = None
|
||||||
|
if __metadata__ and isinstance(__metadata__, dict):
|
||||||
|
chat_tool_ids = __metadata__.get("tool_ids") or None
|
||||||
|
|
||||||
|
# 5. Load OpenWebUI tools dynamically (always fresh, no cache)
|
||||||
openwebui_tools = await self._load_openwebui_tools(
|
openwebui_tools = await self._load_openwebui_tools(
|
||||||
body=body,
|
body=body,
|
||||||
__user__=__user__,
|
__user__=__user__,
|
||||||
__event_call__=__event_call__,
|
__event_call__=__event_call__,
|
||||||
enable_tools=enable_tools,
|
enable_tools=enable_tools,
|
||||||
enable_openapi=enable_openapi,
|
enable_openapi=enable_openapi,
|
||||||
|
chat_tool_ids=chat_tool_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update Cache
|
|
||||||
if enable_cache:
|
|
||||||
self._tool_cache = openwebui_tools
|
|
||||||
await self._emit_debug_log(
|
|
||||||
"✅ OpenWebUI tools cached for subsequent requests.", __event_call__
|
|
||||||
)
|
|
||||||
|
|
||||||
# Log details only when cache is cold
|
|
||||||
if openwebui_tools:
|
if openwebui_tools:
|
||||||
tool_names = [t.name for t in openwebui_tools]
|
tool_names = [t.name for t in openwebui_tools]
|
||||||
await self._emit_debug_log(
|
await self._emit_debug_log(
|
||||||
@@ -770,15 +751,7 @@ class Pipe:
|
|||||||
__event_call__,
|
__event_call__,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create a shallow copy to append user-specific tools without polluting cache
|
final_tools.extend(openwebui_tools)
|
||||||
final_tools = list(openwebui_tools)
|
|
||||||
|
|
||||||
# Inject File Publish Tool
|
|
||||||
chat_ctx = self._get_chat_context(body, __metadata__)
|
|
||||||
chat_id = chat_ctx.get("chat_id")
|
|
||||||
file_tool = self._get_publish_file_tool(__user__, chat_id, __request__)
|
|
||||||
if file_tool:
|
|
||||||
final_tools.append(file_tool)
|
|
||||||
|
|
||||||
return final_tools
|
return final_tools
|
||||||
|
|
||||||
@@ -892,7 +865,9 @@ class Pipe:
|
|||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
base_url = str(__request__.base_url).rstrip("/")
|
base_url = str(__request__.base_url).rstrip("/")
|
||||||
upload_url = f"{base_url}/api/v1/files/"
|
# ?process=false skips RAG processing AND the
|
||||||
|
# ALLOWED_FILE_EXTENSIONS restriction (which blocks html/htm)
|
||||||
|
upload_url = f"{base_url}/api/v1/files/?process=false"
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
with open(target_path, "rb") as f:
|
with open(target_path, "rb") as f:
|
||||||
@@ -925,14 +900,25 @@ class Pipe:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"API upload failed: {e}")
|
logger.error(f"API upload failed: {e}")
|
||||||
|
|
||||||
# 4. Fallback: Manual DB Insert (Local only)
|
# 4. Fallback: Use Storage.upload_file directly (S3/Local/GCS/Azure compatible)
|
||||||
if not api_success:
|
if not api_success:
|
||||||
file_id = str(uuid.uuid4())
|
file_id = str(uuid.uuid4())
|
||||||
safe_filename = target_path.name
|
safe_filename = target_path.name
|
||||||
dest_path = Path(UPLOAD_DIR) / f"{file_id}_{safe_filename}"
|
storage_filename = f"{file_id}_{safe_filename}"
|
||||||
await asyncio.to_thread(shutil.copy2, target_path, dest_path)
|
|
||||||
|
|
||||||
db_path = str(dest_path)
|
def _upload_via_storage():
|
||||||
|
with open(target_path, "rb") as f:
|
||||||
|
_, stored_path = Storage.upload_file(
|
||||||
|
f,
|
||||||
|
storage_filename,
|
||||||
|
{
|
||||||
|
"OpenWebUI-User-Id": user_id,
|
||||||
|
"OpenWebUI-File-Id": file_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return stored_path
|
||||||
|
|
||||||
|
db_path = await asyncio.to_thread(_upload_via_storage)
|
||||||
|
|
||||||
file_form = FileForm(
|
file_form = FileForm(
|
||||||
id=file_id,
|
id=file_id,
|
||||||
@@ -943,7 +929,7 @@ class Pipe:
|
|||||||
"name": safe_filename,
|
"name": safe_filename,
|
||||||
"content_type": mimetypes.guess_type(safe_filename)[0]
|
"content_type": mimetypes.guess_type(safe_filename)[0]
|
||||||
or "text/plain",
|
or "text/plain",
|
||||||
"size": os.path.getsize(dest_path),
|
"size": os.path.getsize(target_path),
|
||||||
"source": "copilot_workspace_publish",
|
"source": "copilot_workspace_publish",
|
||||||
"skip_rag": True,
|
"skip_rag": True,
|
||||||
},
|
},
|
||||||
@@ -952,16 +938,16 @@ class Pipe:
|
|||||||
|
|
||||||
# 5. Result
|
# 5. Result
|
||||||
download_url = f"/api/v1/files/{file_id}/content"
|
download_url = f"/api/v1/files/{file_id}/content"
|
||||||
view_url = download_url
|
is_html = safe_filename.lower().endswith((".html", ".htm"))
|
||||||
is_html = safe_filename.lower().endswith(".html")
|
|
||||||
|
|
||||||
# For HTML files, if user is admin, provide a direct view link (/content/html)
|
# For HTML files, provide a direct view link (/content/html) for browser preview
|
||||||
if is_html and is_admin:
|
view_url = None
|
||||||
|
if is_html:
|
||||||
view_url = f"{download_url}/html"
|
view_url = f"{download_url}/html"
|
||||||
|
|
||||||
# Localized output
|
# Localized output
|
||||||
msg = self._get_translation(user_lang, "publish_success")
|
msg = self._get_translation(user_lang, "publish_success")
|
||||||
if is_html and is_admin:
|
if is_html:
|
||||||
hint = self._get_translation(
|
hint = self._get_translation(
|
||||||
user_lang,
|
user_lang,
|
||||||
"publish_hint_html",
|
"publish_hint_html",
|
||||||
@@ -977,13 +963,16 @@ class Pipe:
|
|||||||
download_url=download_url,
|
download_url=download_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
"file_id": file_id,
|
"file_id": file_id,
|
||||||
"filename": safe_filename,
|
"filename": safe_filename,
|
||||||
"download_url": download_url,
|
"download_url": download_url,
|
||||||
"message": msg,
|
"message": msg,
|
||||||
"hint": hint,
|
"hint": hint,
|
||||||
}
|
}
|
||||||
|
if is_html and view_url:
|
||||||
|
result["view_url"] = view_url
|
||||||
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|
||||||
@@ -1206,6 +1195,31 @@ class Pipe:
|
|||||||
params_type=ParamsModel,
|
params_type=ParamsModel,
|
||||||
)(_tool)
|
)(_tool)
|
||||||
|
|
||||||
|
def _read_tool_server_connections(self) -> list:
|
||||||
|
"""
|
||||||
|
Read tool server connections directly from the database to avoid stale
|
||||||
|
in-memory state in multi-worker deployments.
|
||||||
|
Falls back to the in-memory PersistentConfig value if DB read fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from open_webui.config import get_config
|
||||||
|
|
||||||
|
config_data = get_config()
|
||||||
|
connections = config_data.get("tool_server", {}).get("connections", None)
|
||||||
|
if connections is not None:
|
||||||
|
return connections if isinstance(connections, list) else []
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(
|
||||||
|
f"[Tools] DB config read failed, using in-memory fallback: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fallback: in-memory value (may be stale in multi-worker)
|
||||||
|
if hasattr(TOOL_SERVER_CONNECTIONS, "value") and isinstance(
|
||||||
|
TOOL_SERVER_CONNECTIONS.value, list
|
||||||
|
):
|
||||||
|
return TOOL_SERVER_CONNECTIONS.value
|
||||||
|
return []
|
||||||
|
|
||||||
def _build_openwebui_request(self, user: dict = None, token: str = None):
|
def _build_openwebui_request(self, user: dict = None, token: str = None):
|
||||||
"""Build a more complete request-like object with dynamically loaded OpenWebUI configs."""
|
"""Build a more complete request-like object with dynamically loaded OpenWebUI configs."""
|
||||||
# Dynamically build config from the official registry
|
# Dynamically build config from the official registry
|
||||||
@@ -1219,12 +1233,9 @@ class Pipe:
|
|||||||
setattr(config, item.env_name, val)
|
setattr(config, item.env_name, val)
|
||||||
|
|
||||||
# Critical Fix: Explicitly sync TOOL_SERVER_CONNECTIONS to ensure OpenAPI tools work
|
# Critical Fix: Explicitly sync TOOL_SERVER_CONNECTIONS to ensure OpenAPI tools work
|
||||||
# PERSISTENT_CONFIG_REGISTRY might not contain TOOL_SERVER_CONNECTIONS in all versions
|
# Read directly from DB to avoid stale in-memory state in multi-worker deployments
|
||||||
if not hasattr(config, "TOOL_SERVER_CONNECTIONS"):
|
fresh_connections = self._read_tool_server_connections()
|
||||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
config.TOOL_SERVER_CONNECTIONS = fresh_connections
|
||||||
config.TOOL_SERVER_CONNECTIONS = TOOL_SERVER_CONNECTIONS.value
|
|
||||||
else:
|
|
||||||
config.TOOL_SERVER_CONNECTIONS = TOOL_SERVER_CONNECTIONS
|
|
||||||
|
|
||||||
app_state = SimpleNamespace(
|
app_state = SimpleNamespace(
|
||||||
config=config,
|
config=config,
|
||||||
@@ -1282,6 +1293,7 @@ class Pipe:
|
|||||||
__event_call__=None,
|
__event_call__=None,
|
||||||
enable_tools: bool = True,
|
enable_tools: bool = True,
|
||||||
enable_openapi: bool = True,
|
enable_openapi: bool = True,
|
||||||
|
chat_tool_ids: Optional[list] = None,
|
||||||
):
|
):
|
||||||
"""Load OpenWebUI tools and convert them to Copilot SDK tools."""
|
"""Load OpenWebUI tools and convert them to Copilot SDK tools."""
|
||||||
if isinstance(__user__, (list, tuple)):
|
if isinstance(__user__, (list, tuple)):
|
||||||
@@ -1300,17 +1312,20 @@ class Pipe:
|
|||||||
|
|
||||||
# --- PROBE LOG ---
|
# --- PROBE LOG ---
|
||||||
if __event_call__:
|
if __event_call__:
|
||||||
conn_status = "Missing"
|
conn_list = self._read_tool_server_connections()
|
||||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
conn_summary = []
|
||||||
val = TOOL_SERVER_CONNECTIONS.value
|
for i, s in enumerate(conn_list):
|
||||||
conn_status = (
|
if isinstance(s, dict):
|
||||||
f"List({len(val)})" if isinstance(val, list) else str(type(val))
|
s_id = s.get("info", {}).get("id") or s.get("id") or str(i)
|
||||||
|
s_type = s.get("type", "openapi")
|
||||||
|
s_enabled = s.get("config", {}).get("enable", False)
|
||||||
|
conn_summary.append(
|
||||||
|
{"id": s_id, "type": s_type, "enable": s_enabled}
|
||||||
)
|
)
|
||||||
|
|
||||||
await self._emit_debug_log(
|
await self._emit_debug_log(
|
||||||
f"[Tools Debug] Entry. UserID: {user_id}, EnableTools: {enable_tools}, EnableOpenAPI: {enable_openapi}, Connections: {conn_status}",
|
f"[Tools] TOOL_SERVER_CONNECTIONS ({len(conn_summary)} entries): {conn_summary}",
|
||||||
__event_call__,
|
__event_call__,
|
||||||
debug_enabled=True,
|
|
||||||
)
|
)
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
@@ -1327,8 +1342,7 @@ class Pipe:
|
|||||||
|
|
||||||
# 2. Get OpenAPI Tool Server tools
|
# 2. Get OpenAPI Tool Server tools
|
||||||
if enable_openapi:
|
if enable_openapi:
|
||||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
raw_connections = self._read_tool_server_connections()
|
||||||
raw_connections = TOOL_SERVER_CONNECTIONS.value
|
|
||||||
|
|
||||||
# Handle Pydantic model vs List vs Dict
|
# Handle Pydantic model vs List vs Dict
|
||||||
connections = []
|
connections = []
|
||||||
@@ -1354,6 +1368,25 @@ class Pipe:
|
|||||||
else getattr(server, "type", "openapi")
|
else getattr(server, "type", "openapi")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# P2: config.enable check — skip admin-disabled servers
|
||||||
|
s_config = (
|
||||||
|
server.get("config", {})
|
||||||
|
if isinstance(server, dict)
|
||||||
|
else getattr(server, "config", {})
|
||||||
|
)
|
||||||
|
s_enabled = (
|
||||||
|
s_config.get("enable", False)
|
||||||
|
if isinstance(s_config, dict)
|
||||||
|
else getattr(s_config, "enable", False)
|
||||||
|
)
|
||||||
|
if not s_enabled:
|
||||||
|
if self.valves.DEBUG:
|
||||||
|
await self._emit_debug_log(
|
||||||
|
f"[Tools] Skipped disabled server at index {idx}",
|
||||||
|
__event_call__,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
# Handle server ID: Priority info.id > server.id > index
|
# Handle server ID: Priority info.id > server.id > index
|
||||||
s_id = None
|
s_id = None
|
||||||
if isinstance(server, dict):
|
if isinstance(server, dict):
|
||||||
@@ -1397,6 +1430,16 @@ class Pipe:
|
|||||||
)
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
# P4: Chat tool_ids whitelist — only active when user explicitly selected tools
|
||||||
|
if chat_tool_ids:
|
||||||
|
chat_tool_ids_set = set(chat_tool_ids)
|
||||||
|
filtered = [tid for tid in tool_ids if tid in chat_tool_ids_set]
|
||||||
|
await self._emit_debug_log(
|
||||||
|
f"[Tools] tool_ids whitelist active: {len(tool_ids)} → {len(filtered)} (selected: {chat_tool_ids})",
|
||||||
|
__event_call__,
|
||||||
|
)
|
||||||
|
tool_ids = filtered
|
||||||
|
|
||||||
if self.valves.DEBUG and tool_ids:
|
if self.valves.DEBUG and tool_ids:
|
||||||
await self._emit_debug_log(
|
await self._emit_debug_log(
|
||||||
f"[Tools] Requesting tool IDs: {tool_ids}", __event_call__
|
f"[Tools] Requesting tool IDs: {tool_ids}", __event_call__
|
||||||
@@ -1469,7 +1512,28 @@ class Pipe:
|
|||||||
# Fetch Built-in Tools (Web Search, Memory, etc.)
|
# Fetch Built-in Tools (Web Search, Memory, etc.)
|
||||||
if enable_tools:
|
if enable_tools:
|
||||||
try:
|
try:
|
||||||
|
# Resolve real model dict from DB to respect meta.builtinTools config
|
||||||
|
model_dict = {}
|
||||||
|
model_id = body.get("model", "") if isinstance(body, dict) else ""
|
||||||
|
if model_id:
|
||||||
|
try:
|
||||||
|
from open_webui.models.models import Models as _Models
|
||||||
|
|
||||||
|
model_record = _Models.get_model_by_id(model_id)
|
||||||
|
if model_record:
|
||||||
|
model_dict = {"info": model_record.model_dump()}
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Get builtin tools
|
# Get builtin tools
|
||||||
|
# Open all feature gates so filtering is driven solely by
|
||||||
|
# model.meta.builtinTools (defaults to all-enabled when absent).
|
||||||
|
all_features = {
|
||||||
|
"memory": True,
|
||||||
|
"web_search": True,
|
||||||
|
"image_generation": True,
|
||||||
|
"code_interpreter": True,
|
||||||
|
}
|
||||||
builtin_tools = get_builtin_tools(
|
builtin_tools = get_builtin_tools(
|
||||||
self._build_openwebui_request(user_data),
|
self._build_openwebui_request(user_data),
|
||||||
{
|
{
|
||||||
@@ -1477,16 +1541,8 @@ class Pipe:
|
|||||||
"__chat_id__": extra_params.get("__chat_id__"),
|
"__chat_id__": extra_params.get("__chat_id__"),
|
||||||
"__message_id__": extra_params.get("__message_id__"),
|
"__message_id__": extra_params.get("__message_id__"),
|
||||||
},
|
},
|
||||||
model={
|
features=all_features,
|
||||||
"info": {
|
model=model_dict, # model.meta.builtinTools controls which categories are active
|
||||||
"meta": {
|
|
||||||
"capabilities": {
|
|
||||||
"web_search": True,
|
|
||||||
"image_generation": True,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, # Mock capabilities to allow all globally enabled tools
|
|
||||||
)
|
)
|
||||||
if builtin_tools:
|
if builtin_tools:
|
||||||
tools_dict.update(builtin_tools)
|
tools_dict.update(builtin_tools)
|
||||||
@@ -1504,9 +1560,8 @@ class Pipe:
|
|||||||
tool_metadata_cache = {}
|
tool_metadata_cache = {}
|
||||||
server_metadata_cache = {}
|
server_metadata_cache = {}
|
||||||
|
|
||||||
# Pre-build server metadata cache from TOOL_SERVER_CONNECTIONS
|
# Pre-build server metadata cache from DB-fresh tool server connections
|
||||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
for server in self._read_tool_server_connections():
|
||||||
for server in TOOL_SERVER_CONNECTIONS.value:
|
|
||||||
server_id = server.get("id") or server.get("info", {}).get("id")
|
server_id = server.get("id") or server.get("info", {}).get("id")
|
||||||
if server_id:
|
if server_id:
|
||||||
info = server.get("info", {})
|
info = server.get("info", {})
|
||||||
@@ -1578,7 +1633,10 @@ class Pipe:
|
|||||||
return converted_tools
|
return converted_tools
|
||||||
|
|
||||||
def _parse_mcp_servers(
|
def _parse_mcp_servers(
|
||||||
self, __event_call__=None, enable_mcp: bool = True, enable_cache: bool = True
|
self,
|
||||||
|
__event_call__=None,
|
||||||
|
enable_mcp: bool = True,
|
||||||
|
chat_tool_ids: Optional[list] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
Dynamically load MCP servers from OpenWebUI TOOL_SERVER_CONNECTIONS.
|
Dynamically load MCP servers from OpenWebUI TOOL_SERVER_CONNECTIONS.
|
||||||
@@ -1587,17 +1645,22 @@ class Pipe:
|
|||||||
if not enable_mcp:
|
if not enable_mcp:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Check Cache
|
|
||||||
if enable_cache and self._mcp_server_cache is not None:
|
|
||||||
return self._mcp_server_cache
|
|
||||||
|
|
||||||
mcp_servers = {}
|
mcp_servers = {}
|
||||||
|
|
||||||
# Iterate over OpenWebUI Tool Server Connections
|
# Read MCP servers directly from DB to avoid stale in-memory cache
|
||||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
connections = self._read_tool_server_connections()
|
||||||
connections = TOOL_SERVER_CONNECTIONS.value
|
|
||||||
else:
|
if __event_call__:
|
||||||
connections = []
|
mcp_summary = []
|
||||||
|
for s in connections if isinstance(connections, list) else []:
|
||||||
|
if isinstance(s, dict) and s.get("type") == "mcp":
|
||||||
|
s_id = s.get("info", {}).get("id") or s.get("id", "?")
|
||||||
|
s_enabled = s.get("config", {}).get("enable", False)
|
||||||
|
mcp_summary.append({"id": s_id, "enable": s_enabled})
|
||||||
|
self._emit_debug_log_sync(
|
||||||
|
f"[MCP] TOOL_SERVER_CONNECTIONS MCP entries ({len(mcp_summary)}): {mcp_summary}",
|
||||||
|
__event_call__,
|
||||||
|
)
|
||||||
|
|
||||||
for conn in connections:
|
for conn in connections:
|
||||||
if conn.get("type") == "mcp":
|
if conn.get("type") == "mcp":
|
||||||
@@ -1605,6 +1668,18 @@ class Pipe:
|
|||||||
# Use ID from info or generate one
|
# Use ID from info or generate one
|
||||||
raw_id = info.get("id", f"mcp-server-{len(mcp_servers)}")
|
raw_id = info.get("id", f"mcp-server-{len(mcp_servers)}")
|
||||||
|
|
||||||
|
# P2: config.enable check — skip admin-disabled servers
|
||||||
|
mcp_config = conn.get("config", {})
|
||||||
|
if not mcp_config.get("enable", False):
|
||||||
|
self._emit_debug_log_sync(
|
||||||
|
f"[MCP] Skipped disabled server: {raw_id}", __event_call__
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# P4: chat_tool_ids whitelist — if user selected tools, only include matching servers
|
||||||
|
if chat_tool_ids and f"server:{raw_id}" not in chat_tool_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
# Sanitize server_id (using same logic as tools)
|
# Sanitize server_id (using same logic as tools)
|
||||||
server_id = re.sub(r"[^a-zA-Z0-9_-]", "_", raw_id)
|
server_id = re.sub(r"[^a-zA-Z0-9_-]", "_", raw_id)
|
||||||
if not server_id or re.match(r"^[_.-]+$", server_id):
|
if not server_id or re.match(r"^[_.-]+$", server_id):
|
||||||
@@ -1636,7 +1711,6 @@ class Pipe:
|
|||||||
headers.update(custom_headers)
|
headers.update(custom_headers)
|
||||||
|
|
||||||
# Get filtering configuration
|
# Get filtering configuration
|
||||||
mcp_config = conn.get("config", {})
|
|
||||||
function_filter = mcp_config.get("function_name_filter_list", "")
|
function_filter = mcp_config.get("function_name_filter_list", "")
|
||||||
|
|
||||||
allowed_tools = ["*"]
|
allowed_tools = ["*"]
|
||||||
@@ -1658,10 +1732,6 @@ class Pipe:
|
|||||||
f"🔌 MCP Integrated: {server_id}", __event_call__
|
f"🔌 MCP Integrated: {server_id}", __event_call__
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update Cache
|
|
||||||
if self.valves.ENABLE_TOOL_CACHE:
|
|
||||||
self._mcp_server_cache = mcp_servers
|
|
||||||
|
|
||||||
return mcp_servers if mcp_servers else None
|
return mcp_servers if mcp_servers else None
|
||||||
|
|
||||||
async def _emit_debug_log(
|
async def _emit_debug_log(
|
||||||
@@ -1899,6 +1969,22 @@ class Pipe:
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Append Code Interpreter Warning
|
||||||
|
code_interpreter_warning = (
|
||||||
|
"\n\n[System Note]\n"
|
||||||
|
"The `execute_code` tool (builtin category: `code_interpreter`) executes code in a remote, ephemeral environment. "
|
||||||
|
"It cannot access files in your local workspace or persist changes. "
|
||||||
|
"Use it only for calculation or logic verification, not for file manipulation."
|
||||||
|
"\n"
|
||||||
|
"For links returned by `publish_file_from_workspace`, URL formatting is strict: "
|
||||||
|
"always use relative paths that start with `/api/v1/files/`. "
|
||||||
|
"Do not output `api/...` and do not prepend any domain."
|
||||||
|
)
|
||||||
|
if system_prompt_content:
|
||||||
|
system_prompt_content += code_interpreter_warning
|
||||||
|
else:
|
||||||
|
system_prompt_content = code_interpreter_warning.strip()
|
||||||
|
|
||||||
return system_prompt_content, system_prompt_source
|
return system_prompt_content, system_prompt_source
|
||||||
|
|
||||||
def _get_workspace_dir(self, user_id: str = None, chat_id: str = None) -> str:
|
def _get_workspace_dir(self, user_id: str = None, chat_id: str = None) -> str:
|
||||||
@@ -1969,7 +2055,7 @@ class Pipe:
|
|||||||
is_admin: bool = False,
|
is_admin: bool = False,
|
||||||
user_id: str = None,
|
user_id: str = None,
|
||||||
enable_mcp: bool = True,
|
enable_mcp: bool = True,
|
||||||
enable_cache: bool = True,
|
chat_tool_ids: Optional[list] = None,
|
||||||
__event_call__=None,
|
__event_call__=None,
|
||||||
):
|
):
|
||||||
"""Build SessionConfig for Copilot SDK."""
|
"""Build SessionConfig for Copilot SDK."""
|
||||||
@@ -2018,7 +2104,7 @@ class Pipe:
|
|||||||
}
|
}
|
||||||
|
|
||||||
mcp_servers = self._parse_mcp_servers(
|
mcp_servers = self._parse_mcp_servers(
|
||||||
__event_call__, enable_mcp=enable_mcp, enable_cache=enable_cache
|
__event_call__, enable_mcp=enable_mcp, chat_tool_ids=chat_tool_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
# Prepare session config parameters
|
# Prepare session config parameters
|
||||||
@@ -2061,12 +2147,12 @@ class Pipe:
|
|||||||
|
|
||||||
if mcp_servers:
|
if mcp_servers:
|
||||||
session_params["mcp_servers"] = mcp_servers
|
session_params["mcp_servers"] = mcp_servers
|
||||||
# Critical Fix: When using MCP, available_tools must be None to allow dynamic discovery
|
|
||||||
|
# Always set available_tools=None so the Copilot CLI's built-in tools
|
||||||
|
# (e.g. bash, create_file) remain accessible alongside our custom tools.
|
||||||
|
# Custom tools are registered via the 'tools' param; whitelist filtering
|
||||||
|
# via available_tools would silently block CLI built-ins.
|
||||||
session_params["available_tools"] = None
|
session_params["available_tools"] = None
|
||||||
else:
|
|
||||||
session_params["available_tools"] = (
|
|
||||||
[t.name for t in custom_tools] if custom_tools else None
|
|
||||||
)
|
|
||||||
|
|
||||||
if provider_config:
|
if provider_config:
|
||||||
session_params["provider"] = provider_config
|
session_params["provider"] = provider_config
|
||||||
@@ -2338,7 +2424,7 @@ class Pipe:
|
|||||||
|
|
||||||
# 1. Environment Setup (Only if needed or not done)
|
# 1. Environment Setup (Only if needed or not done)
|
||||||
if needs_setup:
|
if needs_setup:
|
||||||
self._setup_env(token=token, skip_cli_install=True)
|
self._setup_env(token=token)
|
||||||
self.__class__._last_update_check = now
|
self.__class__._last_update_check = now
|
||||||
else:
|
else:
|
||||||
# Still inject token for BYOK real-time updates
|
# Still inject token for BYOK real-time updates
|
||||||
@@ -2380,6 +2466,19 @@ class Pipe:
|
|||||||
current_config_str = f"{token}|{uv.BYOK_BASE_URL or self.valves.BYOK_BASE_URL}|{uv.BYOK_API_KEY or self.valves.BYOK_API_KEY}|{self.valves.BYOK_BEARER_TOKEN}"
|
current_config_str = f"{token}|{uv.BYOK_BASE_URL or self.valves.BYOK_BASE_URL}|{uv.BYOK_API_KEY or self.valves.BYOK_API_KEY}|{self.valves.BYOK_BEARER_TOKEN}"
|
||||||
current_config_hash = hashlib.md5(current_config_str.encode()).hexdigest()
|
current_config_hash = hashlib.md5(current_config_str.encode()).hexdigest()
|
||||||
|
|
||||||
|
# TTL-based cache expiry
|
||||||
|
cache_ttl = self.valves.MODEL_CACHE_TTL
|
||||||
|
if (
|
||||||
|
self._model_cache
|
||||||
|
and cache_ttl > 0
|
||||||
|
and (now - self.__class__._last_model_cache_time) > cache_ttl
|
||||||
|
):
|
||||||
|
if self.valves.DEBUG:
|
||||||
|
logger.info(
|
||||||
|
f"[Pipes] Model cache expired (TTL={cache_ttl}s). Invalidating."
|
||||||
|
)
|
||||||
|
self.__class__._model_cache = []
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self._model_cache
|
self._model_cache
|
||||||
and self.__class__._last_byok_config_hash != current_config_hash
|
and self.__class__._last_byok_config_hash != current_config_hash
|
||||||
@@ -2397,8 +2496,12 @@ class Pipe:
|
|||||||
if self.valves.DEBUG:
|
if self.valves.DEBUG:
|
||||||
logger.info("[Pipes] Refreshing model cache...")
|
logger.info("[Pipes] Refreshing model cache...")
|
||||||
try:
|
try:
|
||||||
# Use effective token for fetching
|
# Use effective token for fetching.
|
||||||
self._setup_env(token=token, skip_cli_install=True)
|
# If COPILOT_CLI_PATH is missing (e.g. env cleared after worker restart),
|
||||||
|
# force a full re-discovery by resetting _env_setup_done first.
|
||||||
|
if not os.environ.get("COPILOT_CLI_PATH"):
|
||||||
|
self.__class__._env_setup_done = False
|
||||||
|
self._setup_env(token=token)
|
||||||
|
|
||||||
# Fetch BYOK models if configured
|
# Fetch BYOK models if configured
|
||||||
byok = []
|
byok = []
|
||||||
@@ -2478,7 +2581,24 @@ class Pipe:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self._model_cache = standard + byok
|
self._model_cache = standard + byok
|
||||||
|
self.__class__._last_model_cache_time = now
|
||||||
if not self._model_cache:
|
if not self._model_cache:
|
||||||
|
has_byok = bool(
|
||||||
|
(uv.BYOK_BASE_URL or self.valves.BYOK_BASE_URL)
|
||||||
|
and (
|
||||||
|
uv.BYOK_API_KEY
|
||||||
|
or self.valves.BYOK_API_KEY
|
||||||
|
or uv.BYOK_BEARER_TOKEN
|
||||||
|
or self.valves.BYOK_BEARER_TOKEN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not token and not has_byok:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"id": "no_token",
|
||||||
|
"name": "⚠️ No credentials configured. Please set GH_TOKEN or BYOK settings in Valves.",
|
||||||
|
}
|
||||||
|
]
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"id": "warming_up",
|
"id": "warming_up",
|
||||||
@@ -2530,8 +2650,6 @@ class Pipe:
|
|||||||
debug_enabled: bool = False,
|
debug_enabled: bool = False,
|
||||||
token: str = None,
|
token: str = None,
|
||||||
enable_mcp: bool = True,
|
enable_mcp: bool = True,
|
||||||
enable_cache: bool = True,
|
|
||||||
skip_cli_install: bool = False, # Kept for call-site compatibility, no longer used
|
|
||||||
__event_emitter__=None,
|
__event_emitter__=None,
|
||||||
user_lang: str = "en-US",
|
user_lang: str = "en-US",
|
||||||
):
|
):
|
||||||
@@ -2548,7 +2666,6 @@ class Pipe:
|
|||||||
__event_call__,
|
__event_call__,
|
||||||
debug_enabled,
|
debug_enabled,
|
||||||
enable_mcp=enable_mcp,
|
enable_mcp=enable_mcp,
|
||||||
enable_cache=enable_cache,
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2762,7 +2879,6 @@ class Pipe:
|
|||||||
__event_call__=None,
|
__event_call__=None,
|
||||||
debug_enabled: bool = False,
|
debug_enabled: bool = False,
|
||||||
enable_mcp: bool = True,
|
enable_mcp: bool = True,
|
||||||
enable_cache: bool = True,
|
|
||||||
):
|
):
|
||||||
"""Sync MCP configuration to ~/.copilot/config.json."""
|
"""Sync MCP configuration to ~/.copilot/config.json."""
|
||||||
path = os.path.expanduser("~/.copilot/config.json")
|
path = os.path.expanduser("~/.copilot/config.json")
|
||||||
@@ -2786,9 +2902,7 @@ class Pipe:
|
|||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
|
|
||||||
mcp = self._parse_mcp_servers(
|
mcp = self._parse_mcp_servers(__event_call__, enable_mcp=enable_mcp)
|
||||||
__event_call__, enable_mcp=enable_mcp, enable_cache=enable_cache
|
|
||||||
)
|
|
||||||
if not mcp:
|
if not mcp:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@@ -2866,9 +2980,13 @@ class Pipe:
|
|||||||
)
|
)
|
||||||
chat_id = chat_ctx.get("chat_id") or "default"
|
chat_id = chat_ctx.get("chat_id") or "default"
|
||||||
|
|
||||||
# Determine effective MCP and cache settings
|
# Determine effective MCP settings
|
||||||
effective_mcp = user_valves.ENABLE_MCP_SERVER
|
effective_mcp = user_valves.ENABLE_MCP_SERVER
|
||||||
effective_cache = user_valves.ENABLE_TOOL_CACHE
|
|
||||||
|
# P4: Chat tool_ids whitelist — extract once, reuse for both OpenAPI and MCP
|
||||||
|
chat_tool_ids = None
|
||||||
|
if __metadata__ and isinstance(__metadata__, dict):
|
||||||
|
chat_tool_ids = __metadata__.get("tool_ids") or None
|
||||||
|
|
||||||
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
||||||
user_lang = user_ctx["user_language"]
|
user_lang = user_ctx["user_language"]
|
||||||
@@ -2879,7 +2997,6 @@ class Pipe:
|
|||||||
debug_enabled=effective_debug,
|
debug_enabled=effective_debug,
|
||||||
token=effective_token,
|
token=effective_token,
|
||||||
enable_mcp=effective_mcp,
|
enable_mcp=effective_mcp,
|
||||||
enable_cache=effective_cache,
|
|
||||||
__event_emitter__=__event_emitter__,
|
__event_emitter__=__event_emitter__,
|
||||||
user_lang=user_lang,
|
user_lang=user_lang,
|
||||||
)
|
)
|
||||||
@@ -3125,7 +3242,7 @@ class Pipe:
|
|||||||
|
|
||||||
# Check MCP Servers
|
# Check MCP Servers
|
||||||
mcp_servers = self._parse_mcp_servers(
|
mcp_servers = self._parse_mcp_servers(
|
||||||
__event_call__, enable_mcp=effective_mcp, enable_cache=effective_cache
|
__event_call__, enable_mcp=effective_mcp, chat_tool_ids=chat_tool_ids
|
||||||
)
|
)
|
||||||
mcp_server_names = list(mcp_servers.keys()) if mcp_servers else []
|
mcp_server_names = list(mcp_servers.keys()) if mcp_servers else []
|
||||||
if mcp_server_names:
|
if mcp_server_names:
|
||||||
@@ -3180,15 +3297,13 @@ class Pipe:
|
|||||||
mcp_servers = self._parse_mcp_servers(
|
mcp_servers = self._parse_mcp_servers(
|
||||||
__event_call__,
|
__event_call__,
|
||||||
enable_mcp=effective_mcp,
|
enable_mcp=effective_mcp,
|
||||||
enable_cache=effective_cache,
|
chat_tool_ids=chat_tool_ids,
|
||||||
)
|
)
|
||||||
if mcp_servers:
|
if mcp_servers:
|
||||||
resume_params["mcp_servers"] = mcp_servers
|
resume_params["mcp_servers"] = mcp_servers
|
||||||
|
|
||||||
|
# Always None: let CLI built-ins (bash etc.) remain available.
|
||||||
resume_params["available_tools"] = None
|
resume_params["available_tools"] = None
|
||||||
else:
|
|
||||||
resume_params["available_tools"] = (
|
|
||||||
[t.name for t in custom_tools] if custom_tools else None
|
|
||||||
)
|
|
||||||
|
|
||||||
# Always inject the latest system prompt in 'replace' mode
|
# Always inject the latest system prompt in 'replace' mode
|
||||||
# This handles both custom models and user-defined system messages
|
# This handles both custom models and user-defined system messages
|
||||||
@@ -3274,7 +3389,7 @@ class Pipe:
|
|||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
enable_mcp=effective_mcp,
|
enable_mcp=effective_mcp,
|
||||||
enable_cache=effective_cache,
|
chat_tool_ids=chat_tool_ids,
|
||||||
__event_call__=__event_call__,
|
__event_call__=__event_call__,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
127
plugins/pipes/github-copilot-sdk/v0.8.0.md
Normal file
127
plugins/pipes/github-copilot-sdk/v0.8.0.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# 🚀 GitHub Copilot SDK Pipe v0.8.0: Conditional Tool Filtering & Publish Reliability 🎛️
|
||||||
|
|
||||||
|
**GitHub Copilot SDK Pipe v0.8.0** — A major control and reliability upgrade. This release introduces a four-priority tool permission system that makes tool access fully configurable per conversation, fixes file publishing across all storage backends, and ensures CLI built-in tools are never accidentally silenced.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Quick Installation
|
||||||
|
|
||||||
|
- **GitHub Copilot SDK (Pipe)**: [Install v0.8.0](https://openwebui.com/posts/ce96f7b4-12fc-4ac3-9a01-875713e69359)
|
||||||
|
- **GitHub Copilot SDK (Filter)**: [Install v0.1.3](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 What's New in v0.8.0
|
||||||
|
|
||||||
|
### 1. Conditional Tool Filtering (P1~P4) — The Headline Feature
|
||||||
|
|
||||||
|
This release introduces a **four-priority tool permission system** that gives you precise control over which tools are active in each conversation.
|
||||||
|
|
||||||
|
| Priority | Scope | Mechanism |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **P1** | Global | Pipe-level Valve switches (`ENABLE_OPENWEBUI_TOOLS`, `ENABLE_OPENAPI_SERVER`, `ENABLE_MCP`) |
|
||||||
|
| **P2** | Server-level | Admin `config.enable` — disable a tool server from the OpenWebUI admin panel without changing code |
|
||||||
|
| **P3** | User-level | Per-user `UserValves` overrides |
|
||||||
|
| **P4** | Conversation-level | **Chat UI tool selection** — only tools the user explicitly checks in the chat input are activated |
|
||||||
|
|
||||||
|
**How P4 works**: OpenWebUI passes the user's currently selected tools as `tool_ids` in `__metadata__`. The plugin reads this whitelist and filters both **OpenWebUI tools** and **MCP servers** accordingly.
|
||||||
|
|
||||||
|
- **Default ON (No Selection)**: If you **do not select any tools** (leave the checkbox empty), the plugin considers all enabled tools active (provided the P1 global Valve is enabled). OpenWebUI tools, OpenAPI Servers, and MCP Servers are **all mounted by default**.
|
||||||
|
- **Whitelist Mode (Selection Active)**: Once you explicitly check at least one tool (e.g., only "web search"), **only your selected tools are activated**, and all other tools (including MCP servers) are filtered out.
|
||||||
|
|
||||||
|
```
|
||||||
|
User checks ✅ web-search, ✅ code-runner in Chat UI
|
||||||
|
↓
|
||||||
|
chat_tool_ids = ["web-search", "server:code-runner"]
|
||||||
|
↓
|
||||||
|
filtered tools = only the two checked tools
|
||||||
|
↓
|
||||||
|
MCP servers not in the list are skipped entirely
|
||||||
|
```
|
||||||
|
|
||||||
|
**P2 admin control**: Each tool server entry in OpenWebUI now has a `config.enable` field. Setting it to `false` from the admin panel disables that server globally — no Valve edits required.
|
||||||
|
|
||||||
|
### 2. File Publish Reliability — All Storage Backends Fixed
|
||||||
|
|
||||||
|
The persistent `{"detail":"[ERROR: Error getting file content]"}` error when agents publish files has been fully resolved.
|
||||||
|
|
||||||
|
| Root Cause | Fix |
|
||||||
|
| :--- | :--- |
|
||||||
|
| Fallback path used `shutil.copy2` + manual DB write, which stored a local absolute path that S3-backed deployments cannot resolve | Fallback now calls `Storage.upload_file()` directly, auto-adapting to local/S3/GCS/Azure |
|
||||||
|
| HTML files were blocked by OpenWebUI's `ALLOWED_FILE_EXTENSIONS` check | Upload URL always includes `?process=false`, bypassing the content-type filter |
|
||||||
|
|
||||||
|
When the published artifact is an HTML file, the plugin also returns a direct-access HTML link for immediate opening/preview in chat.
|
||||||
|
|
||||||
|
All published links now follow a strict format: they must be relative paths beginning with `/api/v1/files/`.
|
||||||
|
`api/...` (without leading slash) and domain-prefixed absolute URLs are treated as invalid.
|
||||||
|
|
||||||
|
### 3. CLI Built-in Tools Always Available
|
||||||
|
|
||||||
|
`available_tools` is now always `None` (instead of filtering by loaded tool IDs), which means Copilot CLI built-in tools — such as `bash`, `create_file`, `read_file`, `list_directory` — are **always available by default**.
|
||||||
|
|
||||||
|
Previously, when no server tools were configured/loaded, the CLI could receive `available_tools=[]` and silently block all built-ins.
|
||||||
|
|
||||||
|
### 4. Publish Tool Always Injected
|
||||||
|
|
||||||
|
`publish_file_from_workspace` is injected into the tool list before the `ENABLE_OPENWEBUI_TOOLS` guard, so it is **never lost** even when all OpenWebUI tool loading is disabled via Valves.
|
||||||
|
|
||||||
|
### 5. Code Interpreter Warning (Not Disabled)
|
||||||
|
|
||||||
|
The built-in `code_interpreter` tool is **enabled** but operates in a remote, ephemeral sandbox.
|
||||||
|
|
||||||
|
A system prompt warning is now automatically injected to clarify its limitations:
|
||||||
|
> "The `code_interpreter` tool executes code in a remote, ephemeral environment. It cannot access files in your local workspace or persist changes."
|
||||||
|
|
||||||
|
### 6. Bug Fixes Summary
|
||||||
|
|
||||||
|
- **File publish backend mismatch**: Fixed object-storage publish failures (`Error getting file content`) caused by local-path fallback writes.
|
||||||
|
- **HTML artifact extension rejection**: Fixed HTML uploads being blocked by OpenWebUI extension processing by forcing `?process=false`.
|
||||||
|
- **Invalid artifact URL formats**: Fixed links occasionally generated as `api/...` or domain-prefixed URLs; output is now constrained to `/api/v1/files/...` relative paths.
|
||||||
|
- **CLI built-ins silently blocked**: Fixed built-ins becoming unavailable when no server tools were configured/loaded (which resulted in `available_tools=[]`); now default remains `None`.
|
||||||
|
- **Publish tool injection loss**: Fixed `publish_file_from_workspace` being omitted when `ENABLE_OPENWEBUI_TOOLS=False`.
|
||||||
|
|
||||||
|
### 7. Companion Files Filter Included in Pipe Guidance
|
||||||
|
|
||||||
|
The release documentation now embeds the key points of `GitHub Copilot SDK Files Filter` so users can configure the full workflow from a single page:
|
||||||
|
|
||||||
|
- It prevents default RAG pre-processing from consuming uploaded files before Pipe processing.
|
||||||
|
- It preserves raw binary access by moving uploads into `copilot_files`.
|
||||||
|
- v0.1.3 improvements included in guidance: BYOK model-id prefix matching + optional dual-channel debug logs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Key Capabilities
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
| :--- | :--- |
|
||||||
|
| **Conditional Tool Filtering (P1~P4)** | Multi-priority permission system: global Valves → admin config.enable → user valves → Chat UI selection |
|
||||||
|
| **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 |
|
||||||
|
| **Multi-storage Publishing** | File publish works across local disk, S3, GCS, and Azure backends |
|
||||||
|
| **11-Language Localization** | Auto-detected, native status messages for global users |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Migration Notes
|
||||||
|
|
||||||
|
- **No breaking changes** for existing users. The P4 whitelist is only active when the user has explicitly selected tools in the Chat UI; with no selection, all enabled tools are passed as before.
|
||||||
|
- Users on S3/GCS/Azure deployments who experienced file download failures can now publish normally without any additional configuration.
|
||||||
|
- `ENABLE_TOOL_CACHE` Valve has been removed. Tool server connections are now always read fresh from the database to avoid stale state across multiple workers. Use `MODEL_CACHE_TTL` for model list caching.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📥 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)
|
||||||
131
plugins/pipes/github-copilot-sdk/v0.8.0_CN.md
Normal file
131
plugins/pipes/github-copilot-sdk/v0.8.0_CN.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# 🚀 GitHub Copilot SDK Pipe v0.8.0:条件工具过滤 & 发布可靠性全面升级 🎛️
|
||||||
|
|
||||||
|
**GitHub Copilot SDK Pipe v0.8.0** — 一次重大的权限管控与稳定性升级。本次更新引入了四优先级工具权限体系,使工具访问可按每次对话精细配置;同时彻底修复了所有存储后端的文件发布问题,并确保 CLI 内置工具不再被意外屏蔽。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 快速安装
|
||||||
|
|
||||||
|
- **GitHub Copilot SDK (Pipe 插件)**: [安装 v0.8.0](https://openwebui.com/posts/ce96f7b4-12fc-4ac3-9a01-875713e69359)
|
||||||
|
- **GitHub Copilot SDK (Filter 插件)**: [安装 v0.1.3](https://openwebui.com/posts/403a62ee-a596-45e7-be65-fab9cc249dd6)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 v0.8.0 更新内容
|
||||||
|
|
||||||
|
### 1. 条件工具过滤 (P1~P4) — 核心新特性
|
||||||
|
|
||||||
|
本次更新引入了**四优先级工具权限体系**,为您提供对每次对话中可用工具的精确控制。
|
||||||
|
|
||||||
|
| 优先级 | 作用域 | 机制描述 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **P1** | 全局 | Pipe 级 Valve 开关(`ENABLE_OPENWEBUI_TOOLS`、`ENABLE_OPENAPI_SERVER`、`ENABLE_MCP`) |
|
||||||
|
| **P2** | 服务器级 | 管理员 `config.enable` — 无需修改代码,直接从 OpenWebUI 管理面板禁用某个工具服务器 |
|
||||||
|
| **P3** | 用户级 | 用户专属 `UserValves` 覆盖设置 |
|
||||||
|
| **P4** | 对话级 | **Chat UI 工具选择** — 只有用户在输入框明确勾选的工具才会在本次对话中激活 |
|
||||||
|
|
||||||
|
**P4 工作原理**:OpenWebUI 会将用户当前选中的工具 ID 通过 `__metadata__` 中的 `tool_ids` 字段传入。插件读取该白名单并对 **OpenWebUI 工具**和 **MCP server** 同时进行过滤。
|
||||||
|
|
||||||
|
- **默认全开 (Default ON)**:如果您在 Chat UI 中**未勾选任何工具**(留空),只要 P1 全局 Valve 开启,插件就会默认启用**所有可用工具**。OpenWebUI 工具、OpenAPI Server 和 MCP Server 将默认全部挂载。
|
||||||
|
- **按需激活 (Whitelist Mode)**:一旦您在输入框主动勾选了至少一个工具(例如只选了“联网搜索”),则**只有您选中的工具会生效**,其他未选中的工具(包括 MCP 服务器)都将被过滤掉。
|
||||||
|
|
||||||
|
```
|
||||||
|
用户在 Chat UI 勾选 ✅ web-search,✅ code-runner
|
||||||
|
↓
|
||||||
|
chat_tool_ids = ["web-search", "server:code-runner"]
|
||||||
|
↓
|
||||||
|
过滤后工具列表 = 仅包含上述两个工具
|
||||||
|
↓
|
||||||
|
白名单外的 MCP server 将被完全跳过
|
||||||
|
```
|
||||||
|
|
||||||
|
**P2 管理员管控**:OpenWebUI 中每个工具服务器条目现在都有 `config.enable` 字段。从管理面板将其设为 `false` 即可全局禁用该服务器,无需修改任何 Valve 配置。
|
||||||
|
|
||||||
|
### 2. 文件发布全面修复 — 适配所有存储后端
|
||||||
|
|
||||||
|
Agent 发布文件时持续出现的 `{"detail":"[ERROR: Error getting file content]"}` 错误已被彻底解决。
|
||||||
|
|
||||||
|
| 根本原因 | 修复方案 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| 回退路径使用 `shutil.copy2` + 手动写入 DB,导致 `file.path` 存储的是本地绝对路径,S3 等对象存储后端无法解析 | 回退路径改为直接调用 `Storage.upload_file()`,自动适配 local/S3/GCS/Azure |
|
||||||
|
| HTML 文件被 OpenWebUI `ALLOWED_FILE_EXTENSIONS` 内容类型检查拦截 | 上传 URL 始终追加 `?process=false`,绕过文件类型限制 |
|
||||||
|
|
||||||
|
当发布产物为 HTML 文件时,插件还会返回一个可直接访问的 HTML 链接,便于在聊天中即时打开/预览。
|
||||||
|
|
||||||
|
所有发布链接现统一执行严格格式:必须是以 `/api/v1/files/` 开头的相对路径。
|
||||||
|
`api/...`(缺少前导 `/`)或拼接域名的绝对 URL 都视为无效格式。
|
||||||
|
|
||||||
|
### 3. CLI 内置工具始终可用
|
||||||
|
|
||||||
|
`available_tools` 现在统一设为 `None`(不再根据已加载的工具 ID 列表过滤),这意味着 Copilot CLI 内置工具 —— 如 `bash`、`create_file`、`read_file`、`list_directory` —— **默认始终可用**。
|
||||||
|
|
||||||
|
此前,在未配置/未加载任何 server 工具时,CLI 可能收到 `available_tools=[]`,导致所有内置工具被静默屏蔽。
|
||||||
|
|
||||||
|
### 4. 发布工具始终注入
|
||||||
|
|
||||||
|
`publish_file_from_workspace` 工具的注入逻辑提前到 `ENABLE_OPENWEBUI_TOOLS` 守卫之前,因此即使通过 Valve 关闭所有 OpenWebUI 工具加载,该工具也**不再丢失**。
|
||||||
|
|
||||||
|
### 5. 代码解释器警告(未禁用)
|
||||||
|
|
||||||
|
内置的 `code_interpreter` 工具保持**启用**状态,但运行在远程的临时沙箱中。
|
||||||
|
|
||||||
|
为了避免混淆,系统提示词中会自动注入一段警告说明:
|
||||||
|
> "The `code_interpreter` tool executes code in a remote, ephemeral environment. It cannot access files in your local workspace or persist changes."
|
||||||
|
|
||||||
|
请仅将其用于纯计算或逻辑验证,勿用于文件操作。
|
||||||
|
|
||||||
|
### 6. Bug 修复汇总
|
||||||
|
|
||||||
|
- **文件发布后端不匹配**:修复了对象存储场景下因本地路径回退写入导致的 `Error getting file content`。
|
||||||
|
- **HTML 产物扩展名拦截**:修复了 OpenWebUI 扩展名处理导致 HTML 上传被拒的问题,统一强制 `?process=false`。
|
||||||
|
- **产物链接格式错误**:修复了链接偶发被生成成 `api/...` 或带域名 URL 的问题,现统一限制为 `/api/v1/files/...` 相对路径。
|
||||||
|
- **CLI 内置工具被静默屏蔽**:修复了在未配置/未加载任何 server 工具时(最终出现 `available_tools=[]`)导致内置工具不可用的问题,默认保持 `None`。
|
||||||
|
- **发布工具注入丢失**:修复了 `ENABLE_OPENWEBUI_TOOLS=False` 时 `publish_file_from_workspace` 被遗漏的问题。
|
||||||
|
|
||||||
|
### 7. 在 Pipe 文档中整合 Files Filter 指南
|
||||||
|
|
||||||
|
本次已将 `GitHub Copilot SDK Files Filter` 的核心说明整合进 Pipe 文档,用户可在单页完成完整配置:
|
||||||
|
|
||||||
|
- 阻止默认 RAG 在 Pipe 接手前抢先处理上传文件。
|
||||||
|
- 通过将上传文件移动到 `copilot_files` 保留原始二进制访问能力。
|
||||||
|
- 纳入 v0.1.3 重点:BYOK 模型 ID 前缀匹配修复 + 可选双通道调试日志。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 核心能力
|
||||||
|
|
||||||
|
| 特性 | 描述 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| **条件工具过滤 (P1~P4)** | 多优先级权限体系:全局 Valve → 管理员 config.enable → 用户 Valve → Chat UI 选择 |
|
||||||
|
| **通用工具协议** | 原生支持 **MCP**、**OpenAPI** 以及 **OpenWebUI 内置工具** |
|
||||||
|
| **原生工具 UI** | 完美适配 OpenWebUI 内置的工具调用渲染 |
|
||||||
|
| **物理隔离工作区** | 为每个会话提供严格的沙箱环境,保护数据隐私与安全 |
|
||||||
|
| **工作区产物工具** | Agent 可生成文件(Excel/CSV/HTML)并提供持久化下载链接 |
|
||||||
|
| **多存储后端发布** | 文件发布兼容本地磁盘、S3、GCS 及 Azure 后端 |
|
||||||
|
| **11 国语言本地化** | 自动检测并显示原生语言的状态消息 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 迁移说明
|
||||||
|
|
||||||
|
- **无破坏性变更**。P4 白名单过滤仅在用户主动在 Chat UI 选择了工具时生效;未做任何选择时,所有已启用的工具照常传递给 Copilot。
|
||||||
|
- 使用 S3/GCS/Azure 存储后端、曾遭遇文件下载失败的用户,无需任何额外配置即可正常发布文件。
|
||||||
|
- `ENABLE_TOOL_CACHE` Valve 已移除。工具服务器连接现在始终从数据库实时读取,避免多 Worker 环境下的状态过时。模型列表缓存请使用 `MODEL_CACHE_TTL`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📥 导入对话模板
|
||||||
|
|
||||||
|
您可以下载并导入真实案例的 JSON 对话日志,直观查看模型如何调用工具:
|
||||||
|
|
||||||
|
- [📥 对话日志:GitHub 增长预测](https://fu-jie.github.io/awesome-openwebui/plugins/pipes/star-prediction-chat.json)
|
||||||
|
- [📥 对话日志:视频高质量 GIF 转换](https://fu-jie.github.io/awesome-openwebui/plugins/pipes/video-processing-chat.json)
|
||||||
|
|
||||||
|
*导入方式:前往 `设置 → 数据 → 导入对话`。*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 相关资源
|
||||||
|
|
||||||
|
- **GitHub 仓库**: [openwebui-extensions](https://github.com/Fu-Jie/openwebui-extensions)
|
||||||
|
- **完整变更日志**: [README_CN.md](https://github.com/Fu-Jie/openwebui-extensions/blob/main/plugins/pipes/github-copilot-sdk/README_CN.md)
|
||||||
Reference in New Issue
Block a user