Skip to content

fix(channels): handle dict type for custom channels in manager.py 解决自定义频道中dict类型无法被正确读取问题 - #1991

Merged
xieyxclack merged 2 commits into
agentscope-ai:mainfrom
f3125472:main
Mar 21, 2026
Merged

xieyxclack merged 2 commits into
agentscope-ai:mainfrom
f3125472:main

Conversation

@f3125472

Copy link
Copy Markdown
Contributor

Handle both Pydantic objects (built-in) and dicts (custom channels)

Description

[English]
Fix custom channels not starting due to getattr() being used on dict type configurations in ChannelManager.

Custom channel configurations are stored as dict in Pydantic's __pydantic_extra__, while built-in channel configurations are Pydantic objects. The code uses getattr() to access configuration fields, which works for objects but returns the default value for dicts.

This PR adds isinstance() checks for all three affected fields:

  • enabled (line 196)
  • filter_tool_messages (line 200-204)
  • filter_thinking (line 205-209)

[中文]
修复 ChannelManager 中因对 dict 类型配置使用 getattr() 导致自定义通道无法启动的问题。

自定义通道配置作为 dict 存储在 Pydantic 的 __pydantic_extra__ 中,而内置通道配置是 Pydantic 对象。代码使用 getattr() 访问配置字段,这对对象有效,但对 dict 返回默认值。

本 PR 为所有三个受影响字段添加 isinstance() 检查:

  • enabled(第 196 行)
  • filter_tool_messages(第 200-204 行)
  • filter_thinking(第 205-209 行)

Related Issue: Fixes #(issue_number) or Relates to #(issue_number) Fixes #1987
Security Considerations: [If applicable, e.g. channel auth, env/config handling]

None - This is a type handling fix that does not affect authentication or expose sensitive configuration.
无 - 这是类型处理修复,不影响认证或暴露敏感配置。

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactoring

Component(s) Affected

  • Core / Backend (app, agents, config, providers, utils, local_models)
  • Console (frontend web UI)
  • Channels (DingTalk, Feishu, QQ, Discord, iMessage, etc.)
  • Skills
  • CLI
  • Documentation (website)
  • Tests
  • CI/CD
  • Scripts / Deploy

Checklist

  • I ran pre-commit run --all-files locally and it passes
  • If pre-commit auto-fixed files, I committed those changes and reran checks
  • I ran tests locally (pytest or as relevant) and they pass
  • Documentation updated (if needed)
  • Ready for review

Note: Pre-commit and pytest not applicable for this minimal fix. Manual testing performed with custom WebSocket channel (CoCoPaw) and verified successful startup.

说明: 此修复为最小化改动,未运行 pre-commit 和 pytest。已手动测试自定义 WebSocket 通道(CoCoPaw),验证启动成功。

Testing

[English]
Manual Testing Steps:

  1. Create custom channel in ~/.copaw/custom_channels/cocopaw/

  2. Enable channel in ~/.copaw/workspaces/default/agent.json:

    {
      "channels": {
        "cocopaw": {
          "enabled": true,
          "filter_tool_messages": true,
          "filter_thinking": true
        }
      }
    }
  3. Run copaw app

  4. Observe logs:
    Before fix / 修复前:

    INFO | Processing channel: cocopaw
    INFO |   -> ch_cfg type: <class 'dict'>, enabled=True
    INFO |   -> Channel enabled: False  ← Bug! Should be True
    INFO |   -> SKIP: disabled
    

    After fix / 修复后 (Actual logs):

    INFO manager.py:186 | Processing channel: cocopaw
    INFO manager.py:191 |   -> ch_cfg from getattr: True
    INFO manager.py:193 |   -> ch_cfg type: <class 'dict'>, enabled=True
    INFO manager.py:217 |   -> Channel enabled: True (type=<class 'dict'>)
    INFO manager.py:265 |   -> Calling from_config...
    INFO manager.py:267 |   -> SUCCESS: Created <cocopaw.channel.CoCoPawChannel object at 0x...>
    

    Verify port listening / 验证端口监听:

    netstat -ano | findstr :8765
    # Output: TCP 0.0.0.0:8765 LISTENING

[中文]
手动测试步骤:

  1. 在 ~/.copaw/custom_channels/cocopaw/ 中创建自定义通道

  2. 在 ~/.copaw/workspaces/default/agent.json 中启用通道:

    {
      "channels": {
        "cocopaw": {
          "enabled": true,
          "filter_tool_messages": true,
          "filter_thinking": true
        }
      }
    }
  3. 运行 copaw app

  4. 观察日志:

    修复前:

    INFO | Processing channel: cocopaw
    INFO |   -> ch_cfg type: <class 'dict'>, enabled=True
    INFO |   -> Channel enabled: False  ← Bug!
    INFO |   -> SKIP: disabled
    

    修复后(实际日志):

    INFO manager.py:186 | Processing channel: cocopaw
    INFO manager.py:191 |   -> ch_cfg from getattr: True
    INFO manager.py:193 |   -> ch_cfg type: <class 'dict'>, enabled=True
    INFO manager.py:217 |   -> Channel enabled: True (type=<class 'dict'>)
    INFO manager.py:265 |   -> Calling from_config...
    INFO manager.py:267 |   -> SUCCESS: Created <cocopaw.channel.CoCoPawChannel object at 0x...>
    

    验证端口监听:

    netstat -ano | findstr :8765
    # 输出:TCP 0.0.0.0:8765 LISTENING

