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:
@@ -1,6 +1,6 @@
|
||||
# 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**.
|
||||
|
||||
@@ -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)
|
||||
- **🧠 Native Tool Call UI**: Full adaptation to **OpenWebUI's native tool call UI** and thinking process visualization. (v0.7.0)
|
||||
- **🏠 OpenWebUI v0.8.0+ Fix**: Resolved "Error getting file content" download failure by switching to absolute path registration for published files. (v0.7.0)
|
||||
- **🌐 Comprehensive Multi-language Support**: Native localization for status messages in 11 languages (EN, ZH, JA, KO, FR, DE, ES, IT, RU, VI, ID). (v0.7.0)
|
||||
- **🧹 Architecture Cleanup**: Refactored core setup and optimized reasoning status display for a leaner experience. (v0.7.0)
|
||||
- **🎛️ 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)
|
||||
- **🔧 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)
|
||||
- **🌐 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)
|
||||
- **🔒 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)
|
||||
- **🛠️ 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.
|
||||
- **🌊 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).
|
||||
- **📤 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.
|
||||
|
||||
---
|
||||
|
||||
## 🧩 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)
|
||||
|
||||
### 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.
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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 交互体验。
|
||||
|
||||
@@ -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)
|
||||
- **🧠 原生工具调用 UI**: 全面适配 **OpenWebUI 原生工具调用 UI** 与模型思考过程(思维链)展示。(v0.7.0)
|
||||
- **🏠 OpenWebUI v0.8.0+ 兼容性修复**: 通过切换为绝对路径注册发布文件,彻底解决了“Error getting file content”无法下载到本地的问题。(v0.7.0)
|
||||
- **🌐 全面的多语言支持**: 针对状态消息进行了 11 国语言的原生本地化 (中/英/日/韩/法/德/西/意/俄/越/印尼)。(v0.7.0)
|
||||
- **🧹 架构精简**: 重构了初始化逻辑并优化了推理状态显示,提供更轻量稳健的体验。(v0.7.0)
|
||||
- **🎛️ 条件工具过滤 (P1~P4)**: 四优先级工具权限体系。**默认全开**: 若未在 Chat UI (P4) 勾选任何工具,则默认启用所有工具;**白名单模式**: 一旦勾选特定工具,即刻进入严格过滤模式,且 MCP server 同步受控;管理员亦可通过 `config.enable` (P2) 全局禁用工具服务器。(v0.8.0)
|
||||
- **🔧 文件发布全面修复**: 通过在回退路径直接调用 `Storage.upload_file()`,彻底修复了所有存储后端(local/S3/GCS/Azure)下的 `Error getting file content` 问题;同时上传时自动携带 `?process=false`,HTML 文件不再被 `ALLOWED_FILE_EXTENSIONS` 拦截。(v0.8.0)
|
||||
- **🌐 HTML 直达链接**: 当 `publish_file_from_workspace` 发布的是 HTML 文件时,插件会额外提供可直接访问的 HTML 链接,便于在聊天中即时预览/打开。(v0.8.0)
|
||||
- **🔒 文件链接格式严格约束**: 发布链接必须是以 `/api/v1/files/` 开头的相对路径(例如 `/api/v1/files/{id}/content/html`)。禁止使用 `api/...`,也禁止拼接任何域名。(v0.8.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 进度条。
|
||||
- **🌊 深度推理展示**: 完整支持模型思考过程 (Thinking Process) 的流式渲染。
|
||||
- **🖼️ 智能多模态**: 完整支持图像识别与附件上传分析(绕过 RAG 直接访问原始二进制内容)。
|
||||
- **📤 工作区产物工具 (`publish_file_from_workspace`)**: Agent 可生成文件(Excel、CSV、HTML 报告等)并直接在聊天中提供**持久化下载链接**。
|
||||
- **📤 工作区产物工具 (`publish_file_from_workspace`)**: Agent 可生成文件(Excel、CSV、HTML 报告等)并直接在聊天中提供**持久化下载链接**。若为 HTML 文件,还会额外提供可直接访问的 HTML 链接。
|
||||
- **🖼️ 交互式伪影 (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)
|
||||
|
||||
### 1. 管理员配置 (基础设置)
|
||||
@@ -105,7 +127,16 @@
|
||||
2. 创建 **Fine-grained token**,授予 **Account permissions** -> **Copilot Requests** 访问权限。
|
||||
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)。
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||
funding_url: https://github.com/open-webui
|
||||
openwebui_id: ce96f7b4-12fc-4ac3-9a01-875713e69359
|
||||
description: Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.
|
||||
version: 0.7.0
|
||||
version: 0.8.0
|
||||
requirements: github-copilot-sdk==0.1.25
|
||||
"""
|
||||
|
||||
@@ -17,11 +17,9 @@ import tempfile
|
||||
import asyncio
|
||||
import logging
|
||||
import shutil
|
||||
import subprocess
|
||||
import hashlib
|
||||
import aiohttp
|
||||
import contextlib
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union, AsyncGenerator, List, Any, Dict, Literal, Tuple
|
||||
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.files import Files, FileForm
|
||||
from open_webui.config import UPLOAD_DIR, DATA_DIR
|
||||
from open_webui.storage.provider import Storage
|
||||
import mimetypes
|
||||
import uuid
|
||||
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"
|
||||
" - **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"
|
||||
" - **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"
|
||||
"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"
|
||||
@@ -197,10 +197,6 @@ class Pipe:
|
||||
default=True,
|
||||
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(
|
||||
default="medium",
|
||||
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",
|
||||
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(
|
||||
default="openai",
|
||||
@@ -314,10 +314,6 @@ class Pipe:
|
||||
default=True,
|
||||
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_API_KEY: str = Field(
|
||||
@@ -352,8 +348,7 @@ class Pipe:
|
||||
_model_cache: List[dict] = [] # Model list cache
|
||||
_standard_model_ids: set = set() # Track standard model IDs
|
||||
_last_byok_config_hash: str = "" # Track BYOK config for cache invalidation
|
||||
_tool_cache = None # Cache for converted OpenWebUI tools
|
||||
_mcp_server_cache = None # Cache for MCP server config
|
||||
_last_model_cache_time: float = 0 # Timestamp of last model cache refresh
|
||||
_env_setup_done = False # Track if env setup has been completed
|
||||
_last_update_check = 0 # Timestamp of last CLI update check
|
||||
|
||||
@@ -717,46 +712,32 @@ class Pipe:
|
||||
uv = self._get_user_valves(__user__)
|
||||
enable_tools = uv.ENABLE_OPENWEBUI_TOOLS
|
||||
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
|
||||
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__)
|
||||
final_tools = [file_tool] if file_tool else []
|
||||
|
||||
# 3. If all OpenWebUI tool types are disabled, skip loading and return early
|
||||
if not enable_tools and not enable_openapi:
|
||||
return []
|
||||
return final_tools
|
||||
|
||||
# 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)
|
||||
# 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
|
||||
|
||||
# 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:
|
||||
tools.append(file_tool)
|
||||
|
||||
return tools
|
||||
|
||||
# Load OpenWebUI tools dynamically
|
||||
# 5. Load OpenWebUI tools dynamically (always fresh, no cache)
|
||||
openwebui_tools = await self._load_openwebui_tools(
|
||||
body=body,
|
||||
__user__=__user__,
|
||||
__event_call__=__event_call__,
|
||||
enable_tools=enable_tools,
|
||||
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:
|
||||
tool_names = [t.name for t in openwebui_tools]
|
||||
await self._emit_debug_log(
|
||||
@@ -770,15 +751,7 @@ class Pipe:
|
||||
__event_call__,
|
||||
)
|
||||
|
||||
# Create a shallow copy to append user-specific tools without polluting cache
|
||||
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)
|
||||
final_tools.extend(openwebui_tools)
|
||||
|
||||
return final_tools
|
||||
|
||||
@@ -892,7 +865,9 @@ class Pipe:
|
||||
import aiohttp
|
||||
|
||||
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:
|
||||
with open(target_path, "rb") as f:
|
||||
@@ -925,14 +900,25 @@ class Pipe:
|
||||
except Exception as 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:
|
||||
file_id = str(uuid.uuid4())
|
||||
safe_filename = target_path.name
|
||||
dest_path = Path(UPLOAD_DIR) / f"{file_id}_{safe_filename}"
|
||||
await asyncio.to_thread(shutil.copy2, target_path, dest_path)
|
||||
storage_filename = f"{file_id}_{safe_filename}"
|
||||
|
||||
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(
|
||||
id=file_id,
|
||||
@@ -943,7 +929,7 @@ class Pipe:
|
||||
"name": safe_filename,
|
||||
"content_type": mimetypes.guess_type(safe_filename)[0]
|
||||
or "text/plain",
|
||||
"size": os.path.getsize(dest_path),
|
||||
"size": os.path.getsize(target_path),
|
||||
"source": "copilot_workspace_publish",
|
||||
"skip_rag": True,
|
||||
},
|
||||
@@ -952,16 +938,16 @@ class Pipe:
|
||||
|
||||
# 5. Result
|
||||
download_url = f"/api/v1/files/{file_id}/content"
|
||||
view_url = download_url
|
||||
is_html = safe_filename.lower().endswith(".html")
|
||||
is_html = safe_filename.lower().endswith((".html", ".htm"))
|
||||
|
||||
# For HTML files, if user is admin, provide a direct view link (/content/html)
|
||||
if is_html and is_admin:
|
||||
# For HTML files, provide a direct view link (/content/html) for browser preview
|
||||
view_url = None
|
||||
if is_html:
|
||||
view_url = f"{download_url}/html"
|
||||
|
||||
# Localized output
|
||||
msg = self._get_translation(user_lang, "publish_success")
|
||||
if is_html and is_admin:
|
||||
if is_html:
|
||||
hint = self._get_translation(
|
||||
user_lang,
|
||||
"publish_hint_html",
|
||||
@@ -977,13 +963,16 @@ class Pipe:
|
||||
download_url=download_url,
|
||||
)
|
||||
|
||||
return {
|
||||
result = {
|
||||
"file_id": file_id,
|
||||
"filename": safe_filename,
|
||||
"download_url": download_url,
|
||||
"message": msg,
|
||||
"hint": hint,
|
||||
}
|
||||
if is_html and view_url:
|
||||
result["view_url"] = view_url
|
||||
return result
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@@ -1206,6 +1195,31 @@ class Pipe:
|
||||
params_type=ParamsModel,
|
||||
)(_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):
|
||||
"""Build a more complete request-like object with dynamically loaded OpenWebUI configs."""
|
||||
# Dynamically build config from the official registry
|
||||
@@ -1219,12 +1233,9 @@ class Pipe:
|
||||
setattr(config, item.env_name, val)
|
||||
|
||||
# Critical Fix: Explicitly sync TOOL_SERVER_CONNECTIONS to ensure OpenAPI tools work
|
||||
# PERSISTENT_CONFIG_REGISTRY might not contain TOOL_SERVER_CONNECTIONS in all versions
|
||||
if not hasattr(config, "TOOL_SERVER_CONNECTIONS"):
|
||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||
config.TOOL_SERVER_CONNECTIONS = TOOL_SERVER_CONNECTIONS.value
|
||||
else:
|
||||
config.TOOL_SERVER_CONNECTIONS = TOOL_SERVER_CONNECTIONS
|
||||
# Read directly from DB to avoid stale in-memory state in multi-worker deployments
|
||||
fresh_connections = self._read_tool_server_connections()
|
||||
config.TOOL_SERVER_CONNECTIONS = fresh_connections
|
||||
|
||||
app_state = SimpleNamespace(
|
||||
config=config,
|
||||
@@ -1282,6 +1293,7 @@ class Pipe:
|
||||
__event_call__=None,
|
||||
enable_tools: bool = True,
|
||||
enable_openapi: bool = True,
|
||||
chat_tool_ids: Optional[list] = None,
|
||||
):
|
||||
"""Load OpenWebUI tools and convert them to Copilot SDK tools."""
|
||||
if isinstance(__user__, (list, tuple)):
|
||||
@@ -1300,17 +1312,20 @@ class Pipe:
|
||||
|
||||
# --- PROBE LOG ---
|
||||
if __event_call__:
|
||||
conn_status = "Missing"
|
||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||
val = TOOL_SERVER_CONNECTIONS.value
|
||||
conn_status = (
|
||||
f"List({len(val)})" if isinstance(val, list) else str(type(val))
|
||||
)
|
||||
conn_list = self._read_tool_server_connections()
|
||||
conn_summary = []
|
||||
for i, s in enumerate(conn_list):
|
||||
if isinstance(s, dict):
|
||||
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(
|
||||
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__,
|
||||
debug_enabled=True,
|
||||
)
|
||||
# -----------------
|
||||
|
||||
@@ -1327,67 +1342,85 @@ class Pipe:
|
||||
|
||||
# 2. Get OpenAPI Tool Server tools
|
||||
if enable_openapi:
|
||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||
raw_connections = TOOL_SERVER_CONNECTIONS.value
|
||||
raw_connections = self._read_tool_server_connections()
|
||||
|
||||
# Handle Pydantic model vs List vs Dict
|
||||
connections = []
|
||||
if isinstance(raw_connections, list):
|
||||
connections = raw_connections
|
||||
elif hasattr(raw_connections, "dict"):
|
||||
connections = raw_connections.dict()
|
||||
elif hasattr(raw_connections, "model_dump"):
|
||||
connections = raw_connections.model_dump()
|
||||
# Handle Pydantic model vs List vs Dict
|
||||
connections = []
|
||||
if isinstance(raw_connections, list):
|
||||
connections = raw_connections
|
||||
elif hasattr(raw_connections, "dict"):
|
||||
connections = raw_connections.dict()
|
||||
elif hasattr(raw_connections, "model_dump"):
|
||||
connections = raw_connections.model_dump()
|
||||
|
||||
# Debug logging for connections
|
||||
if self.valves.DEBUG:
|
||||
await self._emit_debug_log(
|
||||
f"[Tools] Found {len(connections)} server connections (Type: {type(raw_connections)})",
|
||||
__event_call__,
|
||||
)
|
||||
|
||||
for idx, server in enumerate(connections):
|
||||
# Handle server item type
|
||||
s_type = (
|
||||
server.get("type", "openapi")
|
||||
if isinstance(server, dict)
|
||||
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
|
||||
s_id = None
|
||||
if isinstance(server, dict):
|
||||
info = server.get("info", {})
|
||||
s_id = info.get("id") or server.get("id")
|
||||
else:
|
||||
info = getattr(server, "info", {})
|
||||
if isinstance(info, dict):
|
||||
s_id = info.get("id")
|
||||
else:
|
||||
s_id = getattr(info, "id", None)
|
||||
|
||||
if not s_id:
|
||||
s_id = getattr(server, "id", None)
|
||||
|
||||
if not s_id:
|
||||
s_id = str(idx)
|
||||
|
||||
# Debug logging for connections
|
||||
if self.valves.DEBUG:
|
||||
await self._emit_debug_log(
|
||||
f"[Tools] Found {len(connections)} server connections (Type: {type(raw_connections)})",
|
||||
f"[Tools] Checking Server: ID={s_id}, Type={s_type}",
|
||||
__event_call__,
|
||||
)
|
||||
|
||||
for idx, server in enumerate(connections):
|
||||
# Handle server item type
|
||||
s_type = (
|
||||
server.get("type", "openapi")
|
||||
if isinstance(server, dict)
|
||||
else getattr(server, "type", "openapi")
|
||||
if s_type == "openapi":
|
||||
# Ensure we don't add empty IDs, though fallback to idx should prevent this
|
||||
if s_id:
|
||||
tool_ids.append(f"server:{s_id}")
|
||||
elif self.valves.DEBUG:
|
||||
await self._emit_debug_log(
|
||||
f"[Tools] Skipped non-OpenAPI server: {s_id} ({s_type})",
|
||||
__event_call__,
|
||||
)
|
||||
|
||||
# Handle server ID: Priority info.id > server.id > index
|
||||
s_id = None
|
||||
if isinstance(server, dict):
|
||||
info = server.get("info", {})
|
||||
s_id = info.get("id") or server.get("id")
|
||||
else:
|
||||
info = getattr(server, "info", {})
|
||||
if isinstance(info, dict):
|
||||
s_id = info.get("id")
|
||||
else:
|
||||
s_id = getattr(info, "id", None)
|
||||
|
||||
if not s_id:
|
||||
s_id = getattr(server, "id", None)
|
||||
|
||||
if not s_id:
|
||||
s_id = str(idx)
|
||||
|
||||
if self.valves.DEBUG:
|
||||
await self._emit_debug_log(
|
||||
f"[Tools] Checking Server: ID={s_id}, Type={s_type}",
|
||||
__event_call__,
|
||||
)
|
||||
|
||||
if s_type == "openapi":
|
||||
# Ensure we don't add empty IDs, though fallback to idx should prevent this
|
||||
if s_id:
|
||||
tool_ids.append(f"server:{s_id}")
|
||||
elif self.valves.DEBUG:
|
||||
await self._emit_debug_log(
|
||||
f"[Tools] Skipped non-OpenAPI server: {s_id} ({s_type})",
|
||||
__event_call__,
|
||||
)
|
||||
|
||||
if (
|
||||
not tool_ids and not enable_tools
|
||||
): # No IDs and no built-ins either if tools disabled
|
||||
@@ -1397,6 +1430,16 @@ class Pipe:
|
||||
)
|
||||
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:
|
||||
await self._emit_debug_log(
|
||||
f"[Tools] Requesting tool IDs: {tool_ids}", __event_call__
|
||||
@@ -1469,7 +1512,28 @@ class Pipe:
|
||||
# Fetch Built-in Tools (Web Search, Memory, etc.)
|
||||
if enable_tools:
|
||||
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
|
||||
# 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(
|
||||
self._build_openwebui_request(user_data),
|
||||
{
|
||||
@@ -1477,16 +1541,8 @@ class Pipe:
|
||||
"__chat_id__": extra_params.get("__chat_id__"),
|
||||
"__message_id__": extra_params.get("__message_id__"),
|
||||
},
|
||||
model={
|
||||
"info": {
|
||||
"meta": {
|
||||
"capabilities": {
|
||||
"web_search": True,
|
||||
"image_generation": True,
|
||||
}
|
||||
}
|
||||
}
|
||||
}, # Mock capabilities to allow all globally enabled tools
|
||||
features=all_features,
|
||||
model=model_dict, # model.meta.builtinTools controls which categories are active
|
||||
)
|
||||
if builtin_tools:
|
||||
tools_dict.update(builtin_tools)
|
||||
@@ -1504,16 +1560,15 @@ class Pipe:
|
||||
tool_metadata_cache = {}
|
||||
server_metadata_cache = {}
|
||||
|
||||
# Pre-build server metadata cache from TOOL_SERVER_CONNECTIONS
|
||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||
for server in TOOL_SERVER_CONNECTIONS.value:
|
||||
server_id = server.get("id") or server.get("info", {}).get("id")
|
||||
if server_id:
|
||||
info = server.get("info", {})
|
||||
server_metadata_cache[server_id] = {
|
||||
"name": info.get("name") or server_id,
|
||||
"description": info.get("description", ""),
|
||||
}
|
||||
# Pre-build server metadata cache from DB-fresh tool server connections
|
||||
for server in self._read_tool_server_connections():
|
||||
server_id = server.get("id") or server.get("info", {}).get("id")
|
||||
if server_id:
|
||||
info = server.get("info", {})
|
||||
server_metadata_cache[server_id] = {
|
||||
"name": info.get("name") or server_id,
|
||||
"description": info.get("description", ""),
|
||||
}
|
||||
|
||||
for tool_name, tool_def in tools_dict.items():
|
||||
tool_id = tool_def.get("tool_id", "")
|
||||
@@ -1578,7 +1633,10 @@ class Pipe:
|
||||
return converted_tools
|
||||
|
||||
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]:
|
||||
"""
|
||||
Dynamically load MCP servers from OpenWebUI TOOL_SERVER_CONNECTIONS.
|
||||
@@ -1587,17 +1645,22 @@ class Pipe:
|
||||
if not enable_mcp:
|
||||
return None
|
||||
|
||||
# Check Cache
|
||||
if enable_cache and self._mcp_server_cache is not None:
|
||||
return self._mcp_server_cache
|
||||
|
||||
mcp_servers = {}
|
||||
|
||||
# Iterate over OpenWebUI Tool Server Connections
|
||||
if hasattr(TOOL_SERVER_CONNECTIONS, "value"):
|
||||
connections = TOOL_SERVER_CONNECTIONS.value
|
||||
else:
|
||||
connections = []
|
||||
# Read MCP servers directly from DB to avoid stale in-memory cache
|
||||
connections = self._read_tool_server_connections()
|
||||
|
||||
if __event_call__:
|
||||
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:
|
||||
if conn.get("type") == "mcp":
|
||||
@@ -1605,6 +1668,18 @@ class Pipe:
|
||||
# Use ID from info or generate one
|
||||
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)
|
||||
server_id = re.sub(r"[^a-zA-Z0-9_-]", "_", raw_id)
|
||||
if not server_id or re.match(r"^[_.-]+$", server_id):
|
||||
@@ -1636,7 +1711,6 @@ class Pipe:
|
||||
headers.update(custom_headers)
|
||||
|
||||
# Get filtering configuration
|
||||
mcp_config = conn.get("config", {})
|
||||
function_filter = mcp_config.get("function_name_filter_list", "")
|
||||
|
||||
allowed_tools = ["*"]
|
||||
@@ -1658,10 +1732,6 @@ class Pipe:
|
||||
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
|
||||
|
||||
async def _emit_debug_log(
|
||||
@@ -1899,6 +1969,22 @@ class Pipe:
|
||||
)
|
||||
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
|
||||
|
||||
def _get_workspace_dir(self, user_id: str = None, chat_id: str = None) -> str:
|
||||
@@ -1969,7 +2055,7 @@ class Pipe:
|
||||
is_admin: bool = False,
|
||||
user_id: str = None,
|
||||
enable_mcp: bool = True,
|
||||
enable_cache: bool = True,
|
||||
chat_tool_ids: Optional[list] = None,
|
||||
__event_call__=None,
|
||||
):
|
||||
"""Build SessionConfig for Copilot SDK."""
|
||||
@@ -2018,7 +2104,7 @@ class Pipe:
|
||||
}
|
||||
|
||||
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
|
||||
@@ -2061,12 +2147,12 @@ class Pipe:
|
||||
|
||||
if mcp_servers:
|
||||
session_params["mcp_servers"] = mcp_servers
|
||||
# Critical Fix: When using MCP, available_tools must be None to allow dynamic discovery
|
||||
session_params["available_tools"] = None
|
||||
else:
|
||||
session_params["available_tools"] = (
|
||||
[t.name for t in custom_tools] if custom_tools else None
|
||||
)
|
||||
|
||||
# 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
|
||||
|
||||
if provider_config:
|
||||
session_params["provider"] = provider_config
|
||||
@@ -2338,7 +2424,7 @@ class Pipe:
|
||||
|
||||
# 1. Environment Setup (Only if needed or not done)
|
||||
if needs_setup:
|
||||
self._setup_env(token=token, skip_cli_install=True)
|
||||
self._setup_env(token=token)
|
||||
self.__class__._last_update_check = now
|
||||
else:
|
||||
# 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_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 (
|
||||
self._model_cache
|
||||
and self.__class__._last_byok_config_hash != current_config_hash
|
||||
@@ -2397,8 +2496,12 @@ class Pipe:
|
||||
if self.valves.DEBUG:
|
||||
logger.info("[Pipes] Refreshing model cache...")
|
||||
try:
|
||||
# Use effective token for fetching
|
||||
self._setup_env(token=token, skip_cli_install=True)
|
||||
# Use effective token for fetching.
|
||||
# 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
|
||||
byok = []
|
||||
@@ -2478,7 +2581,24 @@ class Pipe:
|
||||
)
|
||||
|
||||
self._model_cache = standard + byok
|
||||
self.__class__._last_model_cache_time = now
|
||||
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 [
|
||||
{
|
||||
"id": "warming_up",
|
||||
@@ -2530,8 +2650,6 @@ class Pipe:
|
||||
debug_enabled: bool = False,
|
||||
token: str = None,
|
||||
enable_mcp: bool = True,
|
||||
enable_cache: bool = True,
|
||||
skip_cli_install: bool = False, # Kept for call-site compatibility, no longer used
|
||||
__event_emitter__=None,
|
||||
user_lang: str = "en-US",
|
||||
):
|
||||
@@ -2548,7 +2666,6 @@ class Pipe:
|
||||
__event_call__,
|
||||
debug_enabled,
|
||||
enable_mcp=enable_mcp,
|
||||
enable_cache=enable_cache,
|
||||
)
|
||||
return
|
||||
|
||||
@@ -2762,7 +2879,6 @@ class Pipe:
|
||||
__event_call__=None,
|
||||
debug_enabled: bool = False,
|
||||
enable_mcp: bool = True,
|
||||
enable_cache: bool = True,
|
||||
):
|
||||
"""Sync MCP configuration to ~/.copilot/config.json."""
|
||||
path = os.path.expanduser("~/.copilot/config.json")
|
||||
@@ -2786,9 +2902,7 @@ class Pipe:
|
||||
pass
|
||||
return
|
||||
|
||||
mcp = self._parse_mcp_servers(
|
||||
__event_call__, enable_mcp=enable_mcp, enable_cache=enable_cache
|
||||
)
|
||||
mcp = self._parse_mcp_servers(__event_call__, enable_mcp=enable_mcp)
|
||||
if not mcp:
|
||||
return
|
||||
try:
|
||||
@@ -2866,9 +2980,13 @@ class Pipe:
|
||||
)
|
||||
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_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_lang = user_ctx["user_language"]
|
||||
@@ -2879,7 +2997,6 @@ class Pipe:
|
||||
debug_enabled=effective_debug,
|
||||
token=effective_token,
|
||||
enable_mcp=effective_mcp,
|
||||
enable_cache=effective_cache,
|
||||
__event_emitter__=__event_emitter__,
|
||||
user_lang=user_lang,
|
||||
)
|
||||
@@ -3125,7 +3242,7 @@ class Pipe:
|
||||
|
||||
# Check 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 []
|
||||
if mcp_server_names:
|
||||
@@ -3180,15 +3297,13 @@ class Pipe:
|
||||
mcp_servers = self._parse_mcp_servers(
|
||||
__event_call__,
|
||||
enable_mcp=effective_mcp,
|
||||
enable_cache=effective_cache,
|
||||
chat_tool_ids=chat_tool_ids,
|
||||
)
|
||||
if mcp_servers:
|
||||
resume_params["mcp_servers"] = mcp_servers
|
||||
resume_params["available_tools"] = None
|
||||
else:
|
||||
resume_params["available_tools"] = (
|
||||
[t.name for t in custom_tools] if custom_tools else None
|
||||
)
|
||||
|
||||
# Always None: let CLI built-ins (bash etc.) remain available.
|
||||
resume_params["available_tools"] = None
|
||||
|
||||
# Always inject the latest system prompt in 'replace' mode
|
||||
# This handles both custom models and user-defined system messages
|
||||
@@ -3274,7 +3389,7 @@ class Pipe:
|
||||
is_admin=is_admin,
|
||||
user_id=user_id,
|
||||
enable_mcp=effective_mcp,
|
||||
enable_cache=effective_cache,
|
||||
chat_tool_ids=chat_tool_ids,
|
||||
__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