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
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
218 lines
6.5 KiB
Python
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]
|