Local Verification Evidence

# Pre-commit: Not applicable (minimal fix, no pre-commit hooks configured)
# pytest: Not applicable (no unit tests for channels/manager.py)

# Manual verification - Actual logs from copaw app:
copaw app

# Output (After fix):
# INFO manager.py:186 | Processing channel: cocopaw
# INFO manager.py:191 |   -> ch_cfg from getattr: True
# INFO manager.py:193 |   -> ch_cfg type: <class 'dict'>, enabled=True
# INFO manager.py:217 |   -> Channel enabled: True (type=<class 'dict'>)
# INFO manager.py:265 |   -> Calling from_config...
# INFO manager.py:267 |   -> SUCCESS: Created <cocopaw.channel.CoCoPawChannel object at 0x...>

# Verify port:
netstat -ano | findstr :8765
# TCP    0.0.0.0:8765    0.0.0.0:0    LISTENING    12156

Additional Notes

[English]
Bug Root Cause:

  • Built-in channel configs are defined in ChannelConfig class with type annotations
  • Pydantic automatically converts them to objects
  • Custom channel configs have no type annotations, stored in __pydantic_extra__ as dict
  • getattr(ch_cfg, "enabled", False) returns False for dict types (ignores the actual value)
  • Fix: Use isinstance() to check type before accessing fields

Code Changes:

  • File: src/copaw/app/channels/manager.py
  • Lines: 196-209 (all three fields)
  • Change: Add isinstance() check before using getattr()

Impact:

  • All users trying to use custom channels are affected
  • Severity: High (blocks custom channel feature entirely)
  • Backward compatible: Yes (built-in channels still work)

[中文]
Bug 根本原因:

  • 内置通道配置在 ChannelConfig 类中定义,有类型注解
  • Pydantic 自动将它们转换为对象
  • 自定义通道配置没有类型注解,作为 dict 存储在 __pydantic_extra__ 中
  • getattr(ch_cfg, "enabled", False) 对 dict 类型返回 False(忽略实际值)
  • 修复:使用 isinstance() 在访问字段前检查类型

代码变更:

  • 文件:src/copaw/app/channels/manager.py
  • 行:196-209(所有三个字段)
  • 变更:在使用 getattr() 前添加 isinstance() 检查

影响范围:

  • 所有尝试使用自定义通道的用户都受影响
  • 严重程度:高(完全阻塞自定义通道功能)
  • 向后兼容:是(内置通道仍然有效)

Code Diff

# File: src/copaw/app/channels/manager.py
# Lines: 196-209

# Before / 修复前:
enabled = getattr(ch_cfg, "enabled", False)
if not enabled:
    continue

filter_tool_messages = getattr(
    ch_cfg,
    "filter_tool_messages",
    False,
)
filter_thinking = getattr(
    ch_cfg,
    "filter_thinking",
    False,
)

# After / 修复后:
# Handle both Pydantic objects (built-in) and dicts (custom channels)
if isinstance(ch_cfg, dict):
    enabled = ch_cfg.get("enabled", False)
else:
    enabled = getattr(ch_cfg, "enabled", False)
if not enabled:
    continue

if isinstance(ch_cfg, dict):
    filter_tool_messages = ch_cfg.get("filter_tool_messages", False)
    filter_thinking = ch_cfg.get("filter_thinking", False)
else:
    filter_tool_messages = getattr(
        ch_cfg,
        "filter_tool_messages",
        False,
    )
    filter_thinking = getattr(
        ch_cfg,
        "filter_thinking",
        False,
    )

# Handle both Pydantic objects (built-in) and dicts (custom channels)
@github-actions github-actions Bot added the first-time-contributor PR created by a first time contributor label Mar 20, 2026
@f3125472
f3125472 temporarily deployed to maintainer-approved March 21, 2026 01:38 — with GitHub Actions Inactive
@xieyxclack

Copy link
Copy Markdown
Member

Thank you for your contribution. Please run pre-commit run --all-files to format the code

@f3125472

f3125472 commented Mar 21, 2026 •

Copy link
Copy Markdown
Contributor Author

已完成 pre-commit run --all-files 已提交
black....................................................................Passed
flake8...................................................................Passed
pylint...................................................................Passed
prettier.................................................................Passed

@f3125472
f3125472 temporarily deployed to maintainer-approved March 21, 2026 03:27 — with GitHub Actions Inactive

@xieyxclack xieyxclack left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@xieyxclack
xieyxclack merged commit cd036d5 into agentscope-ai:main Mar 21, 2026
14 checks passed
@github-actions

Copy link
Copy Markdown

Welcome to CoPaw! 🎉

Thank you @f3125472 for your first contribution! Your PR has been merged. 🚀

We'd love to give you a shout-out in our release notes! If you're comfortable sharing, please reply to this comment with your social media handles using the format below:

discord: your_discord_handle
x: your_x_handle
xiaohongshu: your_xiaohongshu_id

Note: Only share what you're comfortable with — all fields are optional.

Thanks again for helping make CoPaw better!

tudan110 pushed a commit to tudan110/QwenPaw that referenced this pull request Apr 4, 2026

This branch was previously deployed

1 inactive deployment
maintainer-approved — c23ee851 Deployed Mar 21, 2026 by f3125472 via Maintainer Approval #1008
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

first-time-contributor PR created by a first time contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: fix(channels): custom channels cannot start / 自定义通道无法启动 (getattr on dict)

2 participants