fix(studio): guard generated CreateAgentToolset against deepcopy on pinned runtimes - #1115
Open
jdp-just-does-projects wants to merge 1 commit into
Conversation
Generated projects pin veadk-python, and releases before volcengine#1113 deep-copy LlmRequest for 429 retries. tools_dict holds bound methods of the generated CreateAgentToolset, which reaches bootstrap agents, their OpenTelemetry tracers and span processor locks, so agents with dynamic delegation and APMPlus fail with "cannot pickle '_thread.RLock' object". Return the shared toolset instance from __deepcopy__ so generated agents work regardless of the pinned runtime version. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMFeYUADAnuzxhHCyFJA4N
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 摘要
English: Add a
__deepcopy__guard to theCreateAgentToolsetsubclass that Studio writes into generated projects (quick_mode_compat.py), so a deployed agent no longer fails withTypeError: cannot pickle '_thread.RLock' objecteven when its pinnedveadk-pythonpredates #1113.中文: 为 Studio 生成项目中
quick_mode_compat.py里的CreateAgentToolset子类增加__deepcopy__保护,使部署后的 Agent 即使固定安装的是 #1113 之前的veadk-python版本,也不会再报TypeError: cannot pickle '_thread.RLock' object。Related / 相关:#1113 (merged), #1114 (open), #1087 (open)
English
Problem
A Studio-built agent with dynamic agent delegation enabled, deployed to an AgentKit runtime with APMPlus tracing (
ENABLE_APMPLUS=true, which the deploy path sets by default), fails on its first message, before any model call:The Studio UI shows this with the generic "check network settings such as the shared public egress" hint, which is misleading: the failure happens inside the agent process.
Root cause
RetryingLiteLlm.generate_content_asyncsnapshots the request withcopy.deepcopy(llm_request)(releases up to and including 1.1.12).LlmRequest.tools_dictholdscollect_resources/create_agents, which are bound methods ofCreateAgentToolset.OpentelemetryTracer, whose span processor owns a lock.Object path found while reproducing:
#1113 fixes the library by no longer deep-copying
tools_dict. Generated projects, however, install a pinnedveadk-python(_VEADK_VERSION), and the fix only reaches them once the pin is bumped. Onmainthe pin is1.1.11, which still has the olddeepcopy(llm_request). #1114 bumps it to1.1.13.Fix
CreateAgentToolsetin the generatedquick_mode_compat.pynow defines__deepcopy__, returning the same instance and recording it inmemo. The toolset is shared runtime state (bootstrap agents, tracers, locks, caches) and should never be cloned. This makes generated agents safe regardless of whichveadk-pythonthe project pins. It complements #1113 and #1114 rather than replacing them:1.1.11).It does not change the pin, so it does not conflict with #1114 and leaves the golden hashes unchanged (the golden drafts do not enable delegation).
Tests
test_quick_mode_compat_toolset_survives_request_deepcopy: generates a delegation project, loads itsquick_mode_compat.py, attaches anRLockto the toolset, and deep-copies atools_dict-shaped mapping of its bound methods. Fails without the guard (TypeError: cannot pickle '_thread.RLock' object) and passes with it.uv run pytest tests/cli/test_generated_agent_backend_codegen.py tests/cli/test_generated_agent_backend_codegen_extended.py tests/cli/test_generated_agent_component_matrix.py tests/models/test_retrying_lite_llm.py tests/tools -q: 385 passed, 1 skipped.uv run --group dev pre-commit run --files ...: ruff check, ruff format, and secrets scan passed.ENABLE_APMPLUS=trueagainst the pinned runtimeveadk-python==1.1.11,google-adk==2.1.0:TypeError: cannot pickle '_thread.lock' object.Side note (not changed here):
frontend/src/adk/runSseError.tsadds the shared-public-egress network hint to every run error, which makes in-agent failures like this one look like network problems.中文
问题
在 Studio 中开启**动态子智能体(dynamic agent delegation)**的 Agent,部署到开启了 APMPlus 链路追踪(
ENABLE_APMPLUS=true,部署流程默认开启)的 AgentKit Runtime 后,发送第一条消息即失败,且发生在调用模型之前:Studio 前端会附带“请检查共享公网出口等网络配置”的通用提示,但这具有误导性:错误实际发生在 Agent 进程内部,与网络无关。
根因
RetryingLiteLlm.generate_content_async通过copy.deepcopy(llm_request)保存请求快照(1.1.12 及之前版本)。LlmRequest.tools_dict中的collect_resources/create_agents是CreateAgentToolset的绑定方法(bound method)。OpentelemetryTracer,其 span processor 内部持有锁。复现时定位到的对象路径:
#1113 已在库层面修复:不再深拷贝
tools_dict。但 Studio 生成的项目会安装固定版本的veadk-python(_VEADK_VERSION),只有版本号更新后修复才会生效。目前main上固定的是1.1.11,仍使用旧的deepcopy(llm_request);#1114 会将其升级到1.1.13。修复
生成的
quick_mode_compat.py中的CreateAgentToolset现在定义了__deepcopy__:返回同一个实例并写入memo。toolset 属于共享运行时状态(bootstrap agents、tracer、锁、缓存),不应被复制。这样无论生成项目固定的是哪个veadk-python版本,Agent 都是安全的。本 PR 是对 #1113、#1114 的补充,而非替代:1.1.11)。本 PR 不修改固定版本号,因此不会与 #1114 冲突,golden hash 也保持不变(golden 用例未开启动态子智能体)。
测试
test_quick_mode_compat_toolset_survives_request_deepcopy:生成开启动态子智能体的项目,加载其quick_mode_compat.py,为 toolset 挂上RLock,再对形如tools_dict、包含其绑定方法的映射做深拷贝。未加保护时失败(TypeError: cannot pickle '_thread.RLock' object),加保护后通过。uv run pytest tests/cli/test_generated_agent_backend_codegen.py tests/cli/test_generated_agent_backend_codegen_extended.py tests/cli/test_generated_agent_component_matrix.py tests/models/test_retrying_lite_llm.py tests/tools -q:385 通过,1 跳过。uv run --group dev pre-commit run --files ...:ruff check、ruff format、密钥扫描均通过。ENABLE_APMPLUS=true下使用固定的运行时版本veadk-python==1.1.11、google-adk==2.1.0运行:TypeError: cannot pickle '_thread.lock' object。附注(本 PR 未修改):
frontend/src/adk/runSseError.ts会给所有运行错误附加“共享公网出口”网络提示,导致此类 Agent 内部错误看起来像网络问题。🤖 Generated with Claude Code
https://claude.ai/code/session_01VMFeYUADAnuzxhHCyFJA4N