From b44db24de2910eb921846791a2681c18a434c747 Mon Sep 17 00:00:00 2001 From: George-iam Date: Sat, 28 Feb 2026 16:10:31 +0000 Subject: [PATCH] feat: add correlation and idempotency intent options Update create_intent to enforce correlation_id and optionally send idempotency key so Python SDK write behavior matches Track C conformance expectations. Made-with: Cursor --- README.md | 11 +++++++++++ axme_sdk/client.py | 20 ++++++++++++++++++-- tests/test_client.py | 44 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9da1285..b99701e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,17 @@ config = AxmeClientConfig( with AxmeClient(config) as client: print(client.health()) + result = client.create_intent( + { + "intent_type": "notify.message.v1", + "from_agent": "agent://example/sender", + "to_agent": "agent://example/receiver", + "payload": {"text": "hello"}, + }, + correlation_id="11111111-1111-1111-1111-111111111111", + idempotency_key="create-intent-001", + ) + print(result) ``` ## Development diff --git a/axme_sdk/client.py b/axme_sdk/client.py index 776d208..a5afcd3 100644 --- a/axme_sdk/client.py +++ b/axme_sdk/client.py @@ -44,8 +44,24 @@ def health(self) -> dict[str, Any]: raise AxmeHttpError(response.status_code, response.text) return response.json() - def create_intent(self, payload: dict[str, Any]) -> dict[str, Any]: - response = self._http.post("/v1/intents", json=payload) + def create_intent( + self, + payload: dict[str, Any], + *, + correlation_id: str, + idempotency_key: str | None = None, + ) -> dict[str, Any]: + request_payload = dict(payload) + existing_correlation_id = request_payload.get("correlation_id") + if existing_correlation_id is not None and existing_correlation_id != correlation_id: + raise ValueError("payload correlation_id must match correlation_id argument") + request_payload["correlation_id"] = correlation_id + + headers: dict[str, str] | None = None + if idempotency_key is not None: + headers = {"Idempotency-Key": idempotency_key} + + response = self._http.post("/v1/intents", json=request_payload, headers=headers) if response.status_code >= 400: raise AxmeHttpError(response.status_code, response.text) return response.json() diff --git a/tests/test_client.py b/tests/test_client.py index c8d0c47..63cdaba 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,5 +1,7 @@ from __future__ import annotations +import json + import httpx import pytest @@ -36,16 +38,31 @@ def handler(request: httpx.Request) -> httpx.Response: def test_create_intent_success() -> None: - payload = {"intent_type": "notify", "recipient": "agent://user/test"} + payload = { + "intent_type": "notify.message.v1", + "to_agent": "agent://user/test", + "from_agent": "agent://user/self", + "payload": {"text": "hello"}, + } def handler(request: httpx.Request) -> httpx.Response: assert request.method == "POST" assert request.url.path == "/v1/intents" - assert request.read() == b'{"intent_type":"notify","recipient":"agent://user/test"}' + body = json.loads(request.read().decode("utf-8")) + assert body["correlation_id"] == "11111111-1111-1111-1111-111111111111" + assert body["intent_type"] == "notify.message.v1" + assert request.headers["idempotency-key"] == "idem-1" return httpx.Response(200, json={"intent_id": "it_123"}) client = _client(handler) - assert client.create_intent(payload) == {"intent_id": "it_123"} + assert ( + client.create_intent( + payload, + correlation_id="11111111-1111-1111-1111-111111111111", + idempotency_key="idem-1", + ) + == {"intent_id": "it_123"} + ) def test_create_intent_raises_http_error() -> None: @@ -55,6 +72,25 @@ def handler(request: httpx.Request) -> httpx.Response: client = _client(handler, api_key="bad-token") with pytest.raises(AxmeHttpError) as exc_info: - client.create_intent({"intent_type": "notify"}) + client.create_intent( + {"intent_type": "notify.message.v1", "to_agent": "agent://x", "from_agent": "agent://y", "payload": {}}, + correlation_id="11111111-1111-1111-1111-111111111111", + ) assert exc_info.value.status_code == 401 + + +def test_create_intent_raises_for_mismatched_correlation_id() -> None: + client = _client(lambda request: httpx.Response(200, json={"intent_id": "it_123"})) + + with pytest.raises(ValueError, match="payload correlation_id"): + client.create_intent( + { + "intent_type": "notify.message.v1", + "to_agent": "agent://x", + "from_agent": "agent://y", + "payload": {}, + "correlation_id": "22222222-2222-2222-2222-222222222222", + }, + correlation_id="11111111-1111-1111-1111-111111111111", + )