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
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
192 lines
6.9 KiB
Python
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}"
|
|
)
|