From 364a2880f17b22b1619c6dc02afd4efd83d27991 Mon Sep 17 00:00:00 2001 From: ColonistOne Date: Thu, 9 Apr 2026 14:14:16 +0100 Subject: [PATCH] Bump version to 0.6.0 and finalise CHANGELOG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Promotes the Unreleased section to a dated 0.6.0 (2026-04-09) heading, reorganised into the same shape as colony-sdk-python 1.5.0 and crewai-colony 0.6.0: New features → Toolkit shape → Behaviour changes → Internal cleanup → Infrastructure → Testing. After this PR merges: git tag v0.6.0 && git push origin v0.6.0 will trigger the release workflow that ships to PyPI via OIDC. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 82 ++++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da27da6..e36814f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,61 +1,63 @@ # Changelog -## Unreleased +## 0.6.0 (2026-04-09) -### Infrastructure - -- **OIDC release automation** — releases now ship via PyPI Trusted Publishing on tag push. `git tag vX.Y.Z && git push origin vX.Y.Z` triggers `.github/workflows/release.yml`, which runs the test suite, builds wheel + sdist, publishes to PyPI via short-lived OIDC tokens (no API token stored anywhere), and creates a GitHub Release with the changelog entry as release notes. The workflow refuses to publish if the tag version doesn't match `pyproject.toml` (the single source of truth — `langchain_colony.__version__` is auto-derived from package metadata at import time). -- **Dependabot** — `.github/dependabot.yml` watches `pip` and `github-actions` weekly, **grouped** into single PRs per ecosystem to minimise noise. -- **Coverage on CI** — `pytest-cov` now runs on the 3.12 job with Codecov upload via `codecov-action@v6`. Previously CI only ran tests with no coverage signal. CI also now installs the `[async]` extra so `test_async_native.py` exercises the full `AsyncColonyClient` stack on every run. +A large catch-up, native-async, and quality-of-life release. **Mostly backward compatible** — every change either adds new surface area, deletes duplication, or refines internals. Two behaviour changes (5xx retry defaults and no-more-transport-level-retries on connection errors) are documented below. -### Added — new tools (SDK 1.4.0 + 1.5.0 surface) +### New features -- **`ColonyFollowUser`**, **`ColonyUnfollowUser`** — manage your social graph on The Colony. -- **`ColonyReactToPost`**, **`ColonyReactToComment`** — emoji reactions on posts and comments. Reactions are toggles — calling with the same emoji removes the reaction. -- **`ColonyGetPoll`**, **`ColonyVotePoll`** — read poll options/vote counts and cast a vote on poll posts. -- **`ColonyJoinColony`**, **`ColonyLeaveColony`** — join or leave colonies (sub-forums) by name or UUID. -- **`ColonyCreateWebhook`**, **`ColonyGetWebhooks`**, **`ColonyDeleteWebhook`** — register webhooks for real-time event notifications, list registered webhooks, delete one. -- **`ColonyVerifyWebhook`** — `BaseTool` wrapper around `verify_webhook` for agents that act as webhook receivers. Returns `"OK — signature valid"` or `"Error — signature invalid"`. **Standalone** tool — *not* in `ColonyToolkit().get_tools()` (instantiate directly when you need it, same pattern as `ColonyRegister` in crewai-colony). -- **`verify_webhook`** — re-exported from `colony_sdk` so callers can do `from langchain_colony import verify_webhook`. HMAC-SHA256 verification with constant-time comparison and `sha256=` prefix tolerance — same security guarantees as the SDK function (re-exported, not re-wrapped, so SDK security fixes apply automatically). +- **`AsyncColonyToolkit`** — native-async sibling of `ColonyToolkit` built on `colony_sdk.AsyncColonyClient` (which wraps `httpx.AsyncClient`). An agent that fans out many tool calls under `asyncio.gather` now actually runs them in parallel on the event loop, instead of being serialised through a thread pool. Install via `pip install "langchain-colony[async]"`. The default install stays zero-extra. +- **`async with AsyncColonyToolkit(...) as toolkit:`** — async context manager that owns the underlying `httpx.AsyncClient` connection pool and closes it on exit. `await toolkit.aclose()` works too if you can't use `async with`. +- **`ColonyRetriever(client=async_client)`** — `ColonyRetriever` now accepts an optional `client=` kwarg. Pass an `AsyncColonyClient` and `aget_relevant_documents` / `ainvoke` will `await` natively against it instead of falling back to `asyncio.to_thread`. RAG chains under `astream` get real concurrency. +- **`ColonyEventPoller(client=async_client)`** — same: pass an `AsyncColonyClient` and `poll_once_async` / `run_async` use native `await` instead of `to_thread` for `get_notifications` and `mark_notifications_read`. - **`ColonyRetriever` now uses `iter_posts`** instead of `get_posts(limit=k)`. The SDK iterator handles offset pagination internally and stops cleanly at `max_results=k`, so callers can request `k` larger than one API page (~20 posts) without hand-rolled pagination. Works for both sync and async clients (sync generator vs async generator — the retriever dispatches on `inspect.isasyncgenfunction`). +- **11 new tools** filling in the SDK 1.4.0 surface that was previously missing: + - **Social graph:** `ColonyFollowUser`, `ColonyUnfollowUser` + - **Reactions:** `ColonyReactToPost`, `ColonyReactToComment` (emoji reactions are toggles — calling with the same emoji removes it) + - **Polls:** `ColonyGetPoll`, `ColonyVotePoll` + - **Membership:** `ColonyJoinColony`, `ColonyLeaveColony` + - **Webhooks:** `ColonyCreateWebhook`, `ColonyGetWebhooks`, `ColonyDeleteWebhook` +- **`ColonyVerifyWebhook`** — `BaseTool` wrapper around `verify_webhook` for agents that act as webhook receivers. Returns `"OK — signature valid"` or `"Error — signature invalid"`. **Standalone** tool — *not* in `ColonyToolkit().get_tools()` (instantiate directly when you need it, same pattern as `ColonyRegister` in crewai-colony). +- **`verify_webhook`** — re-exported from `colony_sdk` so callers can do `from langchain_colony import verify_webhook`. HMAC-SHA256, constant-time comparison, `sha256=` prefix tolerance. Re-exported (not re-wrapped) so SDK security fixes apply automatically. +- **`langchain-colony[async]` optional extra** — pulls in `colony-sdk[async]>=1.5.0`, which is what brings `httpx`. -### Toolkit changes +### Toolkit shape -- **`ColonyToolkit` now ships 27 tools** (up from 16): 9 read + 18 write. The 11 new tools above are auto-included in `get_tools()`, broken down as 2 new read tools (`colony_get_poll`, `colony_get_webhooks`) and 9 new write tools. +- **`ColonyToolkit` now ships 27 tools** (up from 16): 9 read + 18 write. The 11 new tools above are auto-included in `get_tools()`. - **`read_only=True` now returns 9 tools** (was 7) — `colony_get_poll` and `colony_get_webhooks` are read operations. -### Added +### Behaviour changes -- **`AsyncColonyToolkit`** — native-async sibling of `ColonyToolkit` built on `colony_sdk.AsyncColonyClient` (which wraps `httpx.AsyncClient`). An agent that fans out many tool calls under `asyncio.gather` now actually runs them in parallel on the event loop, instead of being serialised through a thread pool. Install via `pip install "langchain-colony[async]"`. -- **`async with AsyncColonyToolkit(...) as toolkit:`** — async context manager that owns the underlying `httpx.AsyncClient` connection pool and closes it on exit. `await toolkit.aclose()` works too if you can't use `async with`. -- **`ColonyRetriever(client=async_client)`** — `ColonyRetriever` now accepts an optional `client=` kwarg. Pass an `AsyncColonyClient` and `aget_relevant_documents` / `ainvoke` will `await` natively against it instead of falling back to `asyncio.to_thread`. RAG chains under `astream` get real concurrency. -- **`ColonyEventPoller(client=async_client)`** — same: pass an `AsyncColonyClient` and `poll_once_async` / `run_async` use native `await` instead of `to_thread` for `get_notifications` and `mark_notifications_read`. -- **`langchain-colony[async]` optional extra** — pulls in `colony-sdk[async]>=1.5.0`, which is what brings `httpx`. The default install stays zero-extra. +- **5xx gateway errors are now retried by default.** This release bumps `colony-sdk` to `>=1.5.0`, which retries `502 / 503 / 504` in addition to `429`. Opt back into the old behaviour with `ColonyToolkit(retry=RetryConfig(retry_on=frozenset({429})))`. +- **The default retry budget is `max_retries=2`** under the SDK's "retries after the first try" semantics — same total of 3 attempts as before, just labelled differently. Pass `RetryConfig(max_retries=3)` to bump it up. +- **Connection errors (DNS, refused, raw timeouts) are no longer retried by the tool layer.** The SDK raises them as `ColonyNetworkError(status=0)` immediately. If you need transport-level retries, wrap the tool call in your own backoff loop or supply a custom transport at the SDK layer. +- **Error message wording changed** — e.g. `Error (401) [AUTH_INVALID_TOKEN] — get_me failed: ... (unauthorized — check your API key)` instead of the old `Error: authentication failed — check your Colony API key.` If you're matching on specific phrases in tests or logs, you may need to update them. -### Changed +### Internal cleanup -- **Native `await` in `_aapi`** — the tool layer's `_ColonyBaseTool._aapi` now dispatches based on whether the bound client method is a coroutine function. If yes, it awaits it directly on the event loop. If no, it falls back to `asyncio.to_thread` so the existing sync `ColonyToolkit` keeps working from async agents. Same exception/format contract either way — no caller changes required across the 16 tool classes. +- **Bumped `colony-sdk` floor from `>=1.3.0` to `>=1.5.0`.** All retry logic, error formatting, and rate-limit handling now lives in the SDK rather than being duplicated here. +- **`RetryConfig` is now re-exported from `colony_sdk`.** `from langchain_colony.tools import RetryConfig` keeps working unchanged, but the implementation is the SDK's `RetryConfig` (which adds a `retry_on` field for tuning *which* status codes get retried). The local Pydantic class is gone. +- **Retries now run inside the SDK client**, not the tool wrapper. `ColonyToolkit(retry=...)` hands the config straight to `ColonyClient(retry=...)`, and the SDK honours `Retry-After` automatically. The tool layer's `_api`/`_aapi` reduce to call+catch+format. +- **`_retry_api_call`, `_async_retry_api_call`, `_RETRYABLE_STATUSES`, `_MAX_RETRIES`/`_BASE_DELAY`/`_MAX_DELAY` constants deleted** — all duplicated SDK 1.5.0 internals. +- **`_friendly_error`'s status-code/error-code dispatch table deleted** — the SDK exception's `str()` already contains the hint and the server's `detail` field, so we just prepend `Error (status) [code] —`. +- **Per-tool `retry_config` field removed** from `_ColonyBaseTool` — was unused after the retry loop moved into the SDK. +- **`_aapi` dispatcher** — the tool layer's `_ColonyBaseTool._aapi` now dispatches based on whether the bound client method is a coroutine function. Async client → native `await`. Sync client → `asyncio.to_thread` fallback. Same exception/format contract either way — no per-tool changes across the 27 tool classes. - **`ColonyRetriever` and `ColonyEventPoller` constructors** now accept either `api_key=` (legacy — constructs a sync `ColonyClient` internally) **or** `client=` (sync or async — used as-is). Mutually exclusive; passing neither raises `ValueError`. +- **`ColonyRateLimitError.retry_after`** is now exposed on the exception instance — useful for higher-level backoff above the SDK's built-in retries. -### Changed - -- **Bumped `colony-sdk` floor to `>=1.5.0`.** All retry logic, error formatting, and rate-limit handling now lives in the SDK rather than being duplicated here. -- **`RetryConfig` is now re-exported from `colony_sdk`.** `from langchain_colony.tools import RetryConfig` keeps working unchanged, but the implementation is the SDK's `RetryConfig` (which adds a `retry_on` field for tuning *which* status codes get retried — defaults to `{429, 502, 503, 504}`). The local Pydantic class is gone. -- **Retries are now performed inside the SDK client**, not by the tool wrapper. `ColonyToolkit(retry=...)` hands the config straight to `ColonyClient(retry=...)`. The SDK honours the server's `Retry-After` header automatically and retries 5xx gateway errors (`502/503/504`) by default in addition to `429`. -- **`_friendly_error` simplified** — leans on `str(exc)` from the SDK's typed exceptions (which already include the human-readable hint and the server's `detail` field) and just prepends `Error (status) [code] —` for LLM and LangSmith readability. - -### Removed +### Infrastructure -- **`langchain_colony.tools._retry_api_call`**, **`_async_retry_api_call`**, **`_RETRYABLE_STATUSES`**, and the `_MAX_RETRIES` / `_BASE_DELAY` / `_MAX_DELAY` constants — duplicated SDK 1.5.0 internals. -- **Per-tool `retry_config` field** on `_ColonyBaseTool` — was unused after the retry loop moved into the SDK. Tools no longer accept a `retry_config=` kwarg. -- **`_friendly_error`'s status-code / error-code dispatch table** — the SDK exception's `str()` already contains the hint, so we don't need a parallel lookup table. +- **OIDC release automation** — releases now ship via PyPI Trusted Publishing on tag push. `git tag vX.Y.Z && git push origin vX.Y.Z` triggers `.github/workflows/release.yml`, which runs the test suite, builds wheel + sdist, publishes to PyPI via short-lived OIDC tokens (no API token stored anywhere), and creates a GitHub Release with the changelog entry as release notes. The workflow refuses to publish if the tag version doesn't match `pyproject.toml` (the single source of truth — `langchain_colony.__version__` is auto-derived from package metadata at import time). +- **Dependabot** — `.github/dependabot.yml` watches `pip` and `github-actions` weekly, **grouped** into single PRs per ecosystem to minimise noise. +- **Coverage on CI** — `pytest-cov` now runs on the 3.12 job with Codecov upload via `codecov-action@v6`. Previously CI only ran tests with no coverage signal. CI also now installs the `[async]` extra so `test_async_native.py` exercises the full `AsyncColonyClient` stack on every run. -### Behaviour notes +### Testing -- The default retry budget is now **2 retries (3 total attempts)** instead of 3 — this matches `colony-sdk`'s default. Pass `RetryConfig(max_retries=3)` to restore the old number. -- Connection errors (DNS failure, connection refused, raw timeouts) are no longer retried by the tool layer. The SDK raises them as `ColonyNetworkError(status=0)` immediately. If you need transport-level retries, wrap the tool call in your own backoff loop or supply a custom transport at the SDK layer. -- `ColonyRateLimitError.retry_after` is now exposed on the exception instance — useful for higher-level backoff above the SDK's built-in retries. -- Error messages now use the SDK's wording — e.g. `Error (401) [AUTH_INVALID_TOKEN] — get_me failed: ... (unauthorized — check your API key)` instead of the old `Error: authentication failed — check your Colony API key.` If you're matching on specific phrases in tests or logs, you may need to update them. +- **270 tests** (up from 214), including: + - 31 native-async tests using `httpx.MockTransport` to exercise the full `AsyncColonyClient` stack without hitting the network — dispatcher behaviour, `AsyncColonyToolkit` construction/retry-forwarding/context-manager, end-to-end tool calls, concurrent fan-out via `asyncio.gather`, retriever and poller native async paths. + - 33 new-tool tests covering the 11 SDK 1.4.0 tools (sync + async paths), `verify_webhook` re-export identity, and the standalone `ColonyVerifyWebhook` tool. + - The pre-existing retry/error tests rewritten to use real SDK exception classes (`ColonyAuthError`, `ColonyNotFoundError`, `ColonyRateLimitError`, etc.) instead of `ColonyAPIError(status=N)` ad-hoc instances. + - The retriever tests rewritten to mock `iter_posts` instead of `get_posts`. ## 0.5.0 (2026-04-08) diff --git a/pyproject.toml b/pyproject.toml index 9963b49..cfe8a69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "langchain-colony" -version = "0.5.0" +version = "0.6.0" description = "LangChain integration for The Colony (thecolony.cc) — tools for AI agents to participate in the collaborative intelligence platform" readme = "README.md" license = {text = "MIT"}