From 500e090b11557a614d8f1c6872bea3fadb17b33b Mon Sep 17 00:00:00 2001 From: fujie Date: Wed, 21 Jan 2026 21:51:58 +0800 Subject: [PATCH] fix: resolve TypeError and improve Pydantic compatibility in async-context-compression v1.2.2 --- .../filters/async-context-compression.md | 2 +- .../filters/async-context-compression.zh.md | 2 +- docs/plugins/filters/index.md | 2 +- docs/plugins/filters/index.zh.md | 2 +- .../async-context-compression/README.md | 6 +- .../async-context-compression/README_CN.md | 6 +- .../async_context_compression.py | 60 ++++++++++--------- .../async_context_compression_cn.py | 59 +++++++++--------- 8 files changed, 79 insertions(+), 60 deletions(-) diff --git a/docs/plugins/filters/async-context-compression.md b/docs/plugins/filters/async-context-compression.md index 5aa8b78..874f530 100644 --- a/docs/plugins/filters/async-context-compression.md +++ b/docs/plugins/filters/async-context-compression.md @@ -1,7 +1,7 @@ # Async Context Compression Filter -v1.2.1 +v1.2.2 Reduces token consumption in long conversations through intelligent summarization while maintaining conversational coherence. diff --git a/docs/plugins/filters/async-context-compression.zh.md b/docs/plugins/filters/async-context-compression.zh.md index 221071d..a653d9c 100644 --- a/docs/plugins/filters/async-context-compression.zh.md +++ b/docs/plugins/filters/async-context-compression.zh.md @@ -1,7 +1,7 @@ # Async Context Compression(异步上下文压缩) Filter -v1.2.1 +v1.2.2 通过智能摘要减少长对话的 token 消耗,同时保持对话连贯。 diff --git a/docs/plugins/filters/index.md b/docs/plugins/filters/index.md index b56f444..deb8e52 100644 --- a/docs/plugins/filters/index.md +++ b/docs/plugins/filters/index.md @@ -22,7 +22,7 @@ Filters act as middleware in the message pipeline: Reduces token consumption in long conversations through intelligent summarization while maintaining coherence. - **Version:** 1.2.1 + **Version:** 1.2.2 [:octicons-arrow-right-24: Documentation](async-context-compression.md) diff --git a/docs/plugins/filters/index.zh.md b/docs/plugins/filters/index.zh.md index 9b091ec..e565c9e 100644 --- a/docs/plugins/filters/index.zh.md +++ b/docs/plugins/filters/index.zh.md @@ -22,7 +22,7 @@ Filter 充当消息管线中的中间件: 通过智能总结减少长对话的 token 消耗,同时保持连贯性。 - **版本:** 1.2.1 + **版本:** 1.2.2 [:octicons-arrow-right-24: 查看文档](async-context-compression.md) diff --git a/plugins/filters/async-context-compression/README.md b/plugins/filters/async-context-compression/README.md index 099cbb8..ed82d2f 100644 --- a/plugins/filters/async-context-compression/README.md +++ b/plugins/filters/async-context-compression/README.md @@ -1,9 +1,13 @@ # Async Context Compression Filter -**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 1.2.1 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT +**Author:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **Version:** 1.2.2 | **Project:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **License:** MIT This filter reduces token consumption in long conversations through intelligent summarization and message compression while keeping conversations coherent. +## What's new in 1.2.2 +- **Critical Fix**: Resolved `TypeError: 'str' object is not callable` caused by variable name conflict in logging function. +- **Compatibility**: Enhanced `params` handling to support Pydantic objects, improving compatibility with different OpenWebUI versions. + ## What's new in 1.2.1 - **Smart Configuration**: Automatically detects base model settings for custom models and adds `summary_model_max_context` for independent summary limits. diff --git a/plugins/filters/async-context-compression/README_CN.md b/plugins/filters/async-context-compression/README_CN.md index 122a507..025bb93 100644 --- a/plugins/filters/async-context-compression/README_CN.md +++ b/plugins/filters/async-context-compression/README_CN.md @@ -1,11 +1,15 @@ # 异步上下文压缩过滤器 -**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 1.2.1 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT +**作者:** [Fu-Jie](https://github.com/Fu-Jie/awesome-openwebui) | **版本:** 1.2.2 | **项目:** [Awesome OpenWebUI](https://github.com/Fu-Jie/awesome-openwebui) | **许可证:** MIT > **重要提示**:为了确保所有过滤器的可维护性和易用性,每个过滤器都应附带清晰、完整的文档,以确保其功能、配置和使用方法得到充分说明。 本过滤器通过智能摘要和消息压缩技术,在保持对话连贯性的同时,显著降低长对话的 Token 消耗。 +## 1.2.2 版本更新 +- **严重错误修复**: 解决了因日志函数变量名冲突导致的 `TypeError: 'str' object is not callable` 错误。 +- **兼容性增强**: 改进了 `params` 处理逻辑以支持 Pydantic 对象,提高了对不同 OpenWebUI 版本的兼容性。 + ## 1.2.1 版本更新 - **智能配置增强**: 自动检测自定义模型的基础模型配置,并新增 `summary_model_max_context` 参数以独立控制摘要模型的上下文限制。 diff --git a/plugins/filters/async-context-compression/async_context_compression.py b/plugins/filters/async-context-compression/async_context_compression.py index 6eb6a27..ed2c8f0 100644 --- a/plugins/filters/async-context-compression/async_context_compression.py +++ b/plugins/filters/async-context-compression/async_context_compression.py @@ -5,7 +5,7 @@ author: Fu-Jie author_url: https://github.com/Fu-Jie/awesome-openwebui funding_url: https://github.com/open-webui description: Reduces token consumption in long conversations while maintaining coherence through intelligent summarization and message compression. -version: 1.2.1 +version: 1.2.2 openwebui_id: b1655bc8-6de9-4cad-8cb5-a6f7829a02ce license: MIT @@ -839,7 +839,7 @@ class Filter: except Exception as e: logger.error(f"Error emitting debug log: {e}") - async def _log(self, message: str, type: str = "info", event_call=None): + async def _log(self, message: str, log_type: str = "info", event_call=None): """Unified logging to both backend (print) and frontend (console.log)""" # Backend logging if self.valves.debug_mode: @@ -849,11 +849,11 @@ class Filter: if self.valves.show_debug_log and event_call: try: css = "color: #3b82f6;" # Blue default - if type == "error": + if log_type == "error": css = "color: #ef4444; font-weight: bold;" # Red - elif type == "warning": + elif log_type == "warning": css = "color: #f59e0b;" # Orange - elif type == "success": + elif log_type == "success": css = "color: #10b981; font-weight: bold;" # Green # Clean message for frontend: remove separators and extra newlines @@ -999,6 +999,7 @@ class Filter: # 2. For base models: check messages for role='system' system_prompt_content = None + # Try to get from DB (custom model) # Try to get from DB (custom model) try: model_id = body.get("model") @@ -1026,12 +1027,17 @@ class Filter: # Handle case where params is a JSON string if isinstance(params, str): params = json.loads(params) + # Convert Pydantic model to dict if needed + elif hasattr(params, "model_dump"): + params = params.model_dump() + elif hasattr(params, "dict"): + params = params.dict() - # Handle dict or Pydantic object + # Now params should be a dict if isinstance(params, dict): system_prompt_content = params.get("system") else: - # Assume Pydantic model or object + # Fallback: try getattr system_prompt_content = getattr(params, "system", None) if system_prompt_content: @@ -1050,7 +1056,7 @@ class Filter: if self.valves.show_debug_log and __event_call__: await self._log( f"[Inlet] ❌ Failed to parse model params: {e}", - type="error", + log_type="error", event_call=__event_call__, ) @@ -1071,7 +1077,7 @@ class Filter: if self.valves.show_debug_log and __event_call__: await self._log( f"[Inlet] ❌ Error fetching system prompt from DB: {e}", - type="error", + log_type="error", event_call=__event_call__, ) if self.valves.debug_mode: @@ -1125,7 +1131,7 @@ class Filter: if not chat_id: await self._log( "[Inlet] ❌ Missing chat_id in metadata, skipping compression", - type="error", + log_type="error", event_call=__event_call__, ) return body @@ -1154,7 +1160,7 @@ class Filter: else: await self._log( f"[Inlet] ⚠️ Invalid Model Configs (Raw: '{raw_config}'): No valid configs parsed. Expected format: 'model_id:threshold:max_context'", - type="warning", + log_type="warning", event_call=__event_call__, ) else: @@ -1258,7 +1264,7 @@ class Filter: if total_tokens > max_context_tokens: await self._log( f"[Inlet] ⚠️ Candidate prompt ({total_tokens} Tokens) exceeds limit ({max_context_tokens}). Reducing history...", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1395,7 +1401,7 @@ class Filter: await self._log( f"[Inlet] Applied summary: {system_info} + Head({len(head_messages)} msg, {head_tokens}t) + Summary({summary_tokens}t) + Tail({len(tail_messages)} msg, {tail_tokens}t) = Total({total_section_tokens}t)", - type="success", + log_type="success", event_call=__event_call__, ) @@ -1455,7 +1461,7 @@ class Filter: if total_tokens > max_context_tokens: await self._log( f"[Inlet] ⚠️ Original messages ({total_tokens} Tokens) exceed limit ({max_context_tokens}). Reducing history...", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1523,7 +1529,7 @@ class Filter: if not chat_id: await self._log( "[Outlet] ❌ Missing chat_id in metadata, skipping compression", - type="error", + log_type="error", event_call=__event_call__, ) return body @@ -1625,7 +1631,7 @@ class Filter: if current_tokens >= compression_threshold_tokens: await self._log( f"[🔍 Background Calculation] ⚡ Compression threshold triggered (Token: {current_tokens} >= {compression_threshold_tokens})", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1648,7 +1654,7 @@ class Filter: except Exception as e: await self._log( f"[🔍 Background Calculation] ❌ Error: {str(e)}", - type="error", + log_type="error", event_call=__event_call__, ) @@ -1687,7 +1693,7 @@ class Filter: target_compressed_count = max(0, len(messages) - self.valves.keep_last) await self._log( f"[🤖 Async Summary Task] ⚠️ target_compressed_count is None, estimating: {target_compressed_count}", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1734,7 +1740,7 @@ class Filter: if not summary_model_id: await self._log( "[🤖 Async Summary Task] ⚠️ Summary model does not exist, skipping compression", - type="warning", + log_type="warning", event_call=__event_call__, ) return @@ -1765,7 +1771,7 @@ class Filter: excess_tokens = estimated_input_tokens - max_context_tokens await self._log( f"[🤖 Async Summary Task] ⚠️ Middle messages ({middle_tokens} Tokens) + Buffer exceed summary model limit ({max_context_tokens}), need to remove approx {excess_tokens} Tokens", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1822,7 +1828,7 @@ class Filter: if not new_summary: await self._log( "[🤖 Async Summary Task] ⚠️ Summary generation returned empty result, skipping save", - type="warning", + log_type="warning", event_call=__event_call__, ) return @@ -1851,7 +1857,7 @@ class Filter: await self._log( f"[🤖 Async Summary Task] ✅ Complete! New summary length: {len(new_summary)} characters", - type="success", + log_type="success", event_call=__event_call__, ) await self._log( @@ -1957,14 +1963,14 @@ class Filter: except Exception as e: await self._log( f"[Status] Error calculating tokens: {e}", - type="error", + log_type="error", event_call=__event_call__, ) except Exception as e: await self._log( f"[🤖 Async Summary Task] ❌ Error: {str(e)}", - type="error", + log_type="error", event_call=__event_call__, ) @@ -2066,7 +2072,7 @@ Based on the content above, generate the summary: if not model: await self._log( "[🤖 LLM Call] ⚠️ Summary model does not exist, skipping summary generation", - type="warning", + log_type="warning", event_call=__event_call__, ) return "" @@ -2133,7 +2139,7 @@ Based on the content above, generate the summary: await self._log( f"[🤖 LLM Call] ✅ Successfully received summary", - type="success", + log_type="success", event_call=__event_call__, ) @@ -2154,7 +2160,7 @@ Based on the content above, generate the summary: await self._log( f"[🤖 LLM Call] ❌ {error_message}", - type="error", + log_type="error", event_call=__event_call__, ) diff --git a/plugins/filters/async-context-compression/async_context_compression_cn.py b/plugins/filters/async-context-compression/async_context_compression_cn.py index c449a23..36969ec 100644 --- a/plugins/filters/async-context-compression/async_context_compression_cn.py +++ b/plugins/filters/async-context-compression/async_context_compression_cn.py @@ -5,7 +5,7 @@ author: Fu-Jie author_url: https://github.com/Fu-Jie/awesome-openwebui funding_url: https://github.com/open-webui description: 通过智能摘要和消息压缩,降低长对话的 token 消耗,同时保持对话连贯性。 -version: 1.2.1 +version: 1.2.2 openwebui_id: 5c0617cb-a9e4-4bd6-a440-d276534ebd18 license: MIT @@ -787,7 +787,7 @@ class Filter: except Exception as e: print(f"Error emitting debug log: {e}") - async def _log(self, message: str, type: str = "info", event_call=None): + async def _log(self, message: str, log_type: str = "info", event_call=None): """统一日志输出到后端 (print) 和前端 (console.log)""" # 后端日志 if self.valves.debug_mode: @@ -797,11 +797,11 @@ class Filter: if self.valves.show_debug_log and event_call: try: css = "color: #3b82f6;" # 默认蓝色 - if type == "error": + if log_type == "error": css = "color: #ef4444; font-weight: bold;" # 红色 - elif type == "warning": + elif log_type == "warning": css = "color: #f59e0b;" # 橙色 - elif type == "success": + elif log_type == "success": css = "color: #10b981; font-weight: bold;" # 绿色 # 清理前端消息:移除分隔符和多余换行 @@ -948,12 +948,17 @@ class Filter: # 处理 params 是 JSON 字符串的情况 if isinstance(params, str): params = json.loads(params) + # 转换 Pydantic 模型为字典 + elif hasattr(params, "model_dump"): + params = params.model_dump() + elif hasattr(params, "dict"): + params = params.dict() - # 处理字典或 Pydantic 对象 + # 处理字典 if isinstance(params, dict): system_prompt_content = params.get("system") else: - # 假设是 Pydantic 模型或对象 + # 回退:尝试 getattr system_prompt_content = getattr(params, "system", None) if system_prompt_content: @@ -972,7 +977,7 @@ class Filter: if self.valves.show_debug_log and __event_call__: await self._log( f"[Inlet] ❌ 解析模型参数失败: {e}", - type="error", + log_type="error", event_call=__event_call__, ) @@ -986,7 +991,7 @@ class Filter: if self.valves.show_debug_log and __event_call__: await self._log( f"[Inlet] ❌ 数据库中未找到模型", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -994,7 +999,7 @@ class Filter: if self.valves.show_debug_log and __event_call__: await self._log( f"[Inlet] ❌ 从数据库获取系统提示词错误: {e}", - type="error", + log_type="error", event_call=__event_call__, ) if self.valves.debug_mode: @@ -1048,7 +1053,7 @@ class Filter: if not chat_id: await self._log( "[Inlet] ❌ metadata 中缺少 chat_id,跳过压缩", - type="error", + log_type="error", event_call=__event_call__, ) return body @@ -1154,7 +1159,7 @@ class Filter: if total_tokens > max_context_tokens: await self._log( f"[Inlet] ⚠️ 候选提示词 ({total_tokens} Tokens) 超过上限 ({max_context_tokens})。正在缩减历史记录...", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1290,7 +1295,7 @@ class Filter: await self._log( f"[Inlet] 应用摘要: {system_info} + Head({len(head_messages)} 条, {head_tokens}t) + Summary({summary_tokens}t) + Tail({len(tail_messages)} 条, {tail_tokens}t) = Total({total_section_tokens}t)", - type="success", + log_type="success", event_call=__event_call__, ) @@ -1350,7 +1355,7 @@ class Filter: if total_tokens > max_context_tokens: await self._log( f"[Inlet] ⚠️ 原始消息 ({total_tokens} Tokens) 超过上限 ({max_context_tokens})。正在缩减历史记录...", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1420,7 +1425,7 @@ class Filter: if not chat_id: await self._log( "[Outlet] ❌ metadata 中缺少 chat_id,跳过压缩", - type="error", + log_type="error", event_call=__event_call__, ) return body @@ -1486,7 +1491,7 @@ class Filter: if current_tokens >= compression_threshold_tokens: await self._log( f"[🔍 后台计算] ⚡ 触发压缩阈值 (Token: {current_tokens} >= {compression_threshold_tokens})", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1509,7 +1514,7 @@ class Filter: except Exception as e: await self._log( f"[🔍 后台计算] ❌ 错误: {str(e)}", - type="error", + log_type="error", event_call=__event_call__, ) @@ -1546,7 +1551,7 @@ class Filter: target_compressed_count = max(0, len(messages) - self.valves.keep_last) await self._log( f"[🤖 异步摘要任务] ⚠️ target_compressed_count 为 None,进行估算: {target_compressed_count}", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1593,7 +1598,7 @@ class Filter: if not summary_model_id: await self._log( "[🤖 异步摘要任务] ⚠️ 摘要模型不存在,跳过压缩", - type="warning", + log_type="warning", event_call=__event_call__, ) return @@ -1624,7 +1629,7 @@ class Filter: excess_tokens = estimated_input_tokens - max_context_tokens await self._log( f"[🤖 异步摘要任务] ⚠️ 中间消息 ({middle_tokens} Tokens) + 缓冲超过摘要模型上限 ({max_context_tokens}),需要移除约 {excess_tokens} Token", - type="warning", + log_type="warning", event_call=__event_call__, ) @@ -1681,7 +1686,7 @@ class Filter: if not new_summary: await self._log( "[🤖 异步摘要任务] ⚠️ 摘要生成返回空结果,跳过保存", - type="warning", + log_type="warning", event_call=__event_call__, ) return @@ -1710,7 +1715,7 @@ class Filter: await self._log( f"[🤖 异步摘要任务] ✅ 完成!新摘要长度: {len(new_summary)} 字符", - type="success", + log_type="success", event_call=__event_call__, ) await self._log( @@ -1821,14 +1826,14 @@ class Filter: except Exception as e: await self._log( f"[Status] 计算 Token 错误: {e}", - type="error", + log_type="error", event_call=__event_call__, ) except Exception as e: await self._log( f"[🤖 异步摘要任务] ❌ 错误: {str(e)}", - type="error", + log_type="error", event_call=__event_call__, ) @@ -1928,7 +1933,7 @@ class Filter: if not model: await self._log( "[🤖 LLM 调用] ⚠️ 摘要模型不存在,跳过摘要生成", - type="warning", + log_type="warning", event_call=__event_call__, ) return "" @@ -1995,7 +2000,7 @@ class Filter: await self._log( f"[🤖 LLM 调用] ✅ 成功接收摘要", - type="success", + log_type="success", event_call=__event_call__, ) @@ -2016,7 +2021,7 @@ class Filter: await self._log( f"[🤖 LLM 调用] ❌ {error_message}", - type="error", + log_type="error", event_call=__event_call__, )