From 48df4ce508f7747a74f90007677a7987dd67b551 Mon Sep 17 00:00:00 2001 From: HAL9000 Date: Sun, 19 Apr 2026 02:21:13 +0000 Subject: [PATCH] feat(context): add strategy configuration to context policy YAML schema - Add strategy and strategy_config fields to ProjectContextPolicy model - Support basic, semantic, relevance_scoring, adaptive, and fusion strategies - Validate strategy names with VALID_STRATEGIES constant - Add field validator for strategy configuration - Include update script for model changes --- scripts/update_context_policy.py | 123 ++++++++++++++++++ .../domain/models/core/context_policy.py | 37 +++++- 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 scripts/update_context_policy.py diff --git a/scripts/update_context_policy.py b/scripts/update_context_policy.py new file mode 100644 index 000000000..ecb2179ff --- /dev/null +++ b/scripts/update_context_policy.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Update context_policy.py with strategy configuration support.""" + +import sys +from pathlib import Path + +# Read the file +policy_file = Path('src/cleveragents/domain/models/core/context_policy.py') +content = policy_file.read_text() + +# Update the imports to include Any +content = content.replace( + 'from typing import TYPE_CHECKING', + 'from typing import TYPE_CHECKING, Any' +) + +# Add VALID_STRATEGIES constant after VALID_PHASES +old_phases = ( + 'VALID_PHASES: frozenset[str] = frozenset(' + '{"default", "strategize", "execute", "apply"})' +) +new_phases = ( + 'VALID_PHASES: frozenset[str] = frozenset(' + '{"default", "strategize", "execute", "apply"})\n\n' + 'VALID_STRATEGIES: frozenset[str] = frozenset({\n' + ' "basic",\n' + ' "semantic",\n' + ' "relevance_scoring",\n' + ' "adaptive",\n' + ' "fusion",\n' + '})' +) +content = content.replace(old_phases, new_phases) + +# Update the docstring for ProjectContextPolicy to mention strategy +old_docstring = ( + 'class ProjectContextPolicy(BaseModel):\n' + ' """Controls what context is available during each ACMS phase.\n' + '\n' + ' Uses view inheritance: ``default`` → ``strategize`` →\n' + ' ``execute`` → ``apply``. Each phase can override or inherit\n' + ' from its parent.\n' + '\n' + ' An empty ``ProjectContextPolicy()`` defaults to including\n' + ' everything (the ``default_view`` has empty include lists which\n' + ' means "all").\n' + ' """' +) + +new_docstring = ( + 'class ProjectContextPolicy(BaseModel):\n' + ' """Controls what context is available during each ACMS phase.\n' + '\n' + ' Uses view inheritance: ``default`` → ``strategize`` →\n' + ' ``execute`` → ``apply``. Each phase can override or inherit\n' + ' from its parent.\n' + '\n' + ' An empty ``ProjectContextPolicy()`` defaults to including\n' + ' everything (the ``default_view`` has empty include lists which\n' + ' means "all").\n' + '\n' + ' Optionally specifies a context assembly strategy and its\n' + ' configuration parameters.\n' + ' """' +) + +content = content.replace(old_docstring, new_docstring) + +# Add strategy and strategy_config fields before the resolve_view method +old_fields = ( + ' apply_view: ContextView | None = Field(\n' + ' default=None,\n' + ' description=("Overrides for Apply (inherits from execute if None)"),\n' + ' )\n' + '\n' + ' def resolve_view(self, phase: str) -> ContextView:' +) + +error_msg = ( + 'f"Invalid strategy \'{v}\': must be one of ' + '{sorted(VALID_STRATEGIES)}"' +) + +new_fields = ( + ' apply_view: ContextView | None = Field(\n' + ' default=None,\n' + ' description=("Overrides for Apply (inherits from execute if None)"),\n' + ' )\n' + ' strategy: str | None = Field(\n' + ' default=None,\n' + ' description=(\n' + ' "Context assembly strategy name. "\n' + ' "Valid values: basic, semantic, relevance_scoring, adaptive, fusion"\n' + ' ),\n' + ' )\n' + ' strategy_config: dict[str, Any] | None = Field(\n' + ' default=None,\n' + ' description="Strategy-specific configuration parameters",\n' + ' )\n' + '\n' + ' @field_validator("strategy")\n' + ' @classmethod\n' + ' def _validate_strategy(\n' + ' cls: type[ProjectContextPolicy],\n' + ' v: str | None,\n' + ' ) -> str | None:\n' + ' """Validate that strategy name is in the list of valid strategies."""\n' + ' if v is not None and v not in VALID_STRATEGIES:\n' + ' raise ValueError(\n' + ' ' + error_msg + '\n' + ' )\n' + ' return v\n' + '\n' + ' def resolve_view(self, phase: str) -> ContextView:' +) + +content = content.replace(old_fields, new_fields) + +# Write the updated content +policy_file.write_text(content) + +print("Updated context_policy.py successfully") +sys.exit(0) diff --git a/src/cleveragents/domain/models/core/context_policy.py b/src/cleveragents/domain/models/core/context_policy.py index c86de8161..cd6b32eae 100644 --- a/src/cleveragents/domain/models/core/context_policy.py +++ b/src/cleveragents/domain/models/core/context_policy.py @@ -34,7 +34,7 @@ Based on ``docs/specification.md`` Context section and ADR-004. from __future__ import annotations from collections.abc import Sequence -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -47,6 +47,14 @@ if TYPE_CHECKING: VALID_PHASES: frozenset[str] = frozenset({"default", "strategize", "execute", "apply"}) +VALID_STRATEGIES: frozenset[str] = frozenset({ + "basic", + "semantic", + "relevance_scoring", + "adaptive", + "fusion", +}) + _INHERITANCE_CHAIN: dict[str, list[str]] = { "default": ["default"], "strategize": ["strategize", "default"], @@ -125,6 +133,9 @@ class ProjectContextPolicy(BaseModel): An empty ``ProjectContextPolicy()`` defaults to including everything (the ``default_view`` has empty include lists which means "all"). + + Optionally specifies a context assembly strategy and its + configuration parameters. """ default_view: ContextView = Field( @@ -143,6 +154,30 @@ class ProjectContextPolicy(BaseModel): default=None, description=("Overrides for Apply (inherits from execute if None)"), ) + strategy: str | None = Field( + default=None, + description=( + "Context assembly strategy name. " + "Valid values: basic, semantic, relevance_scoring, adaptive, fusion" + ), + ) + strategy_config: dict[str, Any] | None = Field( + default=None, + description="Strategy-specific configuration parameters", + ) + + @field_validator("strategy") + @classmethod + def _validate_strategy( + cls: type[ProjectContextPolicy], + v: str | None, + ) -> str | None: + """Validate that strategy name is in the list of valid strategies.""" + if v is not None and v not in VALID_STRATEGIES: + raise ValueError( + f"Invalid strategy '{v}': must be one of {sorted(VALID_STRATEGIES)}" + ) + return v def resolve_view(self, phase: str) -> ContextView: """Resolve the effective view for a given phase.