Merge pull request #11 from Fu-Jie/copilot/reuse-db-connection
Refactor async-context-compression to re-use OpenWebUI's internal DB connection
This commit is contained in:
113
.github/copilot-instructions.md
vendored
113
.github/copilot-instructions.md
vendored
@@ -349,6 +349,119 @@ requirements: python-docx==1.1.2, openpyxl==3.1.2
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 数据库连接规范 (Database Connection)
|
||||
|
||||
### 复用 OpenWebUI 内部连接 (Re-use OpenWebUI's Internal Connection)
|
||||
|
||||
当插件需要持久化存储时,**必须**复用 Open WebUI 的内部数据库连接,而不是创建新的数据库引擎。这确保了:
|
||||
|
||||
- 插件与数据库类型无关(自动支持 PostgreSQL、SQLite 等)
|
||||
- 自动继承 Open WebUI 的数据库配置
|
||||
- 避免连接池资源浪费
|
||||
- 保持与 Open WebUI 核心的兼容性
|
||||
|
||||
When a plugin requires persistent storage, it **MUST** re-use Open WebUI's internal database connection instead of creating a new database engine. This ensures:
|
||||
|
||||
- The plugin is database-agnostic (automatically supports PostgreSQL, SQLite, etc.)
|
||||
- Automatic inheritance of Open WebUI's database configuration
|
||||
- No wasted connection pool resources
|
||||
- Compatibility with Open WebUI's core
|
||||
|
||||
### 实现示例 (Implementation Example)
|
||||
|
||||
```python
|
||||
# Open WebUI internal database (re-use shared connection)
|
||||
from open_webui.internal.db import engine as owui_engine
|
||||
from open_webui.internal.db import Session as owui_Session
|
||||
from open_webui.internal.db import Base as owui_Base
|
||||
|
||||
from sqlalchemy import Column, String, Text, DateTime, Integer, inspect
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class PluginTable(owui_Base):
|
||||
"""Plugin storage table - inherits from OpenWebUI's Base"""
|
||||
|
||||
__tablename__ = "plugin_table_name"
|
||||
__table_args__ = {"extend_existing": True} # Required to avoid conflicts on plugin reload
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
unique_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
data = Column(Text, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
|
||||
class Filter: # or Pipe, Action, etc.
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
self._db_engine = owui_engine
|
||||
self._SessionLocal = owui_Session
|
||||
self._init_database()
|
||||
|
||||
def _init_database(self):
|
||||
"""Initialize the database table using OpenWebUI's shared connection."""
|
||||
try:
|
||||
inspector = inspect(self._db_engine)
|
||||
if not inspector.has_table("plugin_table_name"):
|
||||
PluginTable.__table__.create(bind=self._db_engine, checkfirst=True)
|
||||
print("[Database] ✅ Created plugin table using OpenWebUI's shared connection.")
|
||||
else:
|
||||
print("[Database] ✅ Using OpenWebUI's shared connection. Table already exists.")
|
||||
except Exception as e:
|
||||
print(f"[Database] ❌ Initialization failed: {str(e)}")
|
||||
|
||||
def _save_data(self, unique_id: str, data: str):
|
||||
"""Save data using context manager pattern."""
|
||||
try:
|
||||
with self._SessionLocal() as session:
|
||||
# Your database operations here
|
||||
session.commit()
|
||||
except Exception as e:
|
||||
print(f"[Storage] ❌ Database save failed: {str(e)}")
|
||||
|
||||
def _load_data(self, unique_id: str):
|
||||
"""Load data using context manager pattern."""
|
||||
try:
|
||||
with self._SessionLocal() as session:
|
||||
record = session.query(PluginTable).filter_by(unique_id=unique_id).first()
|
||||
if record:
|
||||
session.expunge(record) # Detach from session for use after close
|
||||
return record
|
||||
except Exception as e:
|
||||
print(f"[Load] ❌ Database read failed: {str(e)}")
|
||||
return None
|
||||
```
|
||||
|
||||
### 禁止的做法 (Prohibited Practices)
|
||||
|
||||
以下做法**已被弃用**,不应在新插件中使用:
|
||||
|
||||
The following practices are **deprecated** and should NOT be used in new plugins:
|
||||
|
||||
```python
|
||||
# ❌ 禁止: 读取 DATABASE_URL 环境变量
|
||||
# ❌ Prohibited: Reading DATABASE_URL environment variable
|
||||
database_url = os.getenv("DATABASE_URL")
|
||||
|
||||
# ❌ 禁止: 创建独立的数据库引擎
|
||||
# ❌ Prohibited: Creating a separate database engine
|
||||
from sqlalchemy import create_engine
|
||||
self._db_engine = create_engine(database_url, **engine_args)
|
||||
|
||||
# ❌ 禁止: 创建独立的会话工厂
|
||||
# ❌ Prohibited: Creating a separate session factory
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
self._SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self._db_engine)
|
||||
|
||||
# ❌ 禁止: 使用独立的 Base
|
||||
# ❌ Prohibited: Using a separate Base
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
Base = declarative_base()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 代码规范 (Code Style)
|
||||
|
||||
### Python 规范
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Async Context Compression Filter
|
||||
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.0.0 | **License:** MIT
|
||||
**Author:** [Fu-Jie](https://github.com/Fu-Jie) | **Version:** 1.1.0 | **License:** MIT
|
||||
|
||||
> **Important Note**: To ensure the maintainability and usability of all filters, each filter should be accompanied by clear and complete documentation to fully explain its functionality, configuration, and usage.
|
||||
|
||||
@@ -12,7 +12,7 @@ This filter significantly reduces token consumption in long conversations by usi
|
||||
|
||||
- ✅ **Automatic Compression**: Triggers context compression automatically based on a message count threshold.
|
||||
- ✅ **Asynchronous Summarization**: Generates summaries in the background without blocking the current chat response.
|
||||
- ✅ **Persistent Storage**: Supports both PostgreSQL and SQLite databases to ensure summaries are not lost after a service restart.
|
||||
- ✅ **Persistent Storage**: Uses Open WebUI's shared database connection - automatically supports any database backend (PostgreSQL, SQLite, etc.).
|
||||
- ✅ **Flexible Retention Policy**: Freely configure the number of initial and final messages to keep, ensuring critical information and context continuity.
|
||||
- ✅ **Smart Injection**: Intelligently injects the generated historical summary into the new context.
|
||||
|
||||
@@ -20,18 +20,11 @@ This filter significantly reduces token consumption in long conversations by usi
|
||||
|
||||
## Installation & Configuration
|
||||
|
||||
### 1. Environment Variable
|
||||
### 1. Database (Automatic)
|
||||
|
||||
This plugin requires a database connection. You **must** configure the `DATABASE_URL` in your Open WebUI environment variables.
|
||||
This plugin automatically uses Open WebUI's shared database connection. **No additional database configuration is required.**
|
||||
|
||||
- **PostgreSQL Example**:
|
||||
```
|
||||
DATABASE_URL=postgresql://user:password@host:5432/openwebui
|
||||
```
|
||||
- **SQLite Example**:
|
||||
```
|
||||
DATABASE_URL=sqlite:///path/to/your/data/webui.db
|
||||
```
|
||||
The `chat_summary` table will be created automatically on first run.
|
||||
|
||||
### 2. Filter Order
|
||||
|
||||
@@ -64,8 +57,8 @@ You can adjust the following parameters in the filter's settings:
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Problem: Database connection failed.**
|
||||
- **Solution**: Please ensure the `DATABASE_URL` environment variable is set correctly and that the database service is running.
|
||||
- **Problem: Database table not created.**
|
||||
- **Solution**: Ensure Open WebUI is properly configured with a database and check Open WebUI's logs for detailed error messages.
|
||||
|
||||
- **Problem: Summary not generated.**
|
||||
- **Solution**: Check if the `compression_threshold` has been met and verify that `summary_model` is configured correctly. Check the logs for detailed errors.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 异步上下文压缩过滤器
|
||||
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.0.0 | **许可证:** MIT
|
||||
**作者:** [Fu-Jie](https://github.com/Fu-Jie) | **版本:** 1.1.0 | **许可证:** MIT
|
||||
|
||||
> **重要提示**:为了确保所有过滤器的可维护性和易用性,每个过滤器都应附带清晰、完整的文档,以确保其功能、配置和使用方法得到充分说明。
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
- ✅ **自动压缩**: 基于消息数量阈值自动触发上下文压缩。
|
||||
- ✅ **异步摘要**: 在后台生成摘要,不阻塞当前对话的响应。
|
||||
- ✅ **持久化存储**: 支持 PostgreSQL 和 SQLite 数据库,确保摘要在服务重启后不丢失。
|
||||
- ✅ **持久化存储**: 使用 Open WebUI 的共享数据库连接 - 自动支持任何数据库后端(PostgreSQL、SQLite 等)。
|
||||
- ✅ **灵活保留策略**: 可自由配置保留对话头部和尾部的消息数量,确保关键信息和上下文的连贯性。
|
||||
- ✅ **智能注入**: 将生成的历史摘要智能地注入到新的上下文中。
|
||||
|
||||
@@ -22,18 +22,11 @@
|
||||
|
||||
## 安装与配置
|
||||
|
||||
### 1. 环境变量
|
||||
### 1. 数据库(自动)
|
||||
|
||||
本插件的运行依赖于数据库,您**必须**在 Open WebUI 的环境变量中配置 `DATABASE_URL`。
|
||||
本插件自动使用 Open WebUI 的共享数据库连接。**无需额外的数据库配置。**
|
||||
|
||||
- **PostgreSQL 示例**:
|
||||
```
|
||||
DATABASE_URL=postgresql://user:password@host:5432/openwebui
|
||||
```
|
||||
- **SQLite 示例**:
|
||||
```
|
||||
DATABASE_URL=sqlite:///path/to/your/data/webui.db
|
||||
```
|
||||
`chat_summary` 表将在首次运行时自动创建。
|
||||
|
||||
### 2. 过滤器顺序
|
||||
|
||||
@@ -101,8 +94,8 @@
|
||||
|
||||
## 故障排除
|
||||
|
||||
- **问题:数据库连接失败**
|
||||
- **解决**:请确认 `DATABASE_URL` 环境变量已正确设置,并且数据库服务运行正常。
|
||||
- **问题:数据库表未创建**
|
||||
- **解决**:确保 Open WebUI 已正确配置数据库,并查看 Open WebUI 的日志以获取详细的错误信息。
|
||||
|
||||
- **问题:摘要未生成**
|
||||
- **解决**:检查 `compression_threshold_tokens` 是否已达到,并确认 `summary_model` 配置正确。查看日志以获取详细错误。
|
||||
|
||||
@@ -5,7 +5,7 @@ author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
description: Reduces token consumption in long conversations while maintaining coherence through intelligent summarization and message compression.
|
||||
version: 1.0.1
|
||||
version: 1.1.0
|
||||
license: MIT
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -49,14 +49,13 @@ Phase 2: Outlet (Post-response processing)
|
||||
💾 Storage
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
This filter uses a database for persistent storage, configured via the `DATABASE_URL` environment variable. It supports both PostgreSQL and SQLite.
|
||||
This filter uses Open WebUI's shared database connection for persistent storage.
|
||||
It automatically reuses Open WebUI's internal SQLAlchemy engine and SessionLocal,
|
||||
making the plugin database-agnostic and ensuring compatibility with any database
|
||||
backend that Open WebUI supports (PostgreSQL, SQLite, etc.).
|
||||
|
||||
Configuration:
|
||||
- The `DATABASE_URL` environment variable must be set.
|
||||
- PostgreSQL Example: `postgresql://user:password@host:5432/openwebui`
|
||||
- SQLite Example: `sqlite:///path/to/your/database.db`
|
||||
|
||||
The filter automatically selects the appropriate database driver based on the `DATABASE_URL` prefix (`postgres` or `sqlite`).
|
||||
No additional database configuration is required - the plugin inherits
|
||||
Open WebUI's database settings automatically.
|
||||
|
||||
Table Structure (`chat_summary`):
|
||||
- id: Primary Key (auto-increment)
|
||||
@@ -142,21 +141,8 @@ debug_mode
|
||||
🔧 Deployment
|
||||
═══════════════════════════════════════════════════════
|
||||
|
||||
Docker Compose Example:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
services:
|
||||
openwebui:
|
||||
environment:
|
||||
DATABASE_URL: postgresql://user:password@postgres:5432/openwebui
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
environment:
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_DB: openwebui
|
||||
The plugin automatically uses Open WebUI's shared database connection.
|
||||
No additional database configuration is required.
|
||||
|
||||
Suggested Filter Installation Order:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
@@ -201,9 +187,10 @@ Statistics:
|
||||
⚠️ Important Notes
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. Database Permissions
|
||||
⚠ Ensure the user specified in `DATABASE_URL` has permissions to create tables.
|
||||
⚠ The `chat_summary` table will be created automatically on first run.
|
||||
1. Database Connection
|
||||
✓ The plugin uses Open WebUI's shared database connection automatically.
|
||||
✓ No additional configuration is required.
|
||||
✓ The `chat_summary` table will be created automatically on first run.
|
||||
|
||||
2. Retention Policy
|
||||
⚠ The `keep_first` setting is crucial for preserving initial messages that contain system prompts. Configure it as needed.
|
||||
@@ -226,13 +213,11 @@ Statistics:
|
||||
🐛 Troubleshooting
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Problem: Database connection failed
|
||||
Problem: Database table not created
|
||||
Solution:
|
||||
1. Verify that the `DATABASE_URL` environment variable is set correctly.
|
||||
2. Confirm that `DATABASE_URL` starts with either `sqlite` or `postgres`.
|
||||
3. Ensure the database service is running and network connectivity is normal.
|
||||
4. Validate the username, password, host, and port in the connection URL.
|
||||
5. Check the Open WebUI container logs for detailed error messages.
|
||||
1. Ensure Open WebUI is properly configured with a database.
|
||||
2. Check the Open WebUI container logs for detailed error messages.
|
||||
3. Verify that Open WebUI's database connection is working correctly.
|
||||
|
||||
Problem: Summary not generated
|
||||
Solution:
|
||||
@@ -258,7 +243,6 @@ from typing import Optional, Dict, Any, List, Union, Callable, Awaitable
|
||||
import asyncio
|
||||
import json
|
||||
import hashlib
|
||||
import os
|
||||
import time
|
||||
|
||||
# Open WebUI built-in imports
|
||||
@@ -267,6 +251,11 @@ from open_webui.models.users import Users
|
||||
from fastapi.requests import Request
|
||||
from open_webui.main import app as webui_app
|
||||
|
||||
# Open WebUI internal database (re-use shared connection)
|
||||
from open_webui.internal.db import engine as owui_engine
|
||||
from open_webui.internal.db import Session as owui_Session
|
||||
from open_webui.internal.db import Base as owui_Base
|
||||
|
||||
# Try to import tiktoken
|
||||
try:
|
||||
import tiktoken
|
||||
@@ -274,18 +263,15 @@ except ImportError:
|
||||
tiktoken = None
|
||||
|
||||
# Database imports
|
||||
from sqlalchemy import create_engine, Column, String, Text, DateTime, Integer
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import Column, String, Text, DateTime, Integer, inspect
|
||||
from datetime import datetime
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class ChatSummary(Base):
|
||||
class ChatSummary(owui_Base):
|
||||
"""Chat Summary Storage Table"""
|
||||
|
||||
__tablename__ = "chat_summary"
|
||||
__table_args__ = {"extend_existing": True}
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
chat_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
@@ -298,74 +284,29 @@ class ChatSummary(Base):
|
||||
class Filter:
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
self._db_engine = owui_engine
|
||||
self._SessionLocal = owui_Session
|
||||
self.temp_state = {} # Used to pass temporary data between inlet and outlet
|
||||
self._init_database()
|
||||
|
||||
def _init_database(self):
|
||||
"""Initializes the database connection and table."""
|
||||
"""Initializes the database table using Open WebUI's shared connection."""
|
||||
try:
|
||||
database_url = os.getenv("DATABASE_URL")
|
||||
|
||||
if not database_url:
|
||||
# Check if table exists using SQLAlchemy inspect
|
||||
inspector = inspect(self._db_engine)
|
||||
if not inspector.has_table("chat_summary"):
|
||||
# Create the chat_summary table if it doesn't exist
|
||||
ChatSummary.__table__.create(bind=self._db_engine, checkfirst=True)
|
||||
print(
|
||||
"[Database] ❌ Error: DATABASE_URL environment variable is not set. Please set this variable."
|
||||
"[Database] ✅ Successfully created chat_summary table using Open WebUI's shared database connection."
|
||||
)
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
return
|
||||
|
||||
db_type = None
|
||||
engine_args = {}
|
||||
|
||||
if database_url.startswith("sqlite"):
|
||||
db_type = "SQLite"
|
||||
engine_args = {
|
||||
"connect_args": {"check_same_thread": False},
|
||||
"echo": False,
|
||||
}
|
||||
elif database_url.startswith("postgres"):
|
||||
db_type = "PostgreSQL"
|
||||
if database_url.startswith("postgres://"):
|
||||
database_url = database_url.replace(
|
||||
"postgres://", "postgresql://", 1
|
||||
)
|
||||
print(
|
||||
"[Database] ℹ️ Automatically converted postgres:// to postgresql://"
|
||||
)
|
||||
engine_args = {
|
||||
"pool_pre_ping": True,
|
||||
"pool_recycle": 3600,
|
||||
"echo": False,
|
||||
}
|
||||
else:
|
||||
print(
|
||||
f"[Database] ❌ Error: Unsupported database type. DATABASE_URL must start with 'sqlite' or 'postgres'. Current value: {database_url}"
|
||||
"[Database] ✅ Using Open WebUI's shared database connection. chat_summary table already exists."
|
||||
)
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
return
|
||||
|
||||
# Create database engine
|
||||
self._db_engine = create_engine(database_url, **engine_args)
|
||||
|
||||
# Create session factory
|
||||
self._SessionLocal = sessionmaker(
|
||||
autocommit=False, autoflush=False, bind=self._db_engine
|
||||
)
|
||||
|
||||
# Create table if it doesn't exist
|
||||
Base.metadata.create_all(bind=self._db_engine)
|
||||
|
||||
print(
|
||||
f"[Database] ✅ Successfully connected to {db_type} and initialized the chat_summary table."
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Database] ❌ Initialization failed: {str(e)}")
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
|
||||
class Valves(BaseModel):
|
||||
priority: int = Field(
|
||||
@@ -416,14 +357,8 @@ class Filter:
|
||||
|
||||
def _save_summary(self, chat_id: str, summary: str, compressed_count: int):
|
||||
"""Saves the summary to the database."""
|
||||
if not self._SessionLocal:
|
||||
if self.valves.debug_mode:
|
||||
print("[Storage] Database not initialized, skipping summary save.")
|
||||
return
|
||||
|
||||
try:
|
||||
session = self._SessionLocal()
|
||||
try:
|
||||
with self._SessionLocal() as session:
|
||||
# Find existing record
|
||||
existing = session.query(ChatSummary).filter_by(chat_id=chat_id).first()
|
||||
|
||||
@@ -457,27 +392,18 @@ class Filter:
|
||||
f"[Storage] Summary has been {action.lower()} in the database (Chat ID: {chat_id})"
|
||||
)
|
||||
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Storage] ❌ Database save failed: {str(e)}")
|
||||
|
||||
def _load_summary_record(self, chat_id: str) -> Optional[ChatSummary]:
|
||||
"""Loads the summary record object from the database."""
|
||||
if not self._SessionLocal:
|
||||
return None
|
||||
|
||||
try:
|
||||
session = self._SessionLocal()
|
||||
try:
|
||||
with self._SessionLocal() as session:
|
||||
record = session.query(ChatSummary).filter_by(chat_id=chat_id).first()
|
||||
if record:
|
||||
# Detach the object from the session so it can be used after session close
|
||||
session.expunge(record)
|
||||
return record
|
||||
finally:
|
||||
session.close()
|
||||
except Exception as e:
|
||||
print(f"[Load] ❌ Database read failed: {str(e)}")
|
||||
return None
|
||||
|
||||
@@ -5,7 +5,7 @@ author: Fu-Jie
|
||||
author_url: https://github.com/Fu-Jie
|
||||
funding_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||
description: 通过智能摘要和消息压缩,降低长对话的 token 消耗,同时保持对话连贯性。
|
||||
version: 1.0.0
|
||||
version: 1.1.0
|
||||
license: MIT
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -49,14 +49,12 @@ license: MIT
|
||||
💾 存储方案
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
本过滤器使用数据库进行持久化存储,通过 `DATABASE_URL` 环境变量进行配置,支持 PostgreSQL 和 SQLite。
|
||||
本过滤器使用 Open WebUI 的共享数据库连接进行持久化存储。
|
||||
它自动复用 Open WebUI 内部的 SQLAlchemy 引擎和 SessionLocal,
|
||||
使插件与数据库类型无关,并确保与 Open WebUI 支持的任何数据库后端
|
||||
(PostgreSQL、SQLite 等)兼容。
|
||||
|
||||
配置方式:
|
||||
- 必须设置 `DATABASE_URL` 环境变量。
|
||||
- PostgreSQL 示例: `postgresql://user:password@host:5432/openwebui`
|
||||
- SQLite 示例: `sqlite:///path/to/your/database.db`
|
||||
|
||||
过滤器会根据 `DATABASE_URL` 的前缀(`postgres` 或 `sqlite`)自动选择合适的数据库驱动。
|
||||
无需额外的数据库配置 - 插件自动继承 Open WebUI 的数据库设置。
|
||||
|
||||
表结构:
|
||||
- id: 主键(自增)
|
||||
@@ -142,21 +140,8 @@ debug_mode (调试模式)
|
||||
🔧 部署配置
|
||||
═══════════════════════════════════════════════════════
|
||||
|
||||
Docker Compose 示例:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
services:
|
||||
openwebui:
|
||||
environment:
|
||||
DATABASE_URL: postgresql://user:password@postgres:5432/openwebui
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
environment:
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_DB: openwebui
|
||||
插件自动使用 Open WebUI 的共享数据库连接。
|
||||
无需额外的数据库配置。
|
||||
|
||||
过滤器安装顺序建议:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
@@ -201,9 +186,10 @@ Docker Compose 示例:
|
||||
⚠️ 注意事项
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. 数据库权限
|
||||
⚠ 确保 `DATABASE_URL` 指向的用户有创建表的权限。
|
||||
⚠ 首次运行会自动创建 `chat_summary` 表。
|
||||
1. 数据库连接
|
||||
✓ 插件自动使用 Open WebUI 的共享数据库连接。
|
||||
✓ 无需额外配置。
|
||||
✓ 首次运行会自动创建 `chat_summary` 表。
|
||||
|
||||
2. 保留策略
|
||||
⚠ `keep_first` 配置对于保留包含提示或环境变量的初始消息非常重要。请根据需要进行配置。
|
||||
@@ -226,13 +212,11 @@ Docker Compose 示例:
|
||||
🐛 故障排除
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
问题:数据库连接失败
|
||||
问题:数据库表未创建
|
||||
解决:
|
||||
1. 确认 `DATABASE_URL` 环境变量已正确设置。
|
||||
2. 确认 `DATABASE_URL` 以 `sqlite` 或 `postgres` 开头。
|
||||
3. 确认数据库服务正在运行,并且网络连接正常。
|
||||
4. 验证连接 URL 中的用户名、密码、主机和端口是否正确。
|
||||
5. 查看 Open WebUI 的容器日志以获取详细的错误信息。
|
||||
1. 确保 Open WebUI 已正确配置数据库。
|
||||
2. 查看 Open WebUI 的容器日志以获取详细的错误信息。
|
||||
3. 验证 Open WebUI 的数据库连接是否正常工作。
|
||||
|
||||
问题:摘要未生成
|
||||
解决:
|
||||
@@ -258,7 +242,6 @@ from typing import Optional, Dict, Any, List, Union, Callable, Awaitable
|
||||
import asyncio
|
||||
import json
|
||||
import hashlib
|
||||
import os
|
||||
import time
|
||||
|
||||
# Open WebUI 内置导入
|
||||
@@ -267,6 +250,11 @@ from open_webui.models.users import Users
|
||||
from fastapi.requests import Request
|
||||
from open_webui.main import app as webui_app
|
||||
|
||||
# Open WebUI 内部数据库 (复用共享连接)
|
||||
from open_webui.internal.db import engine as owui_engine
|
||||
from open_webui.internal.db import Session as owui_Session
|
||||
from open_webui.internal.db import Base as owui_Base
|
||||
|
||||
# 尝试导入 tiktoken
|
||||
try:
|
||||
import tiktoken
|
||||
@@ -274,18 +262,15 @@ except ImportError:
|
||||
tiktoken = None
|
||||
|
||||
# 数据库导入
|
||||
from sqlalchemy import create_engine, Column, String, Text, DateTime, Integer
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import Column, String, Text, DateTime, Integer, inspect
|
||||
from datetime import datetime
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class ChatSummary(Base):
|
||||
class ChatSummary(owui_Base):
|
||||
"""对话摘要存储表"""
|
||||
|
||||
__tablename__ = "chat_summary"
|
||||
__table_args__ = {"extend_existing": True}
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
chat_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
@@ -298,68 +283,29 @@ class ChatSummary(Base):
|
||||
class Filter:
|
||||
def __init__(self):
|
||||
self.valves = self.Valves()
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
self._db_engine = owui_engine
|
||||
self._SessionLocal = owui_Session
|
||||
self.temp_state = {} # 用于在 inlet 和 outlet 之间传递临时数据
|
||||
self._init_database()
|
||||
|
||||
def _init_database(self):
|
||||
"""初始化数据库连接和表"""
|
||||
"""使用 Open WebUI 的共享连接初始化数据库表"""
|
||||
try:
|
||||
database_url = os.getenv("DATABASE_URL")
|
||||
|
||||
if not database_url:
|
||||
print("[数据库] ❌ 错误: DATABASE_URL 环境变量未设置。请设置该变量。")
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
return
|
||||
|
||||
db_type = None
|
||||
engine_args = {}
|
||||
|
||||
if database_url.startswith("sqlite"):
|
||||
db_type = "SQLite"
|
||||
engine_args = {
|
||||
"connect_args": {"check_same_thread": False},
|
||||
"echo": False,
|
||||
}
|
||||
elif database_url.startswith("postgres"):
|
||||
db_type = "PostgreSQL"
|
||||
if database_url.startswith("postgres://"):
|
||||
database_url = database_url.replace(
|
||||
"postgres://", "postgresql://", 1
|
||||
)
|
||||
print("[数据库] ℹ️ 已自动将 postgres:// 转换为 postgresql://")
|
||||
engine_args = {
|
||||
"pool_pre_ping": True,
|
||||
"pool_recycle": 3600,
|
||||
"echo": False,
|
||||
}
|
||||
# 使用 SQLAlchemy inspect 检查表是否存在
|
||||
inspector = inspect(self._db_engine)
|
||||
if not inspector.has_table("chat_summary"):
|
||||
# 如果表不存在则创建
|
||||
ChatSummary.__table__.create(bind=self._db_engine, checkfirst=True)
|
||||
print(
|
||||
"[数据库] ✅ 使用 Open WebUI 的共享数据库连接成功创建 chat_summary 表。"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"[数据库] ❌ 错误: 不支持的数据库类型。DATABASE_URL 必须以 'sqlite' 或 'postgres' 开头。当前值: {database_url}"
|
||||
"[数据库] ✅ 使用 Open WebUI 的共享数据库连接。chat_summary 表已存在。"
|
||||
)
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
return
|
||||
|
||||
# 创建数据库引擎
|
||||
self._db_engine = create_engine(database_url, **engine_args)
|
||||
|
||||
# 创建会话工厂
|
||||
self._SessionLocal = sessionmaker(
|
||||
autocommit=False, autoflush=False, bind=self._db_engine
|
||||
)
|
||||
|
||||
# 创建表(如果不存在)
|
||||
Base.metadata.create_all(bind=self._db_engine)
|
||||
|
||||
print(f"[数据库] ✅ 成功连接到 {db_type} 并初始化 chat_summary 表")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[数据库] ❌ 初始化失败: {str(e)}")
|
||||
self._db_engine = None
|
||||
self._SessionLocal = None
|
||||
|
||||
class Valves(BaseModel):
|
||||
priority: int = Field(
|
||||
@@ -401,14 +347,8 @@ class Filter:
|
||||
|
||||
def _save_summary(self, chat_id: str, summary: str, compressed_count: int):
|
||||
"""保存摘要到数据库"""
|
||||
if not self._SessionLocal:
|
||||
if self.valves.debug_mode:
|
||||
print("[存储] 数据库未初始化,跳过保存摘要")
|
||||
return
|
||||
|
||||
try:
|
||||
session = self._SessionLocal()
|
||||
try:
|
||||
with self._SessionLocal() as session:
|
||||
# 查找现有记录
|
||||
existing = session.query(ChatSummary).filter_by(chat_id=chat_id).first()
|
||||
|
||||
@@ -440,27 +380,18 @@ class Filter:
|
||||
action = "更新" if existing else "创建"
|
||||
print(f"[存储] 摘要已{action}到数据库 (Chat ID: {chat_id})")
|
||||
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"[存储] ❌ 数据库保存失败: {str(e)}")
|
||||
|
||||
def _load_summary_record(self, chat_id: str) -> Optional[ChatSummary]:
|
||||
"""从数据库加载摘要记录对象"""
|
||||
if not self._SessionLocal:
|
||||
return None
|
||||
|
||||
try:
|
||||
session = self._SessionLocal()
|
||||
try:
|
||||
with self._SessionLocal() as session:
|
||||
record = session.query(ChatSummary).filter_by(chat_id=chat_id).first()
|
||||
if record:
|
||||
# Detach the object from the session so it can be used after session close
|
||||
session.expunge(record)
|
||||
return record
|
||||
finally:
|
||||
session.close()
|
||||
except Exception as e:
|
||||
print(f"[加载] ❌ 数据库读取失败: {str(e)}")
|
||||
return None
|
||||
@@ -815,7 +746,7 @@ class Filter:
|
||||
|
||||
# 计算当前总 Token (使用摘要模型进行计数)
|
||||
total_tokens = await asyncio.to_thread(
|
||||
self._calculate_messages_tokens, messages, summary_model_id
|
||||
self._calculate_messages_tokens, messages
|
||||
)
|
||||
|
||||
if total_tokens > max_context_tokens:
|
||||
|
||||
Reference in New Issue
Block a user