Files
cleveragents-core/features/steps/agent_configurable_limits_steps.py
HAL9000 e9e2deb090
CI / lint (pull_request) Successful in 40s
CI / typecheck (pull_request) Successful in 1m8s
CI / quality (pull_request) Successful in 1m0s
CI / security (pull_request) Successful in 1m21s
CI / push-validation (pull_request) Successful in 27s
CI / helm (pull_request) Successful in 30s
CI / build (pull_request) Successful in 37s
CI / unit_tests (pull_request) Successful in 4m10s
CI / docker (pull_request) Successful in 1m43s
CI / integration_tests (pull_request) Successful in 8m36s
CI / coverage (pull_request) Successful in 13m31s
CI / status-check (pull_request) Successful in 3s
refactor(agent): replace hardcoded dependency and context file limits with configurable parameters
author CleverThis <hal9000@cleverthis.com> 1776170939 +0000
committer CleverThis <hal9000@cleverthis.com> 1776170939 +0000

refactor(agent): replace hardcoded dependency and context file limits with configurable parameters

Implemented configurable limits for the agent and graph components:
- ContextAnalysisAgent now accepts max_dependencies: int = 10 with validation (ValueError if <= 0)
- _parse_dependencies uses self.max_dependencies instead of a hard-coded 10
- PlanGenerationGraph now accepts max_context_files: int = 5 with validation (ValueError if <= 0)
- _format_context_summary uses self.max_context_files instead of a hard-coded 5
- Updated class docstrings to reflect new parameters
- Added Behave feature file at features/agent_configurable_limits.feature with 12 scenarios
- Added step definitions at features/steps/agent_configurable_limits_steps.py

ISSUES CLOSED: #9050
2026-06-02 21:16:38 -04:00

192 lines
6.9 KiB
Python

