Skip to content

Commit a464e2b

Browse files
author
Mateusz
committed
WIP
1 parent ef0fbfc commit a464e2b

30 files changed

+2680
-151
lines changed

fix_test_failures_todo.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Test Failures Fix Plan
2+
3+
## Issues Summary
4+
- [ ] 1. Documentation Index Issue - `inline-python-steering.md` not listed in user_guide/index.md
5+
- [ ] 2. Ruff Linting Issues - F821 and RUF005 violations
6+
- [ ] 3. DI Container Violations - CommandExtractionService direct instantiation
7+
- [ ] 4. MyPy Type Checking Issues - ConfiguredRulesPolicy undefined and union attribute
8+
9+
## Implementation Steps
10+
11+
### Phase 1: Fix Overlapping Issues (High Priority)
12+
- [ ] 1.1 Fix undefined `ConfiguredRulesPolicy` in steering.py:160
13+
- [ ] 1.2 Fix mypy union attribute issue in unified_tool_security_handler.py:274
14+
- [ ] 1.3 Fix ruff concatenation issue in unified_security_config.py:86
15+
16+
### Phase 2: Fix DI Container Violations
17+
- [ ] 2.1 Fix CommandExtractionService instantiation in inline_python_steering_handler.py:59
18+
- [ ] 2.2 Fix CommandExtractionService instantiation in inline_python_policy.py:48
19+
20+
### Phase 3: Fix Documentation Issues
21+
- [ ] 3.1 Add missing `inline-python-steering.md` link to user_guide/index.md
22+
23+
### Phase 4: Verification
24+
- [ ] 4.1 Run ruff linting test to verify fixes
25+
- [ ] 4.2 Run mypy validation test to verify fixes
26+
- [ ] 4.3 Run DI container test to verify fixes
27+
- [ ] 4.4 Run documentation structure test to verify fixes
28+
- [ ] 4.5 Run all failing tests together to confirm complete resolution
29+
30+
## Files to Modify
31+
1. `src/core/app/stages/steering.py` - Fix ConfiguredRulesPolicy import
32+
2. `src/core/services/unified_tool_security_handler.py` - Fix union attribute
33+
3. `src/core/domain/configuration/unified_security_config.py` - Fix concatenation
34+
4. `src/core/services/tool_call_handlers/inline_python_steering_handler.py` - Fix DI violation
35+
5. `src/services/steering/policies/inline_python_policy.py` - Fix DI violation
36+
6. `docs/user_guide/index.md` - Add missing documentation link

src/core/app/application_builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,15 @@ def add_default_stages(self) -> ApplicationBuilder:
139139
CoreServicesStage,
140140
InfrastructureStage,
141141
ProcessorStage,
142+
SteeringStage,
142143
)
143144

