From 31326fd61f7c872a6be496aee5a2d68b9b03775c Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 18 Dec 2025 17:35:28 -0500 Subject: [PATCH] feat: Added deprecation notices for LangGraph v0.3.x support --- .../codon-instrumentation-langgraph/AGENTS.md | 7 +++ .../codon-instrumentation-langgraph/README.md | 12 +++++ .../instrumentation/langgraph/__init__.py | 40 +++++++++++++++++ .../pyproject.toml | 2 +- sdk/AGENTS.md | 1 + .../langgraph/test_deprecation_warning.py | 44 +++++++++++++++++++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 sdk/test/instrumentation/langgraph/test_deprecation_warning.py diff --git a/instrumentation-packages/codon-instrumentation-langgraph/AGENTS.md b/instrumentation-packages/codon-instrumentation-langgraph/AGENTS.md index a312b97..76581b0 100644 --- a/instrumentation-packages/codon-instrumentation-langgraph/AGENTS.md +++ b/instrumentation-packages/codon-instrumentation-langgraph/AGENTS.md @@ -4,6 +4,13 @@ This document explains how to convert an existing LangGraph `StateGraph` into a --- +## Deprecation Notice (LangGraph 0.3.x) +- `codon-instrumentation-langgraph` `0.1.0a5` is the last release that supports LangGraph 0.3.x. +- Starting with `0.2.0a0`, the adapter targets LangChain/LangGraph v1.x only. +- A `DeprecationWarning` is emitted when LangGraph < 1.0 is detected (set `CODON_LANGGRAPH_DEPRECATION_SILENCE=1` to suppress). + +--- + ## Why Wrap a LangGraph Graph? - **Zero instrumentation boilerplate:** every LangGraph node is auto-wrapped with `track_node`, producing OpenTelemetry spans without manual decorators. - **Stable identifiers:** nodes become `NodeSpec`s with deterministic SHA-256 IDs, and the overall graph gets a logic ID for caching, retries, and provenance. diff --git a/instrumentation-packages/codon-instrumentation-langgraph/README.md b/instrumentation-packages/codon-instrumentation-langgraph/README.md index 6a58720..0f259e4 100644 --- a/instrumentation-packages/codon-instrumentation-langgraph/README.md +++ b/instrumentation-packages/codon-instrumentation-langgraph/README.md @@ -2,6 +2,18 @@ If you're already using LangGraph, the Codon SDK provides seamless integration through the `LangGraphWorkloadAdapter`. This allows you to wrap your existing StateGraphs with minimal code changes while gaining comprehensive telemetry and observability. +## Deprecation Notice (LangGraph 0.3.x) + +Support for LangGraph 0.3.x is deprecated and will be removed after the `0.1.0a5` release of `codon-instrumentation-langgraph`. If you need to stay on LangGraph 0.3.x, pin this package at `<=0.1.0a5`. Starting with `0.2.0a0`, the adapter will support only LangChain/LangGraph v1.x. + +### python-warnings + +When running with LangGraph 0.3.x you will see a `DeprecationWarning` explaining the cutoff. To silence the warning, set: + +``` +CODON_LANGGRAPH_DEPRECATION_SILENCE=1 +``` + ## Understanding State Graph vs Compiled Graph LangGraph has two distinct graph representations: diff --git a/instrumentation-packages/codon-instrumentation-langgraph/codon/instrumentation/langgraph/__init__.py b/instrumentation-packages/codon-instrumentation-langgraph/codon/instrumentation/langgraph/__init__.py index a5854a0..b9eefee 100644 --- a/instrumentation-packages/codon-instrumentation-langgraph/codon/instrumentation/langgraph/__init__.py +++ b/instrumentation-packages/codon-instrumentation-langgraph/codon/instrumentation/langgraph/__init__.py @@ -15,7 +15,10 @@ import inspect import json import os +import re import time +import warnings +from importlib import metadata as importlib_metadata from abc import ABC, abstractmethod from contextvars import ContextVar from functools import wraps @@ -85,6 +88,41 @@ def current_invocation() -> Optional[NodeTelemetryPayload]: return _ACTIVE_INVOCATION.get() +def _is_truthy(value: Optional[str]) -> bool: + return str(value or "").strip().lower() in {"1", "true", "yes", "y", "on"} + + +def _parse_major(version: str) -> Optional[int]: + match = re.match(r"(\d+)", version) + if not match: + return None + try: + return int(match.group(1)) + except ValueError: + return None + + +def _maybe_warn_deprecated_langgraph() -> None: + if _is_truthy(os.getenv("CODON_LANGGRAPH_DEPRECATION_SILENCE")): + return + try: + version = importlib_metadata.version("langgraph") + except importlib_metadata.PackageNotFoundError: + return + except Exception: + return + major = _parse_major(version) + if major is not None and major < 1: + warnings.warn( + "LangGraph <1.0 support is deprecated and will be removed after " + "codon-instrumentation-langgraph 0.1.0a5. Upgrade to LangGraph " + "v1.x or pin codon-instrumentation-langgraph<=0.1.0a5. Set " + "CODON_LANGGRAPH_DEPRECATION_SILENCE=1 to suppress this warning.", + DeprecationWarning, + stacklevel=2, + ) + + def _safe_repr(value: Any, *, max_length: int = 2048) -> str: try: rendered = repr(value) @@ -483,3 +521,5 @@ def wrapper(*args, **kwargs): NodeOverride, ) from .callbacks import LangGraphTelemetryCallback # noqa: E402 # isort: skip + +_maybe_warn_deprecated_langgraph() diff --git a/instrumentation-packages/codon-instrumentation-langgraph/pyproject.toml b/instrumentation-packages/codon-instrumentation-langgraph/pyproject.toml index 8630ee2..0b5fe05 100644 --- a/instrumentation-packages/codon-instrumentation-langgraph/pyproject.toml +++ b/instrumentation-packages/codon-instrumentation-langgraph/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "codon-instrumentation-langgraph" -version = "0.1.0a4" +version = "0.1.0a5" license = {text = "Apache-2.0"} authors = [ { name="Codon, Inc.", email="martin@codonops.ai" }, diff --git a/sdk/AGENTS.md b/sdk/AGENTS.md index 4f9b812..f136696 100644 --- a/sdk/AGENTS.md +++ b/sdk/AGENTS.md @@ -10,6 +10,7 @@ This scaffold explains the agent-facing contracts provided by `codon_sdk`. Flesh - **Instrumentation mixins:** Framework packages ship their own mixins (see `docs/guides/workload-mixin-guidelines.md`) to expose `from_*` constructors while keeping the core SDK agnostic. - **Reference implementation:** Each instrumentation package should define mixins inside its own namespace (e.g., `codon.instrumentation.langgraph.LangGraphWorkloadMixin`). - **Adapters:** `LangGraphWorkloadAdapter.from_langgraph(...)` demonstrates how to wrap existing LangGraph graphs with `CodonWorkload` automatically; use it as a model for future adapters. +- **LangGraph compatibility:** `codon-instrumentation-langgraph` `0.1.0a5` is the final release that supports LangGraph 0.3.x. Starting in `0.2.0a0`, the adapter targets LangChain/LangGraph v1.x only and will emit a `DeprecationWarning` on older versions. ## CodonWorkload (Opinionated Implementation) - **Module:** `codon_sdk.agents.codon_workload` diff --git a/sdk/test/instrumentation/langgraph/test_deprecation_warning.py b/sdk/test/instrumentation/langgraph/test_deprecation_warning.py new file mode 100644 index 0000000..536dbf6 --- /dev/null +++ b/sdk/test/instrumentation/langgraph/test_deprecation_warning.py @@ -0,0 +1,44 @@ +import importlib +import warnings + +from importlib import metadata as importlib_metadata + + +def _reload_langgraph(monkeypatch, version_value, *, silence=False): + monkeypatch.setattr(importlib_metadata, "version", lambda name: version_value) + if silence: + monkeypatch.setenv("CODON_LANGGRAPH_DEPRECATION_SILENCE", "1") + else: + monkeypatch.delenv("CODON_LANGGRAPH_DEPRECATION_SILENCE", raising=False) + module = importlib.import_module("codon.instrumentation.langgraph") + return importlib.reload(module) + + +def _has_deprecation_warning(captured): + for warning in captured: + if issubclass(warning.category, DeprecationWarning) and "LangGraph <1.0" in str( + warning.message + ): + return True + return False + + +def test_deprecation_warning_emitted(monkeypatch): + with warnings.catch_warnings(record=True) as captured: + warnings.simplefilter("always", DeprecationWarning) + _reload_langgraph(monkeypatch, "0.3.2") + assert _has_deprecation_warning(captured) + + +def test_deprecation_warning_suppressed(monkeypatch): + with warnings.catch_warnings(record=True) as captured: + warnings.simplefilter("always", DeprecationWarning) + _reload_langgraph(monkeypatch, "0.3.2", silence=True) + assert not _has_deprecation_warning(captured) + + +def test_no_warning_for_v1(monkeypatch): + with warnings.catch_warnings(record=True) as captured: + warnings.simplefilter("always", DeprecationWarning) + _reload_langgraph(monkeypatch, "1.0.0") + assert not _has_deprecation_warning(captured)