diff --git a/docs/changelog/compatibility-guide.mdx b/docs/changelog/compatibility-guide.mdx index 33e01f7f..719ea184 100644 --- a/docs/changelog/compatibility-guide.mdx +++ b/docs/changelog/compatibility-guide.mdx @@ -4,27 +4,23 @@ description: "Compatibility guide for Honcho's SDKs and API" icon: "shield-check" --- -This guide helps you understand which versions of Honcho's API are compatible with which SDK versions. +This guide helps you match the right SDK version to your Honcho API version. Newer SDK patch versions are always backward-compatible within the same major version — install the latest patch for your range. -## Version Compatibility - -### Honcho API v3.0.2 (Current) +## Current Versions - **Compatible Version:** v2.0.0 + **Latest:** v2.0.1 - Install with: ```bash - npm install @honcho-ai/sdk@2.0.0 + npm install @honcho-ai/sdk ``` - **Compatible Version:** v2.0.0 + **Latest:** v2.0.1 - Install with: ```bash - pip install honcho-ai==2.0.0 + pip install honcho-ai ``` @@ -34,9 +30,9 @@ This guide helps you understand which versions of Honcho's API are compatible wi | Honcho API Version | TypeScript SDK | Python SDK | |-------------------|---------------|------------| -| v3.0.2 (Current) | v2.0.0 | v2.0.0 | -| v3.0.1 | v2.0.0 | v2.0.0 | -| v3.0.0 | v2.0.0 | v2.0.0 | +| v3.0.2 (Current) | v2.0.0+ | v2.0.0+ | +| v3.0.1 | v2.0.0+ | v2.0.0+ | +| v3.0.0 | v2.0.0+ | v2.0.0+ | | v2.5.1 | v1.6.0 | v1.6.0 | | v2.5.0 | v1.6.0 | v1.6.0 | | v2.4.3 | v1.5.0 | v1.5.0 | diff --git a/sdks/python/pyproject.toml b/sdks/python/pyproject.toml index 20204d04..dd389d96 100644 --- a/sdks/python/pyproject.toml +++ b/sdks/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "honcho-ai" -version = "2.0.0" +version = "2.0.1" description = "Official DX Optimized Python SDK for Honcho" dynamic = ["readme"] license = "Apache-2.0" diff --git a/sdks/python/src/honcho/__init__.py b/sdks/python/src/honcho/__init__.py index cf8d8bb5..35f42dce 100644 --- a/sdks/python/src/honcho/__init__.py +++ b/sdks/python/src/honcho/__init__.py @@ -66,7 +66,7 @@ from .types import ( DialecticStreamResponse, ) -__version__ = "2.0.0" +__version__ = "2.0.1" __author__ = "Plastic Labs" __email__ = "hello@plasticlabs.ai" diff --git a/sdks/python/src/honcho/aio.py b/sdks/python/src/honcho/aio.py index 1d72946a..2d02418e 100644 --- a/sdks/python/src/honcho/aio.py +++ b/sdks/python/src/honcho/aio.py @@ -604,6 +604,25 @@ class PeerAio(AsyncMetadataConfigMixin): response = PeerCardResponse.model_validate(data) return response.peer_card + @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) + async def set_card( + self, + peer_card: list[str], + target: str | PeerBase | None = None, + ) -> list[str] | None: + """Set the peer card asynchronously.""" + await self._peer._honcho._ensure_workspace_async() + target_id = resolve_id(target) + + query = {"target": target_id} if target_id else None + data = await self._peer._honcho._async_http_client.put( + routes.peer_card(self._peer.workspace_id, self._peer.id), + body={"peer_card": peer_card}, + query=query, + ) + response = PeerCardResponse.model_validate(data) + return response.peer_card + @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) async def representation( self, diff --git a/sdks/python/src/honcho/peer.py b/sdks/python/src/honcho/peer.py index ea71fc79..f53542c1 100644 --- a/sdks/python/src/honcho/peer.py +++ b/sdks/python/src/honcho/peer.py @@ -476,6 +476,38 @@ class Peer(PeerBase, MetadataConfigMixin): return response.peer_card + @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) + def set_card( + self, + peer_card: list[str], + target: str | PeerBase | None = None, + ) -> list[str] | None: + """ + Set the peer card for this peer. + + Makes an API call to set the peer card. If a target is provided, sets this + peer's local card of the target peer. + + Args: + peer_card: A list of strings to set as the peer card. + target: Optional target peer for local card. If provided, sets this + peer's card of the target peer. Can be a Peer object or peer ID string. + + Returns: + A list of strings representing the updated peer card, or None if none is available + """ + self._honcho._ensure_workspace() + target_id = resolve_id(target) + + query = {"target": target_id} if target_id else None + data = self._honcho._http.put( + routes.peer_card(self.workspace_id, self.id), + body={"peer_card": peer_card}, + query=query, + ) + response = PeerCardResponse.model_validate(data) + return response.peer_card + @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) def representation( self, diff --git a/sdks/typescript/__tests__/peer.test.ts b/sdks/typescript/__tests__/peer.test.ts index 9a3b3b61..35f0b32e 100644 --- a/sdks/typescript/__tests__/peer.test.ts +++ b/sdks/typescript/__tests__/peer.test.ts @@ -430,6 +430,48 @@ describe('Peer', () => { }) }) + // =========================================================================== + // Set Peer Card (PUT /peers/:id/card) + // =========================================================================== + + describe('PUT /peers/:id/card', () => { + test('setCard sets and returns peer card', async () => { + const peer = await client.peer('setcard-peer') + + const cardData = ['fact one', 'fact two'] + const result = await peer.setCard(cardData) + + expect(result).toEqual(cardData) + + // Verify with get + const card = await peer.card() + expect(card).toEqual(cardData) + }) + + test('setCard with target peer', async () => { + const observer = await client.peer('setcard-observer') + const observed = await client.peer('setcard-observed') + + const cardData = ['target likes TypeScript', 'target is clever'] + const result = await observer.setCard(cardData, observed) + + expect(result).toEqual(cardData) + + // Verify with get + const card = await observer.card(observed) + expect(card).toEqual(cardData) + }) + + test('setCard with target ID string', async () => { + const peer = await client.peer('setcard-string-peer') + + const cardData = ['some fact'] + const result = await peer.setCard(cardData, 'setcard-string-target') + + expect(result).toEqual(cardData) + }) + }) + // =========================================================================== // Peer Context (POST /peers/:id/context) // =========================================================================== diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json index 956066ed..618a0acf 100644 --- a/sdks/typescript/package.json +++ b/sdks/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@honcho-ai/sdk", - "version": "2.0.0", + "version": "2.0.1", "description": "Official DX Optimized TypeScript SDK for Honcho", "author": "Plastic Labs ", "license": "Apache-2.0", diff --git a/sdks/typescript/src/peer.ts b/sdks/typescript/src/peer.ts index 2ce07468..9d81453b 100644 --- a/sdks/typescript/src/peer.ts +++ b/sdks/typescript/src/peer.ts @@ -295,6 +295,18 @@ export class Peer { ) } + private async _setCard(params: { + peer_card: string[] + target?: string + }): Promise { + await this._ensureWorkspace() + const { peer_card, ...query } = params + return this._http.put( + `/${API_VERSION}/workspaces/${this.workspaceId}/peers/${this.id}/card`, + { body: { peer_card }, query } + ) + } + // =========================================================================== // Public Methods // =========================================================================== @@ -662,6 +674,30 @@ export class Peer { return response.peer_card } + /** + * Set the peer card for this peer. + * + * Makes an API call to set the peer card. If a target is provided, sets this + * peer's local card of the target peer. + * + * @param peerCard - An array of strings to set as the peer card. + * @param target - Optional target peer for local card. If provided, sets this + * peer's card of the target peer. Can be a Peer object or peer ID string. + * @returns Promise resolving to an array of strings containing the updated peer card items, + * or null if no peer card exists + */ + async setCard( + peerCard: string[], + target?: string | Peer + ): Promise { + const validatedTarget = CardTargetSchema.parse(target) + const response = await this._setCard({ + peer_card: peerCard, + target: validatedTarget, + }) + return response.peer_card + } + /** * Get a subset of Honcho's Representation of a peer. * diff --git a/tests/routes/test_peers.py b/tests/routes/test_peers.py index 84f23887..c8047f02 100644 --- a/tests/routes/test_peers.py +++ b/tests/routes/test_peers.py @@ -909,3 +909,52 @@ async def test_get_peer_card_with_data( assert response.status_code == 200 data = response.json() assert data["peer_card"] == target_card_content + + +def test_set_peer_card(client: TestClient, sample_data: tuple[Workspace, Peer]): + """Test setting peer cards via the PUT endpoint.""" + test_workspace, observer_peer = sample_data + + # Create a target peer + target_peer_name = str(generate_nanoid()) + response = client.post( + f"/v3/workspaces/{test_workspace.name}/peers", + json={"name": target_peer_name}, + ) + assert response.status_code in [200, 201] + + # Set the observer's own card + self_card = ["I am a test peer", "I like writing tests"] + response = client.put( + f"/v3/workspaces/{test_workspace.name}/peers/{observer_peer.name}/card", + json={"peer_card": self_card}, + ) + assert response.status_code == 200 + data = response.json() + assert data["peer_card"] == self_card + + # Verify with GET + response = client.get( + f"/v3/workspaces/{test_workspace.name}/peers/{observer_peer.name}/card" + ) + assert response.status_code == 200 + assert response.json()["peer_card"] == self_card + + # Set a card for the target peer + target_card = ["Target is helpful", "Target knows Python"] + response = client.put( + f"/v3/workspaces/{test_workspace.name}/peers/{observer_peer.name}/card", + params={"target": target_peer_name}, + json={"peer_card": target_card}, + ) + assert response.status_code == 200 + data = response.json() + assert data["peer_card"] == target_card + + # Verify with GET + response = client.get( + f"/v3/workspaces/{test_workspace.name}/peers/{observer_peer.name}/card", + params={"target": target_peer_name}, + ) + assert response.status_code == 200 + assert response.json()["peer_card"] == target_card diff --git a/tests/sdk/sdk_integration_test.py b/tests/sdk/sdk_integration_test.py index f5a14fe8..cffe6b1c 100644 --- a/tests/sdk/sdk_integration_test.py +++ b/tests/sdk/sdk_integration_test.py @@ -104,3 +104,37 @@ def test_message_and_chat_operations(honcho_test_client: Honcho): # This is a mock response from the agent _response = user.chat("What did I ask about?") + + +def test_peer_card_operations(honcho_test_client: Honcho): + """ + Tests setting and getting peer cards. + """ + peer = honcho_test_client.peer(id="card-test-peer") + target = honcho_test_client.peer(id="card-test-target") + + # Initially card should be None + card = peer.card() + assert card is None + + # Set own card + own_card = ["I am a helpful assistant", "I enjoy learning"] + result = peer.set_card(own_card) + assert result == own_card + + # Verify with get + card = peer.card() + assert card == own_card + + # Set card for target + target_card = ["Target likes Python", "Target is friendly"] + result = peer.set_card(target_card, target=target) + assert result == target_card + + # Verify with get + card = peer.card(target=target) + assert card == target_card + + # Own card should still be unchanged + card = peer.card() + assert card == own_card diff --git a/uv.lock b/uv.lock index 634827d0..6af5c365 100644 --- a/uv.lock +++ b/uv.lock @@ -1136,7 +1136,7 @@ dev = [ [[package]] name = "honcho-ai" -version = "2.0.0" +version = "2.0.1" source = { editable = "sdks/python" } dependencies = [ { name = "httpx" },