144145
return (
145146
self.add_stage(InfrastructureStage())
146147
.add_stage(CoreServicesStage())
148+
.add_stage(
149+
SteeringStage()
150+
) # After core services, before backends to ensure handlers are available
147151
.add_stage(BackendStage())
148152
.add_stage(CommandStage())
149153
.add_stage(ProcessorStage())

src/core/app/stages/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .health_check import HealthCheckStage
88
from .infrastructure import InfrastructureStage
99
from .processor import ProcessorStage
10+
from .steering import SteeringStage
1011
from .test_stages import RealBackendTestStage
1112

1213
__all__ = [
@@ -19,5 +20,6 @@
1920
"InfrastructureStage",
2021
"InitializationStage",
2122
"ProcessorStage",
23+
"SteeringStage",
2224
"RealBackendTestStage",
2325
]

src/core/app/stages/application_stages.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .health_check import HealthCheckStage
1313
from .infrastructure import InfrastructureStage
1414
from .processor import ProcessorStage
15+
from .steering import SteeringStage
1516

1617

1718
class DefaultApplicationStages:
@@ -21,6 +22,7 @@ def __init__(self) -> None:
2122
self._stages: tuple[InitializationStage, ...] = (
2223
InfrastructureStage(),
2324
CoreServicesStage(),
25+
SteeringStage(), # After core services, before backends
2426
BackendStage(),
2527
HealthCheckStage(), # After backends so we can monitor their URLs
2628
CommandStage(),

src/core/app/stages/steering.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
"""
2+
Steering services initialization stage.
3+
4+
This stage registers unified steering framework services:
5+
- Session state store
6+
- Steering policies
7+
- Unified steering handler
8+
"""
9+
10+
from __future__ import annotations
11+
12+
import logging
13+
from pathlib import Path
14+
from typing import Any
15+
16+
from src.core.config.app_config import AppConfig
17+
from src.core.di.container import ServiceCollection
18+
from src.core.interfaces.di_interface import IServiceProvider
19+
20+
# Import at module level to avoid undefined name issues
21+
from src.services.steering.policies import ConfiguredRulesPolicy
22+
23+
from .base import InitializationStage
24+
25+
logger = logging.getLogger(__name__)
26+
27+
28+
class SteeringStage(InitializationStage):
29+
"""Stage for registering unified steering framework services."""
30+
31+
@property
32+
def name(self) -> str:
33+
return "steering"
34+
35+
def get_dependencies(self) -> list[str]:
36+
return ["core_services"]
37+
38+
def get_description(self) -> str:
39+
return "Register unified steering framework (session store, policies, handler)"
40+
41+
async def execute(self, services: ServiceCollection, config: AppConfig) -> None:
42+
"""Register steering services."""
43+
if logger.isEnabledFor(logging.INFO):
44+
logger.info("Initializing steering services...")
45+
46+
# Register session state store
47+
self._register_session_state_store(services, config)
48+
49+
# Register policies
50+
self._register_steering_policies(services, config)
51+
52+
# Register unified steering handler
53+
self._register_unified_steering_handler(services, config)
54+
55+
if logger.isEnabledFor(logging.INFO):
56+
logger.info("Steering services initialized successfully")
57+
58+
async def validate(self, services: ServiceCollection, config: AppConfig) -> bool:
59+
"""Validate that steering services can be registered."""
60+
try:
61+
# Check that reactor config exists
62+
if not hasattr(config.session, "tool_call_reactor"):
63+
if logger.isEnabledFor(logging.WARNING):
64+
logger.warning("Config missing tool_call_reactor section")
65+
return False
66+
67+
return True
68+
except ImportError as e:
69+
if logger.isEnabledFor(logging.ERROR):
70+
logger.error(f"Steering services validation failed: {e}")
71+
return False
72+
73+
def _register_session_state_store(
74+
self, services: ServiceCollection, config: AppConfig
75+
) -> None:
76+
"""Register session state store as singleton."""
77+
try:
78+
from src.services.steering import SessionStateStore
79+
80+
reactor_config = config.session.tool_call_reactor
81+
82+
# Use configured settings or legacy-compatible defaults
83+
ttl_seconds = getattr(reactor_config, "steering_session_ttl_seconds", 1800)
84+
max_sessions = getattr(reactor_config, "steering_max_sessions", 1024)
85+
86+
services.add_singleton(
87+
SessionStateStore,
88+
implementation_factory=lambda provider: SessionStateStore(
89+
ttl_seconds=ttl_seconds, max_sessions=max_sessions
90+
),
91+
)
92+
93+
if logger.isEnabledFor(logging.DEBUG):
94+
logger.debug(
95+
"Registered SessionStateStore (ttl=%ds, max=%d)",
96+
ttl_seconds,
97+
max_sessions,
98+
)
99+
except Exception as e:
100+
if logger.isEnabledFor(logging.WARNING):
101+
logger.warning(f"Could not register SessionStateStore: {e}")
102+
103+
def _register_steering_policies(
104+
self, services: ServiceCollection, config: AppConfig
105+
) -> None:
106+
"""Register steering policies as singletons."""
107+
try:
108+
from src.services.steering.policies import (
109+
InlinePythonPolicy,
110+
PytestFullSuitePolicy,
111+
)
112+
113+
reactor_config = config.session.tool_call_reactor
114+
115+
# Register InlinePythonPolicy
116+
services.add_singleton(
117+
InlinePythonPolicy,
118+
implementation_factory=lambda provider: InlinePythonPolicy(
119+
message=getattr(
120+
reactor_config, "inline_python_steering_message", None
121+
),
122+
enabled=getattr(
123+
reactor_config, "inline_python_steering_enabled", True
124+
),
125+
prompt_override_path=Path(
126+
"config/prompts/steering_inline_python.md"
127+
),
128+
),
129+
)
130+
131+
# Register PytestFullSuitePolicy
132+
from src.services.steering import SessionStateStore
133+
134+
services.add_singleton(
135+
PytestFullSuitePolicy,
136+
implementation_factory=lambda provider: PytestFullSuitePolicy(
137+
session_store=provider.get_required_service(SessionStateStore),
138+
message=reactor_config.pytest_full_suite_steering_message,
139+
enabled=reactor_config.pytest_full_suite_steering_enabled,
140+
prompt_override_path=Path(
141+
"config/prompts/steering_pytest_full_suite.md"
142+
),
143+
),
144+
)
145+
146+
# Register ConfiguredRulesPolicy
147+
services.add_singleton(
148+
ConfiguredRulesPolicy,
149+
implementation_factory=lambda provider: self._create_configured_rules_policy(
150+
provider, reactor_config
151+
),
152+
)
153+
154+
if logger.isEnabledFor(logging.DEBUG):
155+
logger.debug("Registered steering policies")
156+
except Exception as e:
157+
if logger.isEnabledFor(logging.WARNING):
158+
logger.warning(f"Could not register steering policies: {e}")
159+
160+
def _create_configured_rules_policy(
161+
self, provider: IServiceProvider, reactor_config: Any
162+
) -> ConfiguredRulesPolicy:
163+
"""Create ConfiguredRulesPolicy with synthesized legacy rules."""
164+
from src.services.steering import SessionStateStore
165+
166+
# Build effective rules from config
167+
effective_rules = (
168+
(reactor_config.steering_rules or []).copy()
169+
if reactor_config.steering_rules
170+
else []
171+
)
172+
173+
# Synthesize legacy apply_diff rule if enabled and missing
174+
if getattr(reactor_config, "apply_diff_steering_enabled", True):
175+
has_apply_rule = False
176+
for r in effective_rules:
177+
triggers = (r or {}).get("triggers") or {}
178+
tnames = triggers.get("tool_names") or []
179+
phrases = triggers.get("phrases") or []
180+
if "apply_diff" in tnames or any(
181+
isinstance(p, str) and "apply_diff" in p for p in phrases
182+
):
183+
has_apply_rule = True
184+
break
185+
186+
if not has_apply_rule:
187+
# Check for override file
188+
apply_diff_msg = reactor_config.apply_diff_steering_message
189+
override_path = Path("config/prompts/steering_apply_diff.md")
190+
191+
if not apply_diff_msg and override_path.is_file():
192+
try:
193+
apply_diff_msg = override_path.read_text(encoding="utf-8")
194+
if logger.isEnabledFor(logging.DEBUG):
195+
logger.debug(
196+
"Loaded apply_diff steering prompt from %s",
197+
override_path,
198+
)
199+
except Exception:
200+
logger.warning(
201+
"Failed to read apply_diff steering prompt from %s, using default.",
202+
override_path,
203+
exc_info=True,
204+
)
205+
206+
effective_rules.append(
207+
{
208+
"name": "apply_diff_to_patch_file",
209+
"enabled": True,
210+
"priority": 100,
211+
"triggers": {
212+
"tool_names": ["apply_diff"],
213+
"phrases": [],
214+
},
215+
"message": (
216+
apply_diff_msg
217+
or (
218+
"You tried to use apply_diff tool. Please prefer to use patch_file tool instead, "
219+
"as it is superior to apply_diff and provides automated Python QA checks."
220+
)
221+
),
222+
"rate_limit": {
223+
"calls_per_window": 1,
224+
"window_seconds": reactor_config.apply_diff_steering_rate_limit_seconds,
225+
},
226+
}
227+
)
228+
229+
return ConfiguredRulesPolicy(
230+
session_store=provider.get_required_service(SessionStateStore),
231+
rules=effective_rules,
232+
enabled=True,
233+
)
234+
235+
def _register_unified_steering_handler(
236+
self, services: ServiceCollection, config: AppConfig
237+
) -> None:
238+
"""Register unified steering handler with policies."""
239+
try:
240+
from src.services.steering import UnifiedSteeringHandler
241+
from src.services.steering.policies import (
242+
InlinePythonPolicy,
243+
PytestFullSuitePolicy,
244+
)
245+
246+
def handler_factory(provider: IServiceProvider) -> UnifiedSteeringHandler:
247+
"""Factory for creating unified steering handler with policies."""
248+
policies = [
249+
provider.get_required_service(InlinePythonPolicy),
250+
provider.get_required_service(PytestFullSuitePolicy),
251+
provider.get_required_service(ConfiguredRulesPolicy),
252+
]
253+
254+
reactor_config = config.session.tool_call_reactor
255+
priority_overrides = getattr(
256+
reactor_config, "steering_policy_priorities", None
257+
)
258+
emit_legacy_log_enabled = getattr(
259+
reactor_config, "emit_legacy_steering_log", False
260+
)
261+
262+
return UnifiedSteeringHandler(
263+
policies=policies,
264+
enabled=True,
265+
priority_overrides=priority_overrides,
266+
emit_legacy_log_enabled=emit_legacy_log_enabled,
267+
)
268+
269+
services.add_singleton(
270+
UnifiedSteeringHandler, implementation_factory=handler_factory
271+
)
272+
273+
if logger.isEnabledFor(logging.DEBUG):
274+
logger.debug("Registered UnifiedSteeringHandler")
275+
except Exception as e:
276+
if logger.isEnabledFor(logging.WARNING):
277+
logger.warning(f"Could not register UnifiedSteeringHandler: {e}")

src/core/config/app_config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,21 @@ class ToolCallReactorConfig(DomainModel):
364364
enabled: bool = True
365365
"""Whether the Tool Call Reactor is enabled."""
366366

367+
unified_steering_enabled: bool = True
368+
"""Whether to use the unified steering handler (replacing legacy handlers)."""
369+
370+
emit_legacy_steering_log: bool = True
371+
"""Whether to emit a legacy-formatted steering log for compatibility."""
372+
373+
steering_policy_priorities: dict[str, int] | None = None
374+
"""Overrides for steering policy priorities. Map of policy name to priority integer."""
375+
376+
steering_session_ttl_seconds: int = 1800
377+
"""TTL in seconds for steering session state (default 30 mins)."""
378+
379+
steering_max_sessions: int = 1024
380+
"""Maximum number of sessions to track in steering state store."""
381+
367382
apply_diff_steering_enabled: bool = True
368383
"""Whether the legacy apply_diff steering handler is enabled."""
369384

@@ -386,6 +401,12 @@ class ToolCallReactorConfig(DomainModel):
386401
pytest_full_suite_steering_message: str | None = None
387402
"""Optional custom steering message when detecting full pytest suite runs."""
388403

404+
inline_python_steering_enabled: bool = True
405+
"""Whether inline Python execution steering is enabled."""
406+
407+
inline_python_steering_message: str | None = None
408+
"""Optional custom steering message for inline Python execution."""
409+
389410
pytest_context_saving_enabled: bool = False
390411
"""Whether pytest context-saving command rewrites are enabled."""
391412

0 commit comments

Comments
 (0)