feat: Implement configurable OpenWebUI base URL for deployment scripts and update documentation.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -142,3 +142,4 @@ logs/
|
||||
# OpenWebUI specific
|
||||
# Add any specific ignores for OpenWebUI plugins if needed
|
||||
.git-worktrees/
|
||||
plugins/filters/auth_model_info/
|
||||
|
||||
@@ -1886,19 +1886,9 @@ class Filter:
|
||||
"""
|
||||
Check if compression should be skipped.
|
||||
Returns True if:
|
||||
1. The base model includes 'copilot_sdk'
|
||||
"""
|
||||
# Check if base model includes copilot_sdk
|
||||
if __model__:
|
||||
base_model_id = __model__.get("base_model_id", "")
|
||||
if "copilot_sdk" in base_model_id.lower():
|
||||
if body.get("is_copilot_model", False):
|
||||
return True
|
||||
|
||||
# Also check model in body
|
||||
model_id = body.get("model", "")
|
||||
if "copilot_sdk" in model_id.lower():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def inlet(
|
||||
|
||||
@@ -114,6 +114,7 @@ class Filter:
|
||||
|
||||
# Check if it's a Copilot model
|
||||
is_copilot_model = self._is_copilot_model(current_model)
|
||||
body["is_copilot_model"] = is_copilot_model
|
||||
|
||||
await self._emit_debug_log(
|
||||
__event_emitter__,
|
||||
|
||||
@@ -9,6 +9,7 @@ This directory contains automated scripts for deploying plugins in development t
|
||||
1. **OpenWebUI Running**: Make sure OpenWebUI is running locally (default `http://localhost:3000`)
|
||||
2. **API Key**: You need a valid OpenWebUI API key
|
||||
3. **Environment File**: Create a `.env` file in this directory containing your API key:
|
||||
|
||||
```
|
||||
api_key=sk-xxxxxxxxxxxxx
|
||||
```
|
||||
@@ -42,12 +43,14 @@ python deploy_filter.py --list
|
||||
Used to deploy Filter-type plugins (such as message filtering, context compression, etc.).
|
||||
|
||||
**Key Features**:
|
||||
|
||||
- ✅ Auto-extracts metadata from Python files (version, author, description, etc.)
|
||||
- ✅ Attempts to update existing plugins, creates if not found
|
||||
- ✅ Supports multiple Filter plugin management
|
||||
- ✅ Detailed error messages and connection diagnostics
|
||||
|
||||
**Usage**:
|
||||
|
||||
```bash
|
||||
# Deploy async_context_compression (default)
|
||||
python deploy_filter.py
|
||||
@@ -62,6 +65,7 @@ python deploy_filter.py -l
|
||||
```
|
||||
|
||||
**Workflow**:
|
||||
|
||||
1. Load API key from `.env`
|
||||
2. Find target Filter plugin directory
|
||||
3. Read Python source file
|
||||
@@ -76,6 +80,7 @@ python deploy_filter.py -l
|
||||
Used to deploy Pipe-type plugins (such as GitHub Copilot SDK).
|
||||
|
||||
**Usage**:
|
||||
|
||||
```bash
|
||||
python deploy_pipe.py
|
||||
```
|
||||
@@ -101,6 +106,7 @@ Create a dedicated long-term API key in OpenWebUI Settings for deployment purpos
|
||||
**Cause**: OpenWebUI is not running or port is different
|
||||
|
||||
**Solution**:
|
||||
|
||||
- Make sure OpenWebUI is running
|
||||
- Check which port OpenWebUI is actually listening on (usually 3000)
|
||||
- Edit the URL in the script if needed
|
||||
@@ -110,6 +116,7 @@ Create a dedicated long-term API key in OpenWebUI Settings for deployment purpos
|
||||
**Cause**: `.env` file was not created
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
echo "api_key=sk-your-api-key-here" > .env
|
||||
```
|
||||
@@ -119,6 +126,7 @@ echo "api_key=sk-your-api-key-here" > .env
|
||||
**Cause**: Filter directory name is incorrect
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
# List all available Filters
|
||||
python deploy_filter.py --list
|
||||
@@ -129,6 +137,7 @@ python deploy_filter.py --list
|
||||
**Cause**: API key is invalid or expired
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Verify your API key is valid
|
||||
2. Generate a new API key
|
||||
3. Update the `.env` file
|
||||
@@ -178,6 +187,7 @@ python deploy_filter.py async-context-compression
|
||||
## Security Considerations
|
||||
|
||||
⚠️ **Important**:
|
||||
|
||||
- ✅ Add `.env` file to `.gitignore` (avoid committing sensitive info)
|
||||
- ✅ Never commit API keys to version control
|
||||
- ✅ Use only on trusted networks
|
||||
|
||||
@@ -7,6 +7,7 @@ Added a complete local deployment toolchain for the `async_context_compression`
|
||||
## 📋 New Files
|
||||
|
||||
### 1. **deploy_filter.py** — Filter Plugin Deployment Script
|
||||
|
||||
- **Location**: `scripts/deploy_filter.py`
|
||||
- **Function**: Auto-deploy Filter-type plugins to local OpenWebUI instance
|
||||
- **Features**:
|
||||
@@ -19,6 +20,7 @@ Added a complete local deployment toolchain for the `async_context_compression`
|
||||
- **Code Lines**: ~300
|
||||
|
||||
### 2. **DEPLOYMENT_GUIDE.md** — Complete Deployment Guide
|
||||
|
||||
- **Location**: `scripts/DEPLOYMENT_GUIDE.md`
|
||||
- **Contents**:
|
||||
- Prerequisites and quick start
|
||||
@@ -28,6 +30,7 @@ Added a complete local deployment toolchain for the `async_context_compression`
|
||||
- Step-by-step workflow examples
|
||||
|
||||
### 3. **QUICK_START.md** — Quick Reference Card
|
||||
|
||||
- **Location**: `scripts/QUICK_START.md`
|
||||
- **Contents**:
|
||||
- One-line deployment command
|
||||
@@ -37,6 +40,7 @@ Added a complete local deployment toolchain for the `async_context_compression`
|
||||
- CI/CD integration examples
|
||||
|
||||
### 4. **test_deploy_filter.py** — Unit Test Suite
|
||||
|
||||
- **Location**: `tests/scripts/test_deploy_filter.py`
|
||||
- **Test Coverage**:
|
||||
- ✅ Filter file discovery (3 tests)
|
||||
@@ -138,6 +142,7 @@ openwebui_id: b1655bc8-6de9-4cad-8cb5-a6f7829a02ce
|
||||
```
|
||||
|
||||
**Supported Metadata Fields**:
|
||||
|
||||
- `title` — Filter display name ✅
|
||||
- `id` — Unique identifier ✅
|
||||
- `author` — Author name ✅
|
||||
@@ -335,17 +340,20 @@ Metadata Extraction and Delivery
|
||||
### Debugging Tips
|
||||
|
||||
1. **Enable Verbose Logging**:
|
||||
|
||||
```bash
|
||||
python deploy_filter.py 2>&1 | tee deploy.log
|
||||
```
|
||||
|
||||
2. **Test API Connection**:
|
||||
|
||||
```bash
|
||||
curl -X GET http://localhost:3000/api/v1/functions \
|
||||
-H "Authorization: Bearer $API_KEY"
|
||||
```
|
||||
|
||||
3. **Verify .env File**:
|
||||
|
||||
```bash
|
||||
grep "api_key=" scripts/.env
|
||||
```
|
||||
|
||||
@@ -73,12 +73,14 @@ python deploy_async_context_compression.py
|
||||
```
|
||||
|
||||
**Features**:
|
||||
|
||||
- ✅ Optimized specifically for async_context_compression
|
||||
- ✅ Clear deployment steps and confirmation
|
||||
- ✅ Friendly error messages
|
||||
- ✅ Shows next steps after successful deployment
|
||||
|
||||
**Sample Output**:
|
||||
|
||||
```
|
||||
======================================================================
|
||||
🚀 Deploying Async Context Compression Filter Plugin
|
||||
@@ -117,6 +119,7 @@ python deploy_filter.py --list
|
||||
```
|
||||
|
||||
**Features**:
|
||||
|
||||
- ✅ Generic Filter deployment tool
|
||||
- ✅ Supports multiple plugins
|
||||
- ✅ Auto metadata extraction
|
||||
@@ -142,6 +145,7 @@ python deploy_tool.py openwebui-skills-manager
|
||||
```
|
||||
|
||||
**Features**:
|
||||
|
||||
- ✅ Supports Tools plugin deployment
|
||||
- ✅ Auto-detects `Tools` class definition
|
||||
- ✅ Smart update/create logic
|
||||
@@ -290,6 +294,7 @@ git status # should not show .env
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
# 1. Check if OpenWebUI is running
|
||||
curl http://localhost:3000
|
||||
@@ -309,6 +314,7 @@ curl http://localhost:3000
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
echo "api_key=sk-your-api-key" > .env
|
||||
cat .env # verify file created
|
||||
@@ -321,6 +327,7 @@ cat .env # verify file created
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
# List all available Filters
|
||||
python deploy_filter.py --list
|
||||
@@ -337,6 +344,7 @@ python deploy_filter.py async-context-compression
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
# 1. Verify API key is correct
|
||||
grep "api_key=" .env
|
||||
@@ -370,7 +378,7 @@ python deploy_async_context_compression.py
|
||||
|
||||
### Method 2: Verify in OpenWebUI
|
||||
|
||||
1. Open OpenWebUI: http://localhost:3000
|
||||
1. Open OpenWebUI: <http://localhost:3000>
|
||||
2. Go to Settings → Filters
|
||||
3. Check if 'Async Context Compression' is listed
|
||||
4. Verify version number is correct (should be latest)
|
||||
@@ -380,6 +388,7 @@ python deploy_async_context_compression.py
|
||||
1. Open a new conversation
|
||||
2. Enable 'Async Context Compression' Filter
|
||||
3. Have multiple-turn conversation and verify compression/summarization works
|
||||
|
||||
## 💡 Advanced Usage
|
||||
|
||||
### Automated Deploy & Test
|
||||
@@ -473,4 +482,3 @@ Newly created deployment-related files:
|
||||
**Last Updated**: 2026-03-09
|
||||
**Script Status**: ✅ Ready for production
|
||||
**Test Coverage**: 10/10 passed ✅
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
✅ **Yes, re-deploying automatically updates the plugin!**
|
||||
|
||||
The deployment script uses a **smart two-stage strategy**:
|
||||
|
||||
1. 🔄 **Try UPDATE First** (if plugin exists)
|
||||
2. 📝 **Auto CREATE** (if update fails — plugin doesn't exist)
|
||||
|
||||
@@ -54,6 +55,7 @@ if response.status_code == 200:
|
||||
```
|
||||
|
||||
**What Happens**:
|
||||
|
||||
- Send **POST** to `/api/v1/functions/id/{filter_id}/update`
|
||||
- If returns **HTTP 200**, plugin exists and update succeeded
|
||||
- Includes:
|
||||
@@ -84,6 +86,7 @@ if response.status_code != 200:
|
||||
```
|
||||
|
||||
**What Happens**:
|
||||
|
||||
- If update fails (HTTP ≠ 200), auto-attempt create
|
||||
- Send **POST** to `/api/v1/functions/create`
|
||||
- Uses **same payload** (code, metadata identical)
|
||||
@@ -103,6 +106,7 @@ $ python deploy_async_context_compression.py
|
||||
```
|
||||
|
||||
**What Happens**:
|
||||
|
||||
1. Try UPDATE → fails (HTTP 404 — plugin doesn't exist)
|
||||
2. Auto-try CREATE → succeeds (HTTP 200)
|
||||
3. Plugin created in OpenWebUI
|
||||
@@ -121,6 +125,7 @@ $ python deploy_async_context_compression.py
|
||||
```
|
||||
|
||||
**What Happens**:
|
||||
|
||||
1. Read modified code
|
||||
2. Try UPDATE → succeeds (HTTP 200 — plugin exists)
|
||||
3. Plugin in OpenWebUI updated to latest code
|
||||
@@ -147,6 +152,7 @@ $ python deploy_async_context_compression.py
|
||||
```
|
||||
|
||||
**Characteristics**:
|
||||
|
||||
- 🚀 Each update takes only 5 seconds
|
||||
- 📝 Each is an incremental update
|
||||
- ✅ No need to restart OpenWebUI
|
||||
@@ -181,11 +187,13 @@ version: 1.3.0
|
||||
```
|
||||
|
||||
**Each deployment**:
|
||||
|
||||
1. Script reads version from docstring
|
||||
2. Sends this version in manifest to OpenWebUI
|
||||
3. If you change version in code, deployment updates to new version
|
||||
|
||||
**Best Practice**:
|
||||
|
||||
```bash
|
||||
# 1. Modify code
|
||||
vim async_context_compression.py
|
||||
@@ -300,6 +308,7 @@ Usually **not needed** because:
|
||||
4. ✅ Failures auto-rollback
|
||||
|
||||
但如果真的需要控制,可以:
|
||||
|
||||
- 手动修改脚本 (修改 `deploy_filter.py`)
|
||||
- 或分别使用 UPDATE/CREATE 的具体 API 端点
|
||||
|
||||
@@ -323,6 +332,7 @@ Usually **not needed** because:
|
||||
### Q: 可以同时部署多个插件吗?
|
||||
|
||||
✅ **可以!**
|
||||
|
||||
```bash
|
||||
python deploy_filter.py async-context-compression
|
||||
python deploy_filter.py folder-memory
|
||||
@@ -337,6 +347,7 @@ python deploy_filter.py context_enhancement_filter
|
||||
---
|
||||
|
||||
**总结**: 部署脚本的更新机制完全自动化,开发者只需修改代码,每次运行 `deploy_async_context_compression.py` 就会自动:
|
||||
|
||||
1. ✅ 创建(第一次)或更新(后续)插件
|
||||
2. ✅ 从代码提取最新的元数据和版本号
|
||||
3. ✅ 立即生效,无需重启 OpenWebUI
|
||||
|
||||
@@ -49,6 +49,31 @@ def _load_api_key() -> str:
|
||||
raise ValueError("api_key not found in .env file.")
|
||||
|
||||
|
||||
def _load_openwebui_base_url() -> str:
|
||||
"""Load OpenWebUI base URL from .env file or environment.
|
||||
|
||||
Checks in order:
|
||||
1. OPENWEBUI_BASE_URL in .env
|
||||
2. OPENWEBUI_BASE_URL environment variable
|
||||
3. Default to http://localhost:3000
|
||||
"""
|
||||
if ENV_FILE.exists():
|
||||
for line in ENV_FILE.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if line.startswith("OPENWEBUI_BASE_URL="):
|
||||
url = line.split("=", 1)[1].strip()
|
||||
if url:
|
||||
return url
|
||||
|
||||
# Try environment variable
|
||||
url = os.environ.get("OPENWEBUI_BASE_URL")
|
||||
if url:
|
||||
return url
|
||||
|
||||
# Default
|
||||
return "http://localhost:3000"
|
||||
|
||||
|
||||
def _find_filter_file(filter_name: str) -> Optional[Path]:
|
||||
"""Find the main Python file for a filter.
|
||||
|
||||
@@ -126,7 +151,9 @@ def _build_filter_payload(
|
||||
filter_id = metadata.get("id", filter_name).replace("-", "_")
|
||||
title = metadata.get("title", filter_name)
|
||||
author = metadata.get("author", "Fu-Jie")
|
||||
author_url = metadata.get("author_url", "https://github.com/Fu-Jie/openwebui-extensions")
|
||||
author_url = metadata.get(
|
||||
"author_url", "https://github.com/Fu-Jie/openwebui-extensions"
|
||||
)
|
||||
funding_url = metadata.get("funding_url", "https://github.com/open-webui")
|
||||
description = metadata.get("description", f"Filter plugin: {title}")
|
||||
version = metadata.get("version", "1.0.0")
|
||||
@@ -211,11 +238,13 @@ def deploy_filter(filter_name: str = DEFAULT_FILTER) -> bool:
|
||||
}
|
||||
|
||||
# 6. Send update request
|
||||
update_url = "http://localhost:3000/api/v1/functions/id/{}/update".format(filter_id)
|
||||
create_url = "http://localhost:3000/api/v1/functions/create"
|
||||
base_url = _load_openwebui_base_url()
|
||||
update_url = "{}/api/v1/functions/id/{}/update".format(base_url, filter_id)
|
||||
create_url = "{}/api/v1/functions/create".format(base_url)
|
||||
|
||||
print(f"📦 Deploying filter '{title}' (version {version})...")
|
||||
print(f" File: {file_path}")
|
||||
print(f" Target: {base_url}")
|
||||
|
||||
try:
|
||||
# Try update first
|
||||
@@ -247,7 +276,9 @@ def deploy_filter(filter_name: str = DEFAULT_FILTER) -> bool:
|
||||
print(f"✅ Successfully created '{title}' filter!")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed to update or create. Status: {res_create.status_code}")
|
||||
print(
|
||||
f"❌ Failed to update or create. Status: {res_create.status_code}"
|
||||
)
|
||||
try:
|
||||
error_msg = res_create.json()
|
||||
print(f" Error: {error_msg}")
|
||||
@@ -256,9 +287,8 @@ def deploy_filter(filter_name: str = DEFAULT_FILTER) -> bool:
|
||||
return False
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(
|
||||
"❌ Connection error: Could not reach OpenWebUI at localhost:3000"
|
||||
)
|
||||
base_url = _load_openwebui_base_url()
|
||||
print(f"❌ Connection error: Could not reach OpenWebUI at {base_url}")
|
||||
print(" Make sure OpenWebUI is running and accessible.")
|
||||
return False
|
||||
except requests.exceptions.Timeout:
|
||||
@@ -272,7 +302,11 @@ def deploy_filter(filter_name: str = DEFAULT_FILTER) -> bool:
|
||||
def list_filters() -> None:
|
||||
"""List all available filters."""
|
||||
print("📋 Available filters:")
|
||||
filters = [d.name for d in FILTERS_DIR.iterdir() if d.is_dir() and not d.name.startswith("_")]
|
||||
filters = [
|
||||
d.name
|
||||
for d in FILTERS_DIR.iterdir()
|
||||
if d.is_dir() and not d.name.startswith("_")
|
||||
]
|
||||
|
||||
if not filters:
|
||||
print(" (No filters found)")
|
||||
|
||||
@@ -76,8 +76,7 @@ def _get_base_url() -> str:
|
||||
|
||||
if not base_url:
|
||||
raise ValueError(
|
||||
f"Missing url. Please create {ENV_FILE} with: "
|
||||
"url=http://localhost:3000"
|
||||
f"Missing url. Please create {ENV_FILE} with: " "url=http://localhost:3000"
|
||||
)
|
||||
return base_url.rstrip("/")
|
||||
|
||||
@@ -141,7 +140,9 @@ def _build_tool_payload(
|
||||
tool_id = metadata.get("id", tool_name).replace("-", "_")
|
||||
title = metadata.get("title", tool_name)
|
||||
author = metadata.get("author", "Fu-Jie")
|
||||
author_url = metadata.get("author_url", "https://github.com/Fu-Jie/openwebui-extensions")
|
||||
author_url = metadata.get(
|
||||
"author_url", "https://github.com/Fu-Jie/openwebui-extensions"
|
||||
)
|
||||
funding_url = metadata.get("funding_url", "https://github.com/open-webui")
|
||||
description = metadata.get("description", f"Tool plugin: {title}")
|
||||
version = metadata.get("version", "1.0.0")
|
||||
@@ -263,7 +264,9 @@ def deploy_tool(tool_name: str = DEFAULT_TOOL) -> bool:
|
||||
print(f"✅ Successfully created '{title}' tool!")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed to update or create. Status: {res_create.status_code}")
|
||||
print(
|
||||
f"❌ Failed to update or create. Status: {res_create.status_code}"
|
||||
)
|
||||
try:
|
||||
error_msg = res_create.json()
|
||||
print(f" Error: {error_msg}")
|
||||
@@ -272,9 +275,7 @@ def deploy_tool(tool_name: str = DEFAULT_TOOL) -> bool:
|
||||
return False
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(
|
||||
"❌ Connection error: Could not reach OpenWebUI at {base_url}"
|
||||
)
|
||||
print("❌ Connection error: Could not reach OpenWebUI at {base_url}")
|
||||
print(" Make sure OpenWebUI is running and accessible.")
|
||||
return False
|
||||
except requests.exceptions.Timeout:
|
||||
@@ -288,7 +289,9 @@ def deploy_tool(tool_name: str = DEFAULT_TOOL) -> bool:
|
||||
def list_tools() -> None:
|
||||
"""List all available tools."""
|
||||
print("📋 Available tools:")
|
||||
tools = [d.name for d in TOOLS_DIR.iterdir() if d.is_dir() and not d.name.startswith("_")]
|
||||
tools = [
|
||||
d.name for d in TOOLS_DIR.iterdir() if d.is_dir() and not d.name.startswith("_")
|
||||
]
|
||||
|
||||
if not tools:
|
||||
print(" (No tools found)")
|
||||
|
||||
@@ -187,9 +187,7 @@ def build_payload(candidate: PluginCandidate) -> Dict[str, object]:
|
||||
manifest = dict(candidate.metadata)
|
||||
manifest.setdefault("title", candidate.title)
|
||||
manifest.setdefault("author", "Fu-Jie")
|
||||
manifest.setdefault(
|
||||
"author_url", "https://github.com/Fu-Jie/openwebui-extensions"
|
||||
)
|
||||
manifest.setdefault("author_url", "https://github.com/Fu-Jie/openwebui-extensions")
|
||||
manifest.setdefault("funding_url", "https://github.com/open-webui")
|
||||
manifest.setdefault(
|
||||
"description", f"{candidate.plugin_type.title()} plugin: {candidate.title}"
|
||||
@@ -233,7 +231,9 @@ def build_api_urls(base_url: str, candidate: PluginCandidate) -> Tuple[str, str]
|
||||
)
|
||||
|
||||
|
||||
def discover_plugins(plugin_types: Sequence[str]) -> Tuple[List[PluginCandidate], List[Tuple[Path, str]]]:
|
||||
def discover_plugins(
|
||||
plugin_types: Sequence[str],
|
||||
) -> Tuple[List[PluginCandidate], List[Tuple[Path, str]]]:
|
||||
candidates: List[PluginCandidate] = []
|
||||
skipped: List[Tuple[Path, str]] = []
|
||||
|
||||
@@ -344,7 +344,9 @@ def print_skipped_summary(skipped: Sequence[Tuple[Path, str]]) -> None:
|
||||
for _, reason in skipped:
|
||||
counts[reason] = counts.get(reason, 0) + 1
|
||||
|
||||
summary = ", ".join(f"{reason}: {count}" for reason, count in sorted(counts.items()))
|
||||
summary = ", ".join(
|
||||
f"{reason}: {count}" for reason, count in sorted(counts.items())
|
||||
)
|
||||
print(f"Skipped {len(skipped)} files ({summary}).")
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ local deployment are present and functional.
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
"""Check all deployment tools are ready."""
|
||||
base_dir = Path(__file__).parent.parent
|
||||
@@ -50,7 +51,7 @@ def main():
|
||||
|
||||
if exists and file_path.endswith(".py"):
|
||||
size = full_path.stat().st_size
|
||||
lines = len(full_path.read_text().split('\n'))
|
||||
lines = len(full_path.read_text().split("\n"))
|
||||
print(f" └─ [{size} bytes, ~{lines} lines]")
|
||||
|
||||
if not exists:
|
||||
|
||||
@@ -66,7 +66,7 @@ def test_build_payload_uses_native_tool_shape_for_tools():
|
||||
"description": "Demo tool description",
|
||||
"openwebui_id": "12345678-1234-1234-1234-123456789abc",
|
||||
},
|
||||
content='class Tools:\n pass\n',
|
||||
content="class Tools:\n pass\n",
|
||||
function_id="demo_tool",
|
||||
)
|
||||
|
||||
@@ -79,7 +79,7 @@ def test_build_payload_uses_native_tool_shape_for_tools():
|
||||
"description": "Demo tool description",
|
||||
"manifest": {},
|
||||
},
|
||||
"content": 'class Tools:\n pass\n',
|
||||
"content": "class Tools:\n pass\n",
|
||||
"access_grants": [],
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ def test_build_api_urls_uses_tool_endpoints_for_tools():
|
||||
plugin_type="tool",
|
||||
file_path=Path("plugins/tools/demo/demo_tool.py"),
|
||||
metadata={"title": "Demo Tool"},
|
||||
content='class Tools:\n pass\n',
|
||||
content="class Tools:\n pass\n",
|
||||
function_id="demo_tool",
|
||||
)
|
||||
|
||||
@@ -101,7 +101,9 @@ def test_build_api_urls_uses_tool_endpoints_for_tools():
|
||||
assert create_url == "http://localhost:3000/api/v1/tools/create"
|
||||
|
||||
|
||||
def test_discover_plugins_only_returns_supported_openwebui_plugins(tmp_path, monkeypatch):
|
||||
def test_discover_plugins_only_returns_supported_openwebui_plugins(
|
||||
tmp_path, monkeypatch
|
||||
):
|
||||
actions_dir = tmp_path / "plugins" / "actions"
|
||||
filters_dir = tmp_path / "plugins" / "filters"
|
||||
pipes_dir = tmp_path / "plugins" / "pipes"
|
||||
@@ -110,7 +112,9 @@ def test_discover_plugins_only_returns_supported_openwebui_plugins(tmp_path, mon
|
||||
write_plugin(actions_dir / "flash-card" / "flash_card.py", PLUGIN_HEADER)
|
||||
write_plugin(actions_dir / "flash-card" / "flash_card_cn.py", PLUGIN_HEADER)
|
||||
write_plugin(actions_dir / "infographic" / "verify_generation.py", PLUGIN_HEADER)
|
||||
write_plugin(filters_dir / "missing-id" / "missing_id.py", '"""\ntitle: Missing ID\n"""\n')
|
||||
write_plugin(
|
||||
filters_dir / "missing-id" / "missing_id.py", '"""\ntitle: Missing ID\n"""\n'
|
||||
)
|
||||
write_plugin(pipes_dir / "sdk" / "github_copilot_sdk.py", PLUGIN_HEADER)
|
||||
write_plugin(tools_dir / "skills" / "openwebui_skills_manager.py", PLUGIN_HEADER)
|
||||
|
||||
@@ -150,7 +154,9 @@ def test_discover_plugins_only_returns_supported_openwebui_plugins(tmp_path, mon
|
||||
("class Action:\n pass\n", "missing plugin header"),
|
||||
],
|
||||
)
|
||||
def test_discover_plugins_reports_missing_metadata(tmp_path, monkeypatch, header, expected_reason):
|
||||
def test_discover_plugins_reports_missing_metadata(
|
||||
tmp_path, monkeypatch, header, expected_reason
|
||||
):
|
||||
action_dir = tmp_path / "plugins" / "actions"
|
||||
plugin_file = action_dir / "demo" / "demo.py"
|
||||
write_plugin(plugin_file, header)
|
||||
|
||||
Reference in New Issue
Block a user