Skip to content

fix(end-node): normalize non-string plugin content - #262

Open
openjiuwen-sync-bot[bot] wants to merge 1 commit into
openJiuwen-ai:mainfrom
openjiuwenai:sync/pr-1812
Open

fix(end-node): normalize non-string plugin content#262
openjiuwen-sync-bot[bot] wants to merge 1 commit into
openJiuwen-ai:mainfrom
openjiuwenai:sync/pr-1812

Conversation

@openjiuwen-sync-bot

@openjiuwen-sync-bot openjiuwen-sync-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

Paired: GitHub #262GitCode !1812

Summary

Fixes #​1282: when a plugin node feeds the End node a non-string payload (dict / list / number), Content.content was typed Optional[str], so the payload failed Pydantic validation before reaching the converter — the second dialogue round could not load.

Change

  • backend/openjiuwen_studio/schemas/node.py: widen Content.content to Optional[Any] (the Content model is only used by End-node inputs.content, so the blast radius is contained).
  • backend/openjiuwen_studio/core/manager/convertor/components/end.py: end_convert now stringifies non-string content.content for response_template, so the dumped component configs stay JSON-serializable.

Tests (backend/tests/test_end_node_nonstring_content.py, new)

5 cases:

  • string content passes through unchanged
  • dict content → normalized to a string containing the keys
  • number content → normalized to its string form
  • None content → empty response_template
  • non-string payload keeps dumped configs JSON-serializable (regression guard)

Verification

  • Windows (Python 3.12 + uv): uv run pytest backend/tests/test_end_node_nonstring_content.py5 passed
  • Linux (Docker, python:3.12-slim + uv): same — 5 passed

Linked Closing Issues:

A plugin node may feed the End node a non-string payload (dict / list /
number). Content.content was typed Optional[str], so such payloads failed
Pydantic validation before reaching the converter — the second dialogue
round could not load (#1282).

- Widen Content.content to Optional[Any] (only used by End-node inputs).
- end_convert now stringifies non-string content for response_template so
  the dumped configs stay JSON-serializable.

Closes #1282.

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@openjiuwen-collaboration-bot

openjiuwen-collaboration-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

head_sha: 7e71b1c1eec5cd649b284080b3bfe17006013769

变更摘要

此 PR 修复了 #​1282 号问题:当插件节点向 End 节点传入非字符串类型(如 dictlistnumber)的内容时,由于 Content.content 字段被声明为 Optional[str],Pydantic 验证会在转换器执行前就失败,导致第二轮对话无法加载。变更通过放宽字段类型并增加运行时归一化逻辑来解决该问题。

主要改动

  • 放宽 Content.content 字段类型:在 backend/openjiuwen_studio/schemas/node.py 中,将 content 字段从 Optional[str] 改为 Optional[Any],使 Content 模型能够接收插件节点传入的任意类型载荷,避免 Pydantic 验证阶段直接拒绝非字符串值。
  • end_convert 中增加字符串归一化逻辑:在 backend/openjiuwen_studio/core/manager/convertor/components/end.py 中,对 content.content 进行类型判断:若为 str 则直接使用,否则通过 str() 转换(对 None 回退为空字符串),确保最终写入 response_template 的值始终是字符串且可 JSON 序列化。
  • 新增单元测试覆盖 5 种场景:新增 backend/tests/test_end_node_nonstring_content.py,覆盖字符串透传、字典归一化、数字归一化、None 处理以及非字符串载荷下配置 JSON 可序列化性回归守护,确保 end_convert 在各种输入下行为正确。

@openjiuwen-collaboration-bot

openjiuwen-collaboration-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

head_sha: 7e71b1c1eec5cd649b284080b3bfe17006013769

代码审查

审查总结

按优先级统计:

  • P0:0 个
  • P1:0 个
  • P2:1 个 — end.py 第 60 行 raw_content or "" 的短路求值吞没 falsy 非字符串值(False0[]{}),导致静默丢失内容
  • P3:2 个 — Content.content 类型放宽波及 output.py 但未同步归一化(不一致风险);测试缺失 falsy 值用例

已审查文件:

  • backend/openjiuwen_studio/core/manager/convertor/components/end.py — 发现 1 个问题(P2)
  • backend/openjiuwen_studio/schemas/node.py — 发现 1 个问题(P3)
  • backend/tests/test_end_node_nonstring_content.py — 发现 1 个问题(P3,缺失用例)

整体风险判断:中低风险。核心归一化逻辑存在一个边界条件 bug(falsy 值),可能在插件输出 False0 时复现类似 #​1282 的症状;其余为一致性和测试覆盖问题,不阻塞合入但建议修复。

类型 数量
🔴 阻塞 0
🟡 建议 1

💬 仅评论

@openjiuwen-collaboration-bot

Copy link
Copy Markdown

head_sha: 7e71b1c1eec5cd649b284080b3bfe17006013769

TASK STATUS DETAILS
CodeCheck ✅SUCCESS Click here
AntiPoison ✅SUCCESS Click here
Software Composition Analysis ✅SUCCESS Click here
Npm Build ❌FAILED See CHECK tab
Ruff Check ✅SUCCESS See CHECK tab

# Normalize it to a string so the End node's response_template stays
# serializable and the second dialogue round loads correctly (#1282).
raw_content = content.content
response_template = raw_content if isinstance(raw_content, str) else str(raw_content or "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

head_sha: 7e71b1c1eec5cd649b284080b3bfe17006013769

🟡 Medium Priority

变更行:end.py 第 60 行 str(raw_content or "")。当插件输出 falsy 非字符串值时(False00.0[]{}),表达式 raw_content or "" 会因 or 短路求值直接返回 "",导致 str("") = "",实际应当输出 "False""0""0.0""[]""{}"。Issue #​1282 明确提到的布尔值和数字均受此影响:False0 会静默丢失。

触发条件:插件节点向 End 节点输出 False / 0 / 0.0 / [] / {} 等 falsy 非 None 值。
失败模式:response_template 被设为空字符串,而非正确的字符串表示,对话历史中内容丢失。

修复方向:将 None 检查与 str() 转换分开,避免 or 吞掉 falsy 非 None 值。

建议:将 None 检查与 str() 转换解耦:先判 None → 空串,再判 str → 透传,其余 str() 转换。例如:response_template = "" if raw_content is None else raw_content if isinstance(raw_content, str) else str(raw_content)

Suggested change
response_template = raw_content if isinstance(raw_content, str) else str(raw_content or "")
response_template = "" if raw_content is None else raw_content if isinstance(raw_content, str) else str(raw_content)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants