From a0fa106e422b58054ad65a4ac78d1f5b9805c6ec Mon Sep 17 00:00:00 2001 From: George-iam Date: Sat, 28 Feb 2026 19:52:18 +0000 Subject: [PATCH] feat: add approvals and capabilities helpers to Python SDK Expose approvals.decision and capabilities.get methods with tests and quickstart examples so Track C parity can progress in GA clients. Made-with: Cursor --- README.md | 9 +++++++++ axme_sdk/client.py | 24 ++++++++++++++++++++++ tests/test_client.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/README.md b/README.md index 931e33c..50a1700 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,15 @@ with AxmeClient(config) as client: idempotency_key="reply-001", ) print(replied) + approval = client.decide_approval( + "55555555-5555-4555-8555-555555555555", + decision="approve", + comment="Looks good", + idempotency_key="approval-001", + ) + print(approval["approval"]["decision"]) + capabilities = client.get_capabilities() + print(capabilities["supported_intent_types"]) subscription = client.upsert_webhook_subscription( { "callback_url": "https://integrator.example/webhooks/axme", diff --git a/axme_sdk/client.py b/axme_sdk/client.py index e665975..8d206ac 100644 --- a/axme_sdk/client.py +++ b/axme_sdk/client.py @@ -138,6 +138,30 @@ def reply_inbox_thread( retryable=idempotency_key is not None, ) + def decide_approval( + self, + approval_id: str, + *, + decision: str, + comment: str | None = None, + idempotency_key: str | None = None, + trace_id: str | None = None, + ) -> dict[str, Any]: + payload: dict[str, Any] = {"decision": decision} + if comment is not None: + payload["comment"] = comment + return self._request_json( + "POST", + f"/v1/approvals/{approval_id}/decision", + json_body=payload, + idempotency_key=idempotency_key, + trace_id=trace_id, + retryable=idempotency_key is not None, + ) + + def get_capabilities(self, *, trace_id: str | None = None) -> dict[str, Any]: + return self._request_json("GET", "/v1/capabilities", trace_id=trace_id, retryable=True) + def upsert_webhook_subscription( self, payload: dict[str, Any], diff --git a/tests/test_client.py b/tests/test_client.py index d4c7705..426c2ad 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -231,6 +231,53 @@ def handler(request: httpx.Request) -> httpx.Response: } +def test_decide_approval_success() -> None: + approval_id = "55555555-5555-4555-8555-555555555555" + + def handler(request: httpx.Request) -> httpx.Response: + assert request.method == "POST" + assert request.url.path == f"/v1/approvals/{approval_id}/decision" + assert request.headers["idempotency-key"] == "approval-1" + assert request.read() == b'{"decision":"approve","comment":"approved"}' + return httpx.Response( + 200, + json={ + "ok": True, + "approval": { + "approval_id": approval_id, + "decision": "approve", + "comment": "approved", + "decided_at": "2026-02-28T00:00:01Z", + }, + }, + ) + + client = _client(handler) + assert client.decide_approval( + approval_id, + decision="approve", + comment="approved", + idempotency_key="approval-1", + )["approval"]["approval_id"] == approval_id + + +def test_get_capabilities_success() -> None: + def handler(request: httpx.Request) -> httpx.Response: + assert request.method == "GET" + assert request.url.path == "/v1/capabilities" + return httpx.Response( + 200, + json={ + "ok": True, + "capabilities": ["inbox", "intents"], + "supported_intent_types": ["intent.ask.v1", "intent.notify.v1"], + }, + ) + + client = _client(handler) + assert client.get_capabilities()["ok"] is True + + @pytest.mark.parametrize( ("status_code", "expected_exception"), [