Skip to content

feat: typed assess errors + X-Quota headers + signer_match types (python parity)#21

Merged
vvillait88 merged 8 commits intomainfrom
monetization-branding-rollout
May 2, 2026
Merged

feat: typed assess errors + X-Quota headers + signer_match types (python parity)#21
vvillait88 merged 8 commits intomainfrom
monetization-branding-rollout

Conversation

@vvillait88
Copy link
Copy Markdown
Contributor

Summary

  • Add typed AssessError taxonomy mirroring node-sdk so consumers can branch on denial codes without string parsing
  • Capture X-Quota-Limit / X-Quota-Used / X-Quota-Reset headers on AssessResponse
  • Add ResolveSigner request type + SignerMatch response type for server-side signer matching on /v1/assess (TEC-263 SDK side)
  • Add telemetry_signer_match() method (sync + async) for posting gate-side outcomes
  • Wrap all httpx errors (not just timeouts) for parity with node-sdk's error surface
  • Parity tests added for retry-quota / generic-4xx / token-expired-empty-body
  • ty dev bump 0.0.33 → 0.0.34
  • Bump version 2.0.2 → 2.1.0 (additive minor)

Closes TEC-274.

Test plan

  • Unit tests pass locally (uv run pytest)
  • Coverage above 95% threshold (--cov-fail-under=95)
  • Lefthook pre-push (ty + vulture) passes
  • CI green on this PR
  • uv build produces clean wheel

🤖 Generated with Claude Code

vvillait88 and others added 8 commits May 1, 2026 07:06
…er_match

Parity with @agent-score/sdk equivalent change:

- New error subclasses (all subclass AgentScoreError so existing catches still work):
  PaymentRequiredError (402), TokenExpiredError (401 token_expired with parsed body
  fields exposed: verify_url/session_id/poll_secret/poll_url/next_steps/agent_memory),
  InvalidCredentialError (401 invalid_credential), QuotaExceededError (429 quota_exceeded),
  RateLimitedError (429 rate_limited), TimeoutError (httpx.TimeoutException wrapped)
- AssessResponse gains optional quota field { limit, used, reset } captured from
  X-Quota-Limit / X-Quota-Used / X-Quota-Reset response headers
- New telemetry_signer_match / atelemetry_signer_match methods — fire-and-forget POST
  to /v1/telemetry/signer-match
- Internal: _send_sync_with_response / _send_async_with_response variants expose the
  raw httpx.Response so assess() can read response headers; shared
  _build_error_from_response helper does status+code routing once
- All typed errors + QuotaInfo exported from agentscore.__init__

Backward-compatible: all new error classes inherit AgentScoreError; existing
except AgentScoreError blocks unaffected. New quota field is optional (NotRequired).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit caught a regression: `_send_sync` / `_send_async` only wrapped
`httpx.TimeoutException` into our typed TimeoutError. Other httpx errors
(ConnectError, NetworkError, ProtocolError) propagated unwrapped, breaking the
"all SDK errors are AgentScoreError subclasses" contract callers rely on.

- New `_do_sync` / `_do_async` helpers wrap every `httpx.HTTPError` into
  AgentScoreError(code='network_error', status_code=0); `httpx.TimeoutException`
  specifically becomes our TimeoutError. Matches node-sdk's catch-all in `request<T>`.
- Updated `test_connect_error_raises_agentscore_error` to match the new contract
  (was misnamed — asserted raw httpx.ConnectError previously).

Docs:
- README: "Typed error classes" subsection with class table; quota-observability
  + telemetry sections; explicit note about TimeoutError shadowing the builtin
  (import explicitly from agentscore.errors).
- CLAUDE.md: new methods listed; new "Errors + observability" section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures TEC-274 minor: new public exports (PaymentRequiredError,
TokenExpiredError, InvalidCredentialError, QuotaExceededError, RateLimitedError,
TimeoutError) + AssessResponse.quota field + telemetry_signer_match methods.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mpty-body

Mirror of the node-sdk edge-case tests just added. All paths the audit flagged
as "behavior correct, not tested":

- assess captures quota from retry response on 429 → 200 (not the 429).
- 400 invalid_request falls through to generic AgentScoreError (no typed
  subclass match). Type check uses `type(err) is AgentScoreError` to prove
  exactly the base class.
- 401 token_expired with empty body — TokenExpiredError instance fields stay
  None, error is still TokenExpiredError.
- 401 token_expired with wrong-typed body (number for verify_url) — fields
  stay None, raw values preserved in details.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…h assess/aassess

Add ResolveSigner request type and SignerMatch response type matching the
API's new server-side wallet-signer-match. assess() / aassess() gain an
optional resolve_signer parameter; AssessResponse gains optional
signer_match. Drop vestigial on_the_fly + updated_at fields from
AssessResponse — they were never read by any caller.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Parity with how every other type in the package is exposed via
agentscore/__init__.py — without this, consumers had to do
`from agentscore.types import ResolveSigner` instead of the documented
`from agentscore import ResolveSigner`.
@vvillait88 vvillait88 merged commit aa8a810 into main May 2, 2026
7 checks passed
@vvillait88 vvillait88 deleted the monetization-branding-rollout branch May 2, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant