Refactor async-context-compression to use OpenWebUI's internal DB connection

Co-authored-by: Fu-Jie <33599649+Fu-Jie@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-31 03:52:19 +00:00
parent 501871ece1
commit e510b3b580
5 changed files with 199 additions and 246 deletions

View File

@@ -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.

View File

@@ -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 @@
-**自动压缩**: 基于消息数量阈值自动触发上下文压缩。
-**异步摘要**: 在后台生成摘要,不阻塞当前对话的响应。
-**持久化存储**: 支持 PostgreSQLSQLite 数据库,确保摘要在服务重启后不丢失
-**持久化存储**: 使用 Open WebUI 的共享数据库连接 - 自动支持任何数据库后端(PostgreSQLSQLite 等)
-**灵活保留策略**: 可自由配置保留对话头部和尾部的消息数量,确保关键信息和上下文的连贯性。
-**智能注入**: 将生成的历史摘要智能地注入到新的上下文中。
@@ -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` 配置正确。查看日志以获取详细错误。

View File

@@ -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,15 +263,11 @@ 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"
@@ -298,74 +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 = {} # 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 +356,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 +391,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

View File

@@ -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,15 +262,11 @@ 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"
@@ -298,68 +282,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 +346,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 +379,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