"""Step definitions for agent_configurable_limits.feature.
These steps test the configurable ``max_dependencies`` parameter on
``ContextAnalysisAgent`` and the configurable ``max_context_files`` parameter
on ``PlanGenerationGraph``, covering:
- Default values preserve existing behaviour (10 / 5 respectively)
- Custom values are respected
- Non-positive values raise ``ValueError``
"""
from __future__ import annotations
from typing import Any
from behave import given, then, when
from langchain_community.llms import FakeListLLM
from cleveragents.agents.graphs.context_analysis import ContextAnalysisAgent
from cleveragents.agents.graphs.plan_generation import PlanGenerationGraph
from cleveragents.domain.models.core import Context
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
_DEFAULT_RESPONSES = [
"Dependencies: ['os', 'sys', 'pathlib']",
"Relevance: 0.8 - core module",
"Summary: Generated context overview",
] * 30
def _make_llm() -> FakeListLLM:
return FakeListLLM(responses=list(_DEFAULT_RESPONSES))
def _make_dep_string(count: int) -> str:
"""Return an LLM-style dependency string with *count* items."""
items = ", ".join(f"dep_{i}" for i in range(count))
return f"[{items}]"
def _make_contexts(count: int) -> list[Context]:
"""Return a list of *count* dummy Context objects."""
return [
Context(plan_id=1, path=f"src/file_{i}.py", content=f"content of file {i}")
for i in range(count)
]
# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------
@given("the configurable limits module is importable")
def step_module_importable(context: Any) -> None:
assert ContextAnalysisAgent is not None
assert PlanGenerationGraph is not None
# ---------------------------------------------------------------------------
# ContextAnalysisAgent — default max_dependencies
# ---------------------------------------------------------------------------
@given("a ContextAnalysisAgent with default settings")
def step_create_agent_default(context: Any) -> None:
context.agent = ContextAnalysisAgent(llm=_make_llm())
@given("a ContextAnalysisAgent with max_dependencies set to {limit:d}")
def step_create_agent_custom_max_deps(context: Any, limit: int) -> None:
context.agent = ContextAnalysisAgent(llm=_make_llm(), max_dependencies=limit)
@when("I parse dependencies from a string with {count:d} items")
def step_parse_deps_with_count(context: Any, count: int) -> None:
dep_string = _make_dep_string(count)
context.parsed_deps = context.agent._parse_dependencies(dep_string)
@then("the result should contain exactly {expected:d} dependencies")
def step_check_dep_count(context: Any, expected: int) -> None:
actual = len(context.parsed_deps)
assert actual == expected, (
f"Expected {expected} dependencies, got {actual}: {context.parsed_deps!r}"
)
# ---------------------------------------------------------------------------
# ContextAnalysisAgent — ValueError for invalid max_dependencies
# ---------------------------------------------------------------------------
@when("I create a ContextAnalysisAgent with max_dependencies set to {limit:d}")
def step_create_agent_invalid_max_deps(context: Any, limit: int) -> None:
context.raised_error = None
try:
ContextAnalysisAgent(llm=_make_llm(), max_dependencies=limit)
except ValueError as exc:
context.raised_error = exc
@then("a ValueError should be raised about max_dependencies")
def step_check_value_error_max_deps(context: Any) -> None:
assert context.raised_error is not None, (
"Expected ValueError but no error was raised"
)
assert "max_dependencies" in str(context.raised_error), (
f"Expected 'max_dependencies' in error message, got: {context.raised_error!r}"
)
# ---------------------------------------------------------------------------
# PlanGenerationGraph — default max_context_files
# ---------------------------------------------------------------------------
@given("a PlanGenerationGraph with default settings")
def step_create_graph_default(context: Any) -> None:
context.graph = PlanGenerationGraph(llm=_make_llm())
@given("a PlanGenerationGraph with max_context_files set to {limit:d}")
def step_create_graph_custom_max_ctx(context: Any, limit: int) -> None:
context.graph = PlanGenerationGraph(llm=_make_llm(), max_context_files=limit)
@when("I format a context summary with {count:d} context files")
def step_format_context_summary(context: Any, count: int) -> None:
contexts = _make_contexts(count)
context.summary = context.graph._format_context_summary(contexts)
context.context_count = count
@then("the summary should contain exactly {expected:d} file entries")
def step_check_file_entries(context: Any, expected: int) -> None:
# Each file entry starts with "File: "
file_count = context.summary.count("File: ")
assert file_count == expected, (
f"Expected {expected} 'File:' entries in summary, got {file_count}.\n"
f"Summary:\n{context.summary!r}"
)
@then("the summary should mention {extra:d} more files")
def step_check_more_files_mention(context: Any, extra: int) -> None:
assert f"... and {extra} more files" in context.summary, (
f"Expected '... and {extra} more files' in summary.\n"
f"Summary:\n{context.summary!r}"
)
@then("the summary should not mention more files")
def step_check_no_more_files(context: Any) -> None:
assert "more files" not in context.summary, (
f"Expected no 'more files' mention in summary.\nSummary:\n{context.summary!r}"
)
@then("the summary should be the no-context message")
def step_check_no_context_message(context: Any) -> None:
assert context.summary == "No context files provided", (
f"Expected 'No context files provided', got: {context.summary!r}"
)
# ---------------------------------------------------------------------------
# PlanGenerationGraph — ValueError for invalid max_context_files
# ---------------------------------------------------------------------------
@when("I create a PlanGenerationGraph with max_context_files set to {limit:d}")
def step_create_graph_invalid_max_ctx(context: Any, limit: int) -> None:
context.raised_error = None
try:
PlanGenerationGraph(llm=_make_llm(), max_context_files=limit)
except ValueError as exc:
context.raised_error = exc
@then("a ValueError should be raised about max_context_files")
def step_check_value_error_max_ctx(context: Any) -> None:
assert context.raised_error is not None, (
"Expected ValueError but no error was raised"
)
assert "max_context_files" in str(context.raised_error), (
f"Expected 'max_context_files' in error message, got: {context.raised_error!r}"
)