Skip to content

[Bug] Python SDK: ModelClient polling loops lack timeout and cancellation support #550

@Timandes

Description

@Timandes

Bug Description

ModelClient 中的轮询循环 (while True) 缺少超时机制和取消支持,可能导致程序永久阻塞。

在 PR #492 (TypeScript SDK) 的 Code Review 中,Reviewer @dengwx2026 指出:

Add cancellation support: ModelClient polling loops (while(true)) need AbortController or timeout mechanisms to avoid blocking forever.

经调研,该问题源自 Python SDK 的原始设计。

Steps to Reproduce

  1. 创建 ModelClient 实例
  2. 调用 anti_call_llm(index=0) 方法
  3. 如果日志文件不存在或永远没有请求写入,程序将无限阻塞

Expected Behavior

  • 应该支持超时参数,允许设置最大等待时间
  • 应该支持取消操作(如 asyncio.CancelledError
  • 在超时或取消时抛出明确的异常

Actual Behavior

  • 程序在 while True 循环中无限等待
  • 无超时机制
  • 无取消支持
  • 无法中断阻塞状态

Error Logs

无错误日志输出,程序静默阻塞。

Environment Information

  • OS: macOS 14.0 (Darwin 23.6.0)
  • Python Version: 3.14.2
  • ROCK Version: dev (feature/ts-sdk branch)
  • Installation Method: source installation
  • Docker Version: N/A
  • Deployment Type: local

ROCK Configuration

  • Runtime Environment Type: N/A
  • Sandbox Image: N/A
  • Resource Allocation: N/A

Component Affected

  • Sandbox
  • Actions
  • Deployments
  • SDK & API
  • Envhub
  • CLI
  • Performance & Optimization
  • Documentation & Examples

Additional Context

问题代码位于 sdk/model/client.py

  1. pop_request 方法 (第67-76行)
  2. wait_for_first_request 方法 (第107-120行)

Suggested Solutions

# 方案一:添加超时参数
async def pop_request(self, index: int, timeout: float | None = None) -> str:
    start_time = time.monotonic()
    while True:
        if timeout is not None and time.monotonic() - start_time > timeout:
            raise TimeoutError(f"pop_request timed out after {timeout} seconds")
        # ... rest of the logic
# 方案二:支持 asyncio.CancelledError
async def pop_request(self, index: int) -> str:
    while True:
        try:
            # ... existing logic
            await asyncio.sleep(1)
        except asyncio.CancelledError:
            logger.info("pop_request cancelled")
            raise
# 方案三:调用方使用 asyncio.wait_for 包装
request = await asyncio.wait_for(client.pop_request(index), timeout=30.0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions