anthropics / anthropics/anthropic-sdk-python

environments work poller: ack-before-yield makes any consumer failure permanently strand the work item — request deferred ack or a nack/fail API

未關閉
#1,746 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
主要語言
Python
星號
3.9k
分支
853
平均合併
1 天 18 小時
30 天內合併 PR
11

描述

`anthropic.lib.environments._poller` acknowledges every work item **before** yielding it to the consumer (`_poller.py:234`; docstring at `:66`: "Each yielded `BetaSelfHostedWork` has already been ack'd"). There is no deferred-ack option and no nack/fail API. Verified identical from 0.109.1 through 0.116.0.

For self-hosted managed-agents environments this makes any consumer-side exception after the yield permanently strand that session:

- the acked item freezes in `state='starting'` with `latest_heartbeat_at=null`;
- `reclaim_older_than_ms` cannot reach it (it only reaches unacknowledged items), and lease-TTL re-queue never fires (requires a first heartbeat);
- the platform keeps one work item per session, so all subsequent `user.message` events park in `requires_action` with **no new work enqueued** — the session is wedged until an operator runs `work.stop(work_id, force=True)` and sends a `user.interrupt`.

We hit this in production twice in one day (different consumer exceptions, same terminal state) and separately counted 29 items stuck in `starting` with null heartbeats.

## Ask

Either of:
1. **Deferred ack**: an opt-in poller mode that acks after the consumer callback returns successfully (at-least-once semantics), or
2. **nack/fail API**: a way for a consumer to return a failed item to a re-pendable state.

Related, happy to file separately: documenting which work-item states re-enqueue on which user events (empirically: `stopped` re-pends on message/interrupt; a dead-heartbeat `started` item only after a server-side stop ~5 min later; an acked-but-never-heartbeated `starting` item never), and a server-side janitor for `starting` + null-heartbeat items older than a TTL.

## Workaround we ship today

Consumer retry + `work.stop(force=True)` on final failure, plus a scheduled sweep that force-stops stale `starting` items and interrupts sessions parked in `requires_action` with no live worker. Effective, but every self-hosted-environment consumer will independently rediscover this failure class — ack-after-success semantics in the SDK would eliminate it.

貢獻指南

開啟貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。