Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import asyncio
import logging
import os
import uuid
from datetime import UTC, datetime
from typing import Any, override
Expand All @@ -25,9 +26,6 @@
TaskStatusUpdateEvent,
TextPart,
)
from langchain_core.runnables import RunnableConfig
from pydantic import BaseModel

from kagent.core.a2a import (
KAGENT_HITL_DECISION_TYPE_DENY,
TaskResultAggregator,
Expand All @@ -41,6 +39,9 @@
clear_kagent_span_attributes,
set_kagent_span_attributes,
)
from langchain_core.runnables import RunnableConfig
from pydantic import BaseModel, Field

from langgraph.graph.state import CompiledStateGraph
from langgraph.types import Command

Expand All @@ -59,6 +60,14 @@ class LangGraphAgentExecutorConfig(BaseModel):
# Whether to stream intermediate results
enable_streaming: bool = True

# Maximum number of steps before LangGraph raises a recursion error.
# Configurable via LANGGRAPH_RECURSION_LIMIT env var. Default is 25 (LangGraph's default).
recursion_limit: int = Field(
default_factory=lambda: int(os.getenv("LANGGRAPH_RECURSION_LIMIT", "25")),
gt=0,
description="Maximum number of steps before LangGraph raises a recursion error",
)


class LangGraphAgentExecutor(AgentExecutor):
"""An AgentExecutor that runs LangGraph workflows against A2A requests.
Expand Down Expand Up @@ -97,6 +106,7 @@ def _create_graph_config(self, context: RequestContext) -> RunnableConfig:
"thread_id": session_id,
"app_name": self.app_name,
},
"recursion_limit": self._config.recursion_limit,
"project_name": self.app_name,
"run_name": "kagent-langgraph-exec",
"tags": [
Expand Down Expand Up @@ -322,6 +332,7 @@ async def _handle_resume(
"thread_id": thread_id, # Use thread from interrupted task!
"app_name": self.app_name,
},
"recursion_limit": self._config.recursion_limit,
"project_name": self.app_name,
"run_name": "kagent-langgraph-resume",
"tags": [
Expand Down
Empty file.
58 changes: 58 additions & 0 deletions python/packages/kagent-langgraph/tests/test_executor_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Tests for LangGraphAgentExecutorConfig."""

import os
from unittest.mock import patch

import pytest


def test_recursion_limit_default():
"""Test that default recursion_limit is 25 (LangGraph's default)."""
from kagent.langgraph._executor import LangGraphAgentExecutorConfig

with patch.dict(os.environ, {}, clear=False):
os.environ.pop("LANGGRAPH_RECURSION_LIMIT", None)
config = LangGraphAgentExecutorConfig()
assert config.recursion_limit == 25


def test_recursion_limit_from_env_var():
"""Test that LANGGRAPH_RECURSION_LIMIT env var is picked up at instance creation."""
import kagent.langgraph._executor as executor_mod

with patch.dict(os.environ, {"LANGGRAPH_RECURSION_LIMIT": "50"}):
config = executor_mod.LangGraphAgentExecutorConfig()
assert config.recursion_limit == 50


def test_recursion_limit_explicit_override():
"""Test that explicit config value overrides env var default."""
from kagent.langgraph._executor import LangGraphAgentExecutorConfig

config = LangGraphAgentExecutorConfig(recursion_limit=100)
assert config.recursion_limit == 100
Comment on lines +28 to +33
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite is missing coverage for invalid environment variable values. The current implementation will raise a ValueError if someone sets LANGGRAPH_RECURSION_LIMIT to a non-numeric value (e.g., "abc"), but this error case is not tested.

Consider adding a test case that verifies the behavior when the environment variable contains an invalid value, to ensure it either fails gracefully with a helpful error message or falls back to a sensible default.

Copilot uses AI. Check for mistakes.


def test_recursion_limit_rejects_zero():
"""Test that recursion_limit=0 is rejected by gt=0 validation."""
from kagent.langgraph._executor import LangGraphAgentExecutorConfig

with pytest.raises(Exception):
LangGraphAgentExecutorConfig(recursion_limit=0)


def test_recursion_limit_rejects_negative():
"""Test that negative recursion_limit is rejected by gt=0 validation."""
from kagent.langgraph._executor import LangGraphAgentExecutorConfig

with pytest.raises(Exception):
LangGraphAgentExecutorConfig(recursion_limit=-5)


def test_recursion_limit_invalid_env_var():
"""Test that a non-numeric LANGGRAPH_RECURSION_LIMIT env var raises an error."""
import kagent.langgraph._executor as executor_mod

with patch.dict(os.environ, {"LANGGRAPH_RECURSION_LIMIT": "abc"}):
with pytest.raises((ValueError, Exception)):
executor_mod.LangGraphAgentExecutorConfig()
Loading