Files
cleveragents-core/robot/helper_advanced_context_strategies.py
T
HAL9000 9e3bf30bca
CI / lint (pull_request) Successful in 1m2s
CI / helm (pull_request) Successful in 1m2s
CI / build (pull_request) Successful in 1m10s
CI / typecheck (pull_request) Successful in 1m19s
CI / security (pull_request) Successful in 1m20s
CI / quality (pull_request) Successful in 1m39s
CI / push-validation (pull_request) Successful in 44s
CI / unit_tests (pull_request) Successful in 10m27s
CI / docker (pull_request) Successful in 2m48s
CI / integration_tests (pull_request) Successful in 17m20s
CI / coverage (pull_request) Successful in 22m15s
CI / status-check (pull_request) Successful in 4s
fix(tests): resolve AmbiguousStep conflict and robot helper import path
Three issues causing CI failures in advanced-context-strategies tests:

1. AmbiguousStep: `@then("the strategy should be {strategy_type}")` in
   advanced_context_strategies_steps.py conflicted with the existing
   `@then('the strategy should be "{expected_strategy}"')` in
   plan_merge_strategy_steps.py:122. Renamed to
   `@then("the loaded strategy type should be {strategy_type}")` and
   updated all four matching lines in the feature file.

2. Wrong fragment count assertion: scenario "Semantic search strategy
   ranks by embedding similarity" expected 3 fragments but
   SemanticEmbeddingStrategy (word-overlap Jaccard, min_similarity=0.05)
   correctly filters "File input output handler" (0 overlap with
   "database connection"). Fixed assertion from 3 to 2.

3. Robot helper import failure: `features.mocks` is not importable when
   Robot Framework imports the library because it adds robot/ to
   sys.path but not the project root. Added explicit project-root
   sys.path.insert before the features.mocks import (same pattern as
   helper_lsp_stub.py), with # noqa: E402 on the post-path imports.

ISSUES CLOSED: #7574
2026-06-06 05:52:06 -04:00

218 lines
6.5 KiB
Python

"""Helper functions for advanced context strategies Robot Framework tests."""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Any
# Robot Framework adds the library file's directory (robot/) to sys.path, but
# features.mocks lives at the project root. Insert the project root so the
# import below resolves correctly regardless of invocation context.
_PROJECT_ROOT = str(Path(__file__).resolve().parents[1])
if _PROJECT_ROOT not in sys.path:
sys.path.insert(0, _PROJECT_ROOT)
from features.mocks.advanced_context_strategies_mocks import ( # noqa: E402
AdaptiveContextSelector,
ContextFusionStrategy,
RelevanceScoringStrategy,
)
from cleveragents.application.services.context_strategies import ( # noqa: E402
BreadthDepthNavigatorStrategy,
SemanticEmbeddingStrategy,
)
from cleveragents.domain.models.core.context_fragment import ( # noqa: E402
ContextBudget,
ContextFragment,
FragmentProvenance,
)
# Default provenance used for test fragments (no real resource needed).
_TEST_PROVENANCE = FragmentProvenance(resource_uri="test://fixture")
def create_semantic_search_strategy_impl() -> SemanticEmbeddingStrategy:
"""Create a semantic search strategy with FakeEmbeddings."""
return SemanticEmbeddingStrategy(min_similarity=0.05)
def create_relevance_scoring_strategy_impl() -> RelevanceScoringStrategy:
"""Create a relevance scoring strategy."""
return RelevanceScoringStrategy()
def create_adaptive_selector_impl() -> AdaptiveContextSelector:
"""Create an adaptive context strategy selector."""
return AdaptiveContextSelector()
def create_context_fusion_strategy_impl(
strategies: list[str],
) -> ContextFusionStrategy:
"""Create a context fusion strategy."""
return ContextFusionStrategy(strategies)
def create_test_fragments_impl(args: list[str]) -> list[ContextFragment]:
"""Create test context fragments from arguments."""
fragments: list[ContextFragment] = []
i = 0
while i < len(args):
if i + 4 < len(args):
uko_node = args[i]
content = args[i + 1]
score = float(args[i + 2])
tokens = int(args[i + 3])
depth = int(args[i + 4])
frag = ContextFragment(
uko_node=uko_node,
content=content,
relevance_score=score,
token_count=tokens,
detail_depth=depth,
provenance=_TEST_PROVENANCE,
)
fragments.append(frag)
i += 5
else:
break
return fragments
def create_context_budget_impl(max_tokens: int, reserved_tokens: int) -> ContextBudget:
"""Create a context budget."""
return ContextBudget(
max_tokens=int(max_tokens),
reserved_tokens=int(reserved_tokens),
)
def search_with_query_impl(
strategy: SemanticEmbeddingStrategy,
query: str,
fragments: list[ContextFragment],
budget: ContextBudget,
) -> list[ContextFragment]:
"""Search with a query."""
strategy.set_query(query)
return strategy.assemble(fragments, budget)
def assemble_with_strategy_impl(
strategy: Any,
fragments: list[ContextFragment],
budget: ContextBudget,
) -> list[ContextFragment]:
"""Assemble with a strategy."""
return strategy.assemble(fragments, budget)
def select_strategy_for_query_impl(
selector: AdaptiveContextSelector, query: str
) -> str:
"""Select strategy for a query."""
request = {"query": query}
strategy_name, _ = selector.select_strategy(request)
return strategy_name
def select_strategy_without_query_impl(selector: AdaptiveContextSelector) -> str:
"""Select strategy without query."""
request = {}
strategy_name, _ = selector.select_strategy(request)
return strategy_name
def select_strategy_with_invalid_request_impl(
selector: AdaptiveContextSelector,
) -> str:
"""Select strategy with invalid request."""
request = {"invalid": "data"}
strategy_name, _ = selector.select_strategy(request)
return strategy_name
def fuse_with_query_impl(
fusion: ContextFusionStrategy,
query: str,
fragments: list[ContextFragment],
budget: ContextBudget,
) -> list[ContextFragment]:
"""Fuse strategies with a query."""
return fusion.assemble(fragments, budget, query)
def load_strategy_from_yaml_impl(config: dict[str, Any]) -> Any:
"""Load strategy from YAML configuration."""
strategy_type = config.get("strategy")
if strategy_type == "semantic-embedding":
min_sim = config.get("min_similarity", 0.05)
return SemanticEmbeddingStrategy(min_similarity=min_sim)
elif strategy_type == "relevance-scoring":
return RelevanceScoringStrategy()
elif strategy_type == "adaptive-selector":
return AdaptiveContextSelector()
elif strategy_type == "context-fusion":
strategies = config.get("strategies", [])
return ContextFusionStrategy(strategies)
else:
raise ValueError(f"Unknown strategy type: {strategy_type!r}")
def create_context_assembler_with_advanced_strategies_impl() -> dict[str, Any]:
"""Create a ContextAssembler with advanced strategies."""
return {
"strategies": [
SemanticEmbeddingStrategy(),
RelevanceScoringStrategy(),
BreadthDepthNavigatorStrategy(),
]
}
def create_context_assembler_with_multiple_strategies_impl() -> dict[str, Any]:
"""Create a ContextAssembler with multiple strategies."""
return {
"strategies": [
SemanticEmbeddingStrategy(),
RelevanceScoringStrategy(),
]
}
def assemble_context_with_query_impl(
assembler: dict[str, Any],
query: str,
fragments: list[ContextFragment],
budget: ContextBudget,
) -> list[ContextFragment]:
"""Assemble context with a query."""
request = {"query": query}
best_strategy = None
best_confidence = 0.0
for strategy in assembler["strategies"]:
confidence = strategy.can_handle(request)
if confidence > best_confidence:
best_confidence = confidence
best_strategy = strategy
if best_strategy is None:
best_strategy = assembler["strategies"][0]
if hasattr(best_strategy, "set_query"):
best_strategy.set_query(query)
return best_strategy.assemble(fragments, budget)
def get_uko_nodes_impl(fragments: list[ContextFragment]) -> list[str]:
"""Extract uko_node values from fragments."""
return [f.uko_node for f in fragments]