diff --git a/features/steps/semantic_context_search_steps.py b/features/steps/semantic_context_search_steps.py index 3e48d2e73..c6fe8480b 100644 --- a/features/steps/semantic_context_search_steps.py +++ b/features/steps/semantic_context_search_steps.py @@ -2,15 +2,17 @@ from __future__ import annotations -from behave import given, when, then +from behave import given, then, when + from cleveragents.application.services.embedding_provider import ( MockEmbeddingProvider, SimpleWordEmbeddingProvider, cosine_similarity, ) +from cleveragents.domain.models.acms.crp import FragmentProvenance from cleveragents.domain.models.core.context_fragment import ( - ContextFragment, ContextBudget, + ContextFragment, ) @@ -36,7 +38,9 @@ def step_embed_text(context, text): @then("the embedding should have {dim:d} dimensions") def step_check_embedding_dimension(context, dim): """Verify embedding dimension.""" - assert len(context.embedding) == dim, f"Expected {dim} dimensions, got {len(context.embedding)}" + assert len(context.embedding) == dim, ( + f"Expected {dim} dimensions, got {len(context.embedding)}" + ) @then("the embedding should be a valid vector") @@ -63,7 +67,9 @@ def step_compute_similarity(context): @then("the similarity should be between -1 and 1") def step_check_similarity_range(context): """Verify similarity is in valid range.""" - assert -1 <= context.similarity <= 1, f"Similarity {context.similarity} out of range" + assert -1 <= context.similarity <= 1, ( + f"Similarity {context.similarity} out of range" + ) @given("I have context fragments with content:") @@ -73,10 +79,12 @@ def step_have_fragments_with_content(context): for row in context.table: content = row["content"] embedding = context.embedding_provider.embed(content) - context.fragments.append({ - "content": content, - "embedding": embedding, - }) + context.fragments.append( + { + "content": content, + "embedding": embedding, + } + ) @given("I have a query {query}") @@ -93,7 +101,7 @@ def step_rank_fragments(context): for frag in context.fragments: sim = cosine_similarity(context.query_embedding, frag["embedding"]) similarities.append((frag, sim)) - + similarities.sort(key=lambda x: x[1], reverse=True) context.ranked_fragments = similarities @@ -102,14 +110,12 @@ def step_rank_fragments(context): def step_check_python_ranking(context): """Verify Python fragments rank higher.""" python_sims = [ - sim for frag, sim in context.ranked_fragments - if "Python" in frag["content"] + sim for frag, sim in context.ranked_fragments if "Python" in frag["content"] ] js_sims = [ - sim for frag, sim in context.ranked_fragments - if "JavaScript" in frag["content"] + sim for frag, sim in context.ranked_fragments if "JavaScript" in frag["content"] ] - + if python_sims and js_sims: assert min(python_sims) >= max(js_sims), "Python fragments should rank higher" @@ -124,7 +130,8 @@ def step_have_threshold(context, threshold): def step_filter_by_threshold(context): """Filter fragments by threshold.""" context.filtered_fragments = [ - (frag, sim) for frag, sim in context.ranked_fragments + (frag, sim) + for frag, sim in context.ranked_fragments if sim >= context.threshold ] @@ -132,8 +139,10 @@ def step_filter_by_threshold(context): @then("only semantically similar fragments should be included") def step_check_filtered_fragments(context): """Verify filtered fragments meet threshold.""" - for frag, sim in context.filtered_fragments: - assert sim >= context.threshold, f"Fragment similarity {sim} below threshold {context.threshold}" + for _frag, sim in context.filtered_fragments: + assert sim >= context.threshold, ( + f"Fragment similarity {sim} below threshold {context.threshold}" + ) @given("I have a semantic context strategy") @@ -154,7 +163,7 @@ def step_have_context_fragments(context): relevance_score=0.5, detail_depth=1, tier="hot", - created_at=None, + provenance=FragmentProvenance(resource_uri=row["uko_node"]), ) context.fragments.append(frag) @@ -170,21 +179,21 @@ def step_assemble_context(context, query): """Assemble context with query.""" context.query = query query_embedding = context.embedding_provider.embed(query) - + # Score fragments by similarity scored = [] for frag in context.fragments: frag_embedding = context.embedding_provider.embed(frag.content) sim = cosine_similarity(query_embedding, frag_embedding) scored.append((frag, sim)) - + # Sort by similarity scored.sort(key=lambda x: x[1], reverse=True) - + # Pack within budget context.selected_fragments = [] total_tokens = 0 - for frag, sim in scored: + for frag, _sim in scored: if total_tokens + frag.token_count <= context.budget.max_tokens: context.selected_fragments.append(frag) total_tokens += frag.token_count diff --git a/src/cleveragents/application/services/embedding_provider.py b/src/cleveragents/application/services/embedding_provider.py index 7060d4aad..6b65aefba 100644 --- a/src/cleveragents/application/services/embedding_provider.py +++ b/src/cleveragents/application/services/embedding_provider.py @@ -10,7 +10,7 @@ from __future__ import annotations import logging from abc import ABC, abstractmethod -from typing import Sequence +from collections.abc import Sequence logger = logging.getLogger(__name__) @@ -193,7 +193,7 @@ def cosine_similarity(vec_a: Sequence[float], vec_b: Sequence[float]) -> float: if len(vec_a) != len(vec_b): raise ValueError("Vectors must have the same dimension") - dot_product = sum(a * b for a, b in zip(vec_a, vec_b)) + dot_product = sum(a * b for a, b in zip(vec_a, vec_b, strict=False)) mag_a = sum(a * a for a in vec_a) ** 0.5 mag_b = sum(b * b for b in vec_b) ** 0.5 diff --git a/src/cleveragents/cli/main.py b/src/cleveragents/cli/main.py index 5b804379f..0372d44e4 100644 --- a/src/cleveragents/cli/main.py +++ b/src/cleveragents/cli/main.py @@ -735,6 +735,7 @@ def main(args: list[str] | None = None) -> int: "config", # Configuration management "session", # Session management "tool", # Tool registry management + "plugin", # Plugin management "validation", # Validation management "auto-debug", # Auto-debug commands "automation-profile", # Automation profile management