Skip to content

feat(tokenless): extend AgentScope support - #2553

Open
ikunkun-sys wants to merge 1 commit into
alibaba:mainfrom
ikunkun-sys:agent/extend-agentscope-support
Open

feat(tokenless): extend AgentScope support#2553
ikunkun-sys wants to merge 1 commit into
alibaba:mainfrom
ikunkun-sys:agent/extend-agentscope-support

Conversation

@ikunkun-sys

@ikunkun-sys ikunkun-sys commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Why

Tokenless previously required AgentScope 2.0.5 or later and kept compression policy inside the
2.x middleware. Supporting AgentScope without maintaining patch-specific SDKs requires a stable
public entry point and a framework-neutral compression core.

What changed

  • Add shared TokenlessConfig and ToolResponseCompressor APIs to anolisa-tokenless.
  • Add lifecycle adapters for AgentScope 1.0.11 through 1.0.x and AgentScope 2.0.x.
  • Preserve the existing TokenlessMiddleware API for AgentScope 2.x callers.
  • Support direct Agent construction from 2.0.0 and App factories from 2.0.1.
  • Exercise the supported range with real installed wheels at lifecycle and Tool ABI boundaries.

Related issue

no-issue: framework compatibility expansion requested during SDK design work

User / Agent impact

AgentScope 1.x users can install Tokenless on an existing Agent, while AgentScope 2.x users use the
same configuration and entry point during Agent construction. Existing 2.x middleware callers
remain compatible.

Risk and compatibility

  • Public CLI, API, configuration, or documented behavior changed
  • Privileged or security-sensitive behavior changed
  • Cross-component contract changed
  • Migration or rollback guidance is needed

This adds public Python APIs and widens the AgentScope dependency range. Retrieval remains scoped
to markers visible in the current Agent memory or state, and the existing 2.x API is retained.

Validation

  • make test-python-runtime test-integration test-agentscope-integration
  • Real wheel matrix: AgentScope 1.0.11, 1.0.21, 2.0.0, 2.0.1, 2.0.3, and 2.0.6
  • Ruff, Black, isort, bash -n, and git diff --check
  • npm run build from website/

Documentation and rollback

Updated the English and Chinese README, canonical framework-integration user guides, and
runtime-library design documents. Roll back by reverting this PR; existing AgentScope 2.x users
can continue using TokenlessMiddleware.

@github-actions github-actions Bot added component:tokenless src/tokenless/ scope:documentation ./docs/|./*.md|./NOTICE labels Aug 14, 2026
@ikunkun-sys
ikunkun-sys marked this pull request as ready for review August 14, 2026 09:41

@qoderai qoderai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本次审查范围内未发现需要修改的问题。


🤖 Generated by QoderView workflow run

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review baseline: aa8b023862bf...bfab7055570a

[P2] 同步新的 AgentScope 支持范围到规范用户指南

docs/user-guide/en/token-saving/tokenless/framework-integration.md:209
仍声明仅支持 >=2.0.5,并在 222-266 行让所有用户调用旧的
register_tools() 和单 middleware App 流程。这个 PR 将依赖下限降到 1.0.11,
而 2.0.0-2.0.2 的 Toolkit 没有 add_tool;按当前规范指南接入会在
TokenlessMiddleware.register_tools() 抛错,1.x 用户也找不到新增的
install() 流程。中文镜像在
docs/user-guide/zh/token-saving/tokenless/framework-integration.md:216
同样过期。建议同步两份指南中的版本矩阵和 1.x/2.x/App 示例,并扩展文档一致性检查,
避免支持范围和公开入口再次漂移。

@ikunkun-sys
ikunkun-sys force-pushed the agent/extend-agentscope-support branch from bfab705 to a7c579c Compare August 14, 2026 10:08

Copy link
Copy Markdown
Collaborator Author

@kongche-jbw 该意见正确,已在 a7c579c6 处理:

  • 同步中英文规范用户指南,明确 1.0.11–1.0.x、2.0.0、2.0.1–2.0.x 的支持矩阵。
  • 示例统一改用稳定入口 TokenlessAgentScope:1.x 使用 install(),2.x 构造时注入 Tool 和 middleware,App 使用 app_options()
  • 扩展现有 AgentScope wheel 集成测试,校验两份指南的版本矩阵和公开入口,防止再次漂移。

验证通过:六档真实依赖 smoke(1.0.11、1.0.21、2.0.0、2.0.1、2.0.3、2.0.6)及中英文文档站 production build。

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review baseline: c122a0a8f582...a7c579c6f673

[P1] 将 JSON 数值解析限制作为普通文本处理

src/tokenless/python/tokenless/python/anolisa_tokenless/tool_response.py:213
会先对每段工具文本调用 json.loads(),但这里只捕获 JSONDecodeError
在 CPython 3.11+ 中,4,301 位纯数字工具文本会因整数转换上限抛出
ValueError;该异常会穿透 AgentScope 1.x postprocessor 和 2.x middleware,
让工具调用失败,与 Tokenless 失败时保留原响应的契约不符。

Possible direction: 将这类解析限制视为普通文本并继续 fail-open,补一条通过
compress_text() 处理 4,301 位数字且不抛异常的回归测试。

@Forrest-ly Forrest-ly left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review: APPROVE

Commit: a7c579c · 范围: +1668/−533,20 个文件,代码、测试、文档均已逐行审查。

总体评价

代码质量高。框架无关的压缩策略被提取到 anolisa_tokenless.tool_responseTokenlessConfig + ToolResponseCompressor),AgentScope 集成按大版本拆分为 _v1.py / _v2.py(导入时按已安装版本分发),旧 TokenlessMiddleware 通过 middleware.py shim 保留,2.x 既有调用方兼容。测试覆盖全面:新增 v1 单元测试与 v1 real-wheel smoke、6 环境真实 wheel 矩阵(1.0.11 / 最新 1.0.x / 2.0.0 / 2.0.1 / 2.0.3 / 最新 2.0.x)、文档一致性检查与 policy drift 检查。kongche-jbw 先前的 P2 建议(同步文档版本矩阵并扩展文档一致性检查)也已落实。

独立验证

我对照 AgentScope 上游源码(v1.0.11 / v2.0.0 / v2.0.1 / v2.0.3)逐一核实了代码中的框架 ABI 假设:

  • 1.x Toolkit.call_tool_function 是返回异步生成器包装的 coroutine → smoke test 中 await toolkit.call_tool_function(...) 正确
  • 1.x RegisteredToolFunction.postprocess_func 为可变 dataclass 字段,支持 sync/async,返回 None 时保留原响应 → _wrap_postprocessor 三种情况处理均正确
  • register_tool_function(json_schema=...) 的格式与 _retrieve_schema(){"type": "function", "function": {...}} 一致
  • 1.x 工具抛异常时 toolkit 生成 Error: {e} 前缀文本 → _looks_like_error 启发式与之匹配(方向保守、fail-safe)
  • 2.0.0 ToolBasecall(仅 __call__),2.0.3+ 以 call 为新 override point → hasattr(ToolBase, "call") 能力选择正确
  • 2.0.0 MiddlewareBaselist_tools,2.0.1+ 有 → app_options() 对 2.0.0 的 RuntimeError gating 正确
  • 2.0.1 create_app(extra_agent_middlewares=..., extra_agent_tools=...) 的 factory 签名 (user_id, agent_id, session_id) -> Awaitable[list[...]]app_options() 一致

安全姿态未退化:retrieval 仍限定 marker 可见 + 24 位 hex hash 校验;App 模式按 user/agent/session sha256 派生隔离 data_dir;压缩失败 fail-open。

小建议(P3,不阻塞合并)

  1. _v1.py: compress_text(..., session_id=None) —— 1.x 的 stash/stats 失去 session 维度归属;如后续需要按 session 统计,可考虑允许通过 config 或 install() 传入 session ID。
  2. _v1.py retrieve 的可见范围是整个 Agent memory,而 2.x 限定为模型可见的 context+summary —— 1.x 范围更宽但安全(memory 是 1.x 的唯一事实来源,文档已写明);建议在代码中加一句注释说明该差异的原因,便于后续维护。
  3. pyproject.toml.inmcp>=1.13,<2 pin 作用于所有 AgentScope 版本,但严格来说只有 2.0.0 需要(2.0.0 未 cap MCP);文件内注释已解释,未来移除 2.0.0 支持时可考虑放宽。

CI 提示

review 时该 commit 的 "Test tokenless" CI 仍在 pending(前一 commit 为 12/12 通过),合并前请确认 CI 通过。

@Forrest-ly Forrest-ly left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

补充 Review:REQUEST_CHANGES(撤回此前的 APPROVE)

前一条 APPROVE 提交后,复核了 kongche-jbw 在 10:17 提出的 P1,独立验证为真实缺陷,因此改判为 REQUEST_CHANGES。其余审查结论(架构、ABI 适配、测试覆盖、文档)维持正面评价,仅此一处阻塞。

P1 验证:json.loads 整数位数限制导致 fail-open 契约被破坏

本地复现(Python 3.8.17,该限制同样适用;包要求 requires-python = ">=3.11" 必然命中):

>>> json.loads("9" * 4301)
ValueError: Exceeds the limit (4300) for integer string conversion: value has 4301 digits

anolisa_tokenless/tool_response.py:213 _normalize_input 只捕获 json.JSONDecodeError,而 CPython 3.11+ 对超过 4300 位的整数字面量抛出的是裸 ValueErrorJSONDecodeError 虽是 ValueError 子类,但此处抛出的不是它)。该异常会从 compress_text 穿透 AgentScope 1.x postprocessor(_postprocess_tool_response 无异常兜底)和 2.x on_acting,导致工具调用整体失败——当工具正常返回 4301+ 位纯数字文本(大整数计算、长数字序列等)时,Tokenless 反而把成功的工具调用打挂,违背"失败时保留原响应"的 fail-open 契约。

建议修复

  1. _normalize_input:将 except json.JSONDecodeError 放宽为 except ValueError(或同时捕获),把此类解析限制当作普通文本继续走 fail-open 路径。
  2. compress_text 中压缩结果的 json.loads(candidate_json) 处(tool_response.py:186 附近)存在同类写法,建议一并放宽为 ValueError,做防御性处理。
  3. 补回归测试:compress_text() 处理 4301 位纯数字文本时返回 None(或正常压缩结果)且不抛异常,覆盖 1.x/2.x 两条路径。

修复后我这边可以直接通过。其余此前提出的 P3 建议(session_id 归属、memory 可见范围注释、mcp pin)不阻塞。

@ikunkun-sys
ikunkun-sys force-pushed the agent/extend-agentscope-support branch from a7c579c to 79ceceb Compare August 14, 2026 10:24

Copy link
Copy Markdown
Collaborator Author

@kongche-jbw 该 P1 判断正确,已在 79ceceb2 处理:

  • ToolResponseCompressor._normalize_input() 现在捕获 json.loads()ValueError,把超过 CPython 整数转换上限的数值文本作为普通字符串继续处理,不再让异常穿透 AgentScope 生命周期。
  • 在共享 Runtime 的 installed-wheel 测试中新增 4,301 位数字通过公开 compress_text() 的回归用例。

验证通过:Runtime Wheel 14 项测试、AgentScope 1.x/2.x 单测,以及 1.0.11、1.0.21、2.0.0、2.0.1、2.0.3、2.0.6 六档真实依赖 smoke。

Extract framework-neutral response compression into the Python runtime and select an
AgentScope lifecycle adapter from the installed major version.

AgentScope 1.x uses Toolkit postprocessors while 2.x retains middleware and Tool
integration. This avoids patch-specific public APIs and supports 1.0.11 through 1.0.x
plus 2.0.x; AgentScope App remains available from 2.0.1.

Assisted-by: Codex:0.147.0
Signed-off-by: 爱鲲 <jiawa.syx@alibaba-inc.com>
@ikunkun-sys
ikunkun-sys force-pushed the agent/extend-agentscope-support branch from 79ceceb to 1305d0a Compare August 14, 2026 10:27

Copy link
Copy Markdown
Collaborator Author

@Forrest-ly 补充 P1 判断正确,已在 1305d0af 完整处理:

  • 输入侧 _normalize_input() 和压缩结果侧 json.loads(candidate_json) 均捕获 ValueError;前者将超限数值文本按普通字符串处理,后者把异常 Runtime 结果视为无效压缩并保留原文。
  • 共享 Runtime installed-wheel 测试覆盖 4,301 位输入通过公开 compress_text() 不抛异常;2.x 既有 Runtime/output fail-open 参数表新增 4,301 位压缩结果。1.x 与 2.x 均委托该共享核心,六档真实依赖 smoke 已再次通过。

本地验证通过:Ruff、Black、Runtime Wheel 14 项测试、AgentScope 1.x/2.x 单测,以及 1.0.11、1.0.21、2.0.0、2.0.1、2.0.3、2.0.6 六档真实依赖 smoke。三条 P3 按 review 结论保持非阻塞,本次未扩大范围。

@ikunkun-sys
ikunkun-sys requested a review from Forrest-ly August 14, 2026 10:36

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review baseline: c122a0a8f582...1305d0afbe1f

[P1] 深层 JSON 仍会绕过 fail-open

src/tokenless/python/tokenless/python/anolisa_tokenless/tool_response.py:213
这里只捕获 ValueError。工具返回约 10,000 层嵌套的合法 JSON array 时,
CPython json.loads() 会抛出 RecursionError;该异常会从公开 compress_text()
穿透 AgentScope 1.x postprocessor 和 2.x middleware,使成功的工具调用失败。
这与共享压缩核心的 fail-open 契约不符。

Possible direction: 将解析深度异常也按普通文本处理,并补一个通过公开
compress_text() 的深层 JSON 回归测试,断言保留原响应且不抛异常。

@Forrest-ly Forrest-ly left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

总体评价

重写后的单 commit(1305d0a)已完整修复上一轮 kongche-jbw / Forrest-ly 提出的 P1 阻塞问题(json.loads 整数位数限制破坏 fail-open 契约),并在两处 json.loads 位点与回归测试上逐一落实;框架无关压缩核心的提取、AgentScope 1.x/2.x 双版本适配、测试矩阵与文档一致性均复查无误,CI(含六档真实 wheel 矩阵)全绿。

审查结论

approve

详细意见

🔴 必须修改(阻塞合并)

无。此前唯一阻塞项(P1)已修复并独立验证:

  • 输入侧:src/tokenless/python/tokenless/python/anolisa_tokenless/tool_response.py:214 _normalize_input 已放宽为 except ValueError,超过 CPython 整数转换上限的数值文本按普通字符串继续 fail-open。
  • 压缩结果侧:tool_response.py:171result.outputjson.loads 同样捕获 ValueError,异常 Runtime 结果按无效压缩处理并保留原文。
  • 回归测试:python/tokenless/tests/test_runtime.py 新增 test_framework_core_treats_oversized_integer_as_text(4,301 位数字经公开 compress_text() 不抛异常);tests/test_agentscope_middleware.py fail-open 参数表新增 _CompressionResult("9" * 4_301, True) 覆盖 2.x 输出侧;1.x/2.x 均委托该共享核心。
  • 本地独立验证:Python 3.8.17 / 3.11.14 复现 json.loads("9" * 4301) 抛裸 ValueError;另以 stub runtime 对真实 tool_response.py 实测输入侧与输出侧均返回 None、无异常穿透。
  • CI:头提交 1305d0a 8/8 checks 通过,含 “Test tokenless”(test-python-runtime + 六档真实依赖 test-agentscope-integration),上一轮的 CI pending 提示已消除。

🟡 建议修改(不阻塞但推荐)

  • [src/tokenless/python/agentscope/src/tokenless_agentscope/_v1.py:48-57] install() 的冲突检查只校验自身配置的 retrieve_tool_name:若在同一 Agent 上再安装一个使用不同 retrieve_tool_nameTokenlessAgentScope 实例(默认名会被冲突检查拦下),可通过检查并对所有工具二次包裹 postprocessor(含第一个实例的 retrieve 工具)。后果是响应被串联压缩两次,且第一个实例 retrieve 工具的输出(不在第二个实例的排除集内)也会被再压缩,与 retrieve “取回被省略内容” 的目的相悖。该场景需刻意误配置,建议加一层防御即可:例如给包裹函数挂标记属性,install() 发现已有工具的 postprocess_func 是 Tokenless 包裹时拒绝或跳过。(2.x 存在同构场景:同时传入两个 middleware 会在 on_acting 中双重压缩,同属误配置场景,可一并考虑。)

🟢 值得肯定

  • P1 修复完整:两处 json.loads 位点均按建议放宽为 ValueError,输入侧与压缩结果侧各有回归测试,与修复建议逐条对应。
  • 提取干净:与重构前 middleware.py 逐行比对,2.x on_acting / _compress_response / 阈值 / 排除 / retrieve 授权语义完全保留;middleware.py shim 同时保留公开 API 与私有别名,policy drift 测试继续有效。
  • 1.x 防御设计到位:install 幂等、retrieve 工具名冲突检查、既有 postprocessor 串接(sync/async/返回 None 三态)、partial/interrupted/success=False/error-like 响应跳过均有单测覆盖;retrieve 限定 Agent memory 内 marker 可见。
  • 测试矩阵充分:本地以 stub runtime 跑通 v1 单测(5 项)与 2.x 单测(20 项,Python 3.11.14);CI 以真实 wheel 覆盖 1.0.11 / 最新 1.0.x / 2.0.0 / 2.0.1 / 2.0.3 / 最新 2.0.x,且新增文档一致性 fragments 检查与打包元数据断言,防止支持范围再次漂移。
  • 文档中英文同步齐全:用户指南版本矩阵、README 接入示例、设计文档边界描述一致,全仓 grep 确认无残留 >=2.0.5 旧表述。
  • 安全姿态未退化:retrieve 仍要求 24 位 hex hash + marker 可见;App 模式按 user/agent/session sha256 派生隔离 data_dir;各路径 fail-open 契约成立。

上一轮的三条 P3(1.x session_id 归属、retrieve 可见范围注释、mcp pin)维持 Forrest-ly review 结论:非阻塞,本次不重复列入意见。

@Forrest-ly Forrest-ly left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

结论:REQUEST_CHANGES

复核 head 1305d0a。kongche-jbw 于 10:46 提出的 [P1] 深层 JSON 仍会绕过 fail-open 在当前 head 上成立且未被任何 commit 处理,我独立复现并核对了完整代码路径。这是破坏 fail-open 契约的真实正确性缺陷,故本轮改判为 REQUEST_CHANGES。

独立验证(本地 Python 3.8.17 复现)

>>> import json
>>> json.loads("[" * 10000 + "]" * 10000)
RecursionError: maximum recursion depth exceeded while decoding a JSON array from a unicode string
>>> json.loads("[" * 1500 + "]" * 1500)   # 超过默认递归上限 1000 即触发
RecursionError

RecursionErrorRuntimeError 的子类,不是 ValueError 的子类,现有 except ValueError 无法捕获。

代码路径(src/tokenless/python/tokenless/python/anolisa_tokenless/tool_response.py

  1. compress_text() 在第 148 行调用 self._normalize_input(text),该行位于第 150 行才开始的 try/except TokenlessError之外,因此 _normalize_input 抛出的任何异常都会直接穿透 compress_text()
  2. _normalize_input() 第 213 行 json.loads(text)except ValueError(214-215 行)。工具返回约 10,000 层(甚至约 1,500 层)嵌套的合法 JSON array 时抛出 RecursionError 并逃逸。
  3. 该异常向上穿透 AgentScope 1.x postprocessor(_v1.py:106)与 2.x middleware(_v2.py:236_compress_text → 260),两处调用点均无兜底 try/except(_v1.py 唯一的 try/except 在 131-133 行,仅包住 retrieve,不包 compress_text)。结果是一次本来成功的工具调用被标记为失败——直接违背本 PR 反复强调的"异常时保留原文"fail-open 契约。

可达性_normalize_input 仅在 len(text) >= min_chars(默认 200)时进入;约 1,500 层嵌套即约 3KB,轻松越过该阈值,真实工具(如递归结构、AST/DOM 序列化)完全可能命中。

修复建议(与 kongche-jbw 一致,按位点落实)

  1. 输入侧(必须,当前可利用)_normalize_input() 第 213-214 行,将 except ValueError 放宽为 except (ValueError, RecursionError);命中时按不透明文本处理(走 json.dumps(text) 字符串压缩路径,保证可逆、可 retrieve)。
  2. 压缩结果侧(建议,纵深防御)compress_text() 第 170-171 行 json.loads(candidate_json) 同样补 RecursionError。当前 native 运行时输出被 max_depth(≤32)截断,实际不会触达递归上限,但补上后两处 json.loads 的 fail-open 契约保持一致,也防止将来放宽 max_depth 回归。
  3. 回归测试:仿照已有 test_framework_core_treats_oversized_integer_as_text,在 test_runtime.py 增加用例,断言对 "[" * 10000 + "]" * 10000 这类深层嵌套输入 compress_text() 不抛异常、走 fail-open(保留原文或压缩为可恢复字符串)。

其余结论

前几轮的正面结论维持不变:框架无关压缩核心的提取、AgentScope 1.x/2.x 双版本适配(含 Tool ABI 能力探测)、六档真实 wheel 测试矩阵、文档一致性均复查无误;上一轮 P1(整数位数限制 / ValueError)的修复也已确认落实。本轮唯一阻塞项即上述 RecursionError P1。

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

Labels

component:tokenless src/tokenless/ scope:documentation ./docs/|./*.md|./NOTICE

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants