diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d2e105..64cb14f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +5,12 @@ All notable changes to the AxonFlow Python SDK will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [6.3.0] - Release Pending (2026-04-09) +## [6.3.0] - 2026-04-09 ### Added - -- **Explicit IPv6 + RFC1918 boundary test coverage.** The v6.2.0 test suite relied on Python stdlib `ipaddress.is_private` for correctness and didn't assert the behavior explicitly. New cases cover: - - `172.15.0.1` and `172.32.0.1` must be `remote` (RFC1918 boundary) - - `172.16.0.0` and `172.31.255.255` must be `private_network` - - IPv6 ULA (`fd00::1`, `fd12:3456:789a::1`, `fc00::1`) → `private_network` - - IPv6 link-local (`fe80::1`) → `private_network` - - Public IPv6 (`2001:4860:4860::8888`, `2606:4700:4700::1111`) → `remote` - - IPv6 loopback `::1` → `localhost` - -No runtime behavior change — Python's stdlib classifier was already correct for all these cases. The added tests are cross-SDK parity with TS/Java/Go. +- `AXONFLOW_TRY=1` environment variable to connect to `try.getaxonflow.com` shared evaluation server +- `register_try()` helper in `axonflow.community` for self-registering a tenant +- Checkpoint telemetry reports `endpoint_type: "community-saas"` when try mode is active --- diff --git a/axonflow/client.py b/axonflow/client.py index 3033513..d2ec080 100644 --- a/axonflow/client.py +++ b/axonflow/client.py @@ -358,11 +358,18 @@ def __init__( As of v1.0.0, all routes go through a single endpoint (Single Entry Point Architecture). """ - # Support AXONFLOW_AGENT_URL env var for backwards compatibility - resolved_endpoint = endpoint or os.environ.get("AXONFLOW_AGENT_URL") - if not resolved_endpoint: - msg = "endpoint is required (or set AXONFLOW_AGENT_URL environment variable)" - raise TypeError(msg) + # Try mode: auto-connect to try.getaxonflow.com (must be checked before endpoint validation) + if os.environ.get("AXONFLOW_TRY") == "1": + resolved_endpoint = "https://try.getaxonflow.com" + if not client_id: + msg = "client_id is required in try mode (AXONFLOW_TRY=1)" + raise TypeError(msg) + else: + # Support AXONFLOW_AGENT_URL env var for backwards compatibility + resolved_endpoint = endpoint or os.environ.get("AXONFLOW_AGENT_URL") or "" + if not resolved_endpoint: + msg = "endpoint is required (or set AXONFLOW_AGENT_URL environment variable)" + raise TypeError(msg) if isinstance(mode, str): mode = Mode(mode) diff --git a/axonflow/community.py b/axonflow/community.py new file mode 100644 index 0000000..71ed53a --- /dev/null +++ b/axonflow/community.py @@ -0,0 +1,29 @@ +"""Community SaaS registration helper for try.getaxonflow.com.""" + +from __future__ import annotations + +from typing import Any + +import httpx + +TRY_ENDPOINT = "https://try.getaxonflow.com" + + +def register_try(label: str = "", endpoint: str = TRY_ENDPOINT) -> dict[str, Any]: + """Register for a free evaluation tenant on try.getaxonflow.com. + + Returns dict with keys: tenant_id, secret, secret_prefix, expires_at, endpoint, note. + Store the secret securely — it is shown only once. + + Args: + label: Optional human-readable name for the registration. + endpoint: Override the default endpoint (for local testing). + """ + response = httpx.post( + f"{endpoint}/api/v1/register", + json={"label": label} if label else {}, + timeout=10.0, + ) + response.raise_for_status() + data: dict[str, Any] = response.json() + return data diff --git a/axonflow/telemetry.py b/axonflow/telemetry.py index 0be8137..552c19b 100644 --- a/axonflow/telemetry.py +++ b/axonflow/telemetry.py @@ -87,6 +87,7 @@ def _classify_endpoint(url: str | None) -> str: # noqa: PLR0911 """Classify the configured AxonFlow endpoint for analytics (#1525). Returns one of: + ``"community-saas"`` — try.getaxonflow.com shared evaluation server ``"localhost"`` — localhost, 127.0.0.1, ::1, 0.0.0.0, ``*.localhost`` ``"private_network"`` — RFC1918 ranges, link-local, ``*.local``, ``*.internal``, ``*.lan``, ``*.intranet`` @@ -95,6 +96,8 @@ def _classify_endpoint(url: str | None) -> str: # noqa: PLR0911 The raw URL is never sent — only the classification. See issue #1525. """ + if os.environ.get("AXONFLOW_TRY") == "1": + return "community-saas" if not url: return "unknown" try: