Download tests/test_auto_dev/test_memento_engine.py from techprotrade/annator-command-center: direct link, hf CLI and curl.
- Browser
- Download file 18 kB
-
https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/test_auto_dev/test_memento_engine.py
- Command line
-
hf download hf://spaces/techprotrade/annator-command-center/tests/test_auto_dev/test_memento_engine.py
-
curl -L -o test_memento_engine.py https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/test_auto_dev/test_memento_engine.py
18 kB
| """ | |
| Test MementoEngine skill generation. | |
| Tests cover: | |
| - Episode analysis (extract failure patterns) | |
| - Code proposal (generate skill code) | |
| - Validation (execute in sandbox) | |
| - Skill promotion (create CommunitySkill) | |
| - Event subscription and triggering | |
| """ | |
| import pytest | |
| from unittest.mock import AsyncMock, MagicMock, Mock | |
| from core.auto_dev.memento_engine import MementoEngine | |
| from core.auto_dev.models import SkillCandidate | |
| # ============================================================================= | |
| # Episode Analysis Tests | |
| # ============================================================================= | |
| class TestMementoEngineEpisodeAnalysis: | |
| """Test analyze_episode() extracts failure pattern.""" | |
| async def test_analyze_episode_extracts_task_description(self, auto_dev_db_session, sample_episode): | |
| """Test analyze_episode() extracts task_description.""" | |
| if not sample_episode: | |
| pytest.skip("Episode model not available") | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| result = await engine.analyze_episode(sample_episode.id) | |
| assert "task_description" in result | |
| assert result["task_description"] == "Analyze customer data and generate insights" | |
| async def test_analyze_episode_extracts_error_trace(self, auto_dev_db_session): | |
| """Test analyze_episode() extracts error_trace from failed steps.""" | |
| try: | |
| from core.models import AgentEpisode, EpisodeSegment | |
| # Create test episode with error segments | |
| episode = AgentEpisode( | |
| id="ep-error-001", | |
| agent_id="agent-001", | |
| tenant_id="tenant-001", | |
| task_description="Process data", | |
| maturity_at_time="AUTONOMOUS", | |
| outcome="failure", | |
| success=False, | |
| status="active", | |
| confidence_score=0.5, | |
| constitutional_score=1.0, | |
| human_intervention_count=0, | |
| step_efficiency=1.0, | |
| access_count=0, | |
| importance_score=0.5, | |
| decay_score=1.0, | |
| ) | |
| auto_dev_db_session.add(episode) | |
| auto_dev_db_session.flush() | |
| error_segment = EpisodeSegment( | |
| id="seg-error-001", | |
| episode_id=episode.id, | |
| segment_type="error", | |
| sequence_order=1, | |
| content="ValueError: Invalid data format", | |
| ) | |
| auto_dev_db_session.add(error_segment) | |
| auto_dev_db_session.commit() | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| result = await engine.analyze_episode(episode.id) | |
| assert "error_trace" in result | |
| assert "ValueError" in result["error_trace"] | |
| except ImportError: | |
| pytest.skip("Episode models not available") | |
| async def test_analyze_episode_extracts_tool_calls(self, auto_dev_db_session): | |
| """Test analyze_episode() extracts tool_calls from execution data.""" | |
| try: | |
| from core.models import AgentEpisode, EpisodeSegment | |
| episode = AgentEpisode( | |
| id="ep-tools-001", | |
| agent_id="agent-001", | |
| tenant_id="tenant-001", | |
| task_description="Execute tools", | |
| maturity_at_time="AUTONOMOUS", | |
| outcome="failure", | |
| success=False, | |
| status="active", | |
| confidence_score=0.5, | |
| constitutional_score=1.0, | |
| human_intervention_count=0, | |
| step_efficiency=1.0, | |
| access_count=0, | |
| importance_score=0.5, | |
| decay_score=1.0, | |
| ) | |
| auto_dev_db_session.add(episode) | |
| auto_dev_db_session.flush() | |
| segment = EpisodeSegment( | |
| id="seg-tool-001", | |
| episode_id=episode.id, | |
| segment_type="tool_call", | |
| sequence_order=1, | |
| content="Tool call: data_processor - failed", | |
| ) | |
| auto_dev_db_session.add(segment) | |
| auto_dev_db_session.commit() | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| result = await engine.analyze_episode(episode.id) | |
| assert "tool_calls_attempted" in result | |
| assert len(result["tool_calls_attempted"]) > 0 | |
| assert result["tool_calls_attempted"][0]["tool_name"] == "data_processor" | |
| except ImportError: | |
| pytest.skip("Episode models not available") | |
| async def test_analyze_episode_identifies_failure_patterns(self, auto_dev_db_session): | |
| """Test analyze_episode() identifies failure patterns.""" | |
| try: | |
| from core.models import AgentEpisode | |
| episode = AgentEpisode( | |
| id="ep-pattern-001", | |
| agent_id="agent-001", | |
| tenant_id="tenant-001", | |
| task_description="Process CSV data with invalid format", | |
| maturity_at_time="AUTONOMOUS", | |
| outcome="failure", | |
| success=False, | |
| status="active", | |
| confidence_score=0.5, | |
| constitutional_score=1.0, | |
| human_intervention_count=0, | |
| step_efficiency=1.0, | |
| access_count=0, | |
| importance_score=0.5, | |
| decay_score=1.0, | |
| ) | |
| auto_dev_db_session.add(episode) | |
| auto_dev_db_session.commit() | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| result = await engine.analyze_episode(episode.id) | |
| assert "failure_summary" in result | |
| assert "suggested_skill_name" in result | |
| except ImportError: | |
| pytest.skip("Episode models not available") | |
| async def test_analyze_episode_handles_missing_data_gracefully(self, auto_dev_db_session): | |
| """Test analyze_episode() handles missing data gracefully.""" | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| result = await engine.analyze_episode("nonexistent-episode-id") | |
| assert "error" in result | |
| # ============================================================================= | |
| # Code Proposal Tests | |
| # ============================================================================= | |
| class TestMementoEngineCodeProposal: | |
| """Test propose_code_change() generates skill code.""" | |
| async def test_propose_code_change_calls_llm(self, mock_auto_dev_llm): | |
| """Test propose_code_change() calls LLM.""" | |
| engine = MementoEngine(db=MagicMock(), llm_service=mock_auto_dev_llm) | |
| context = { | |
| "task_description": "Process sales data", | |
| "error_trace": "ValueError: Invalid format", | |
| "tool_calls_attempted": [], | |
| } | |
| result = await engine.propose_code_change(context) | |
| mock_auto_dev_llm.generate_completion.assert_called_once() | |
| assert "def" in result # Should contain function definition | |
| async def test_propose_code_change_generates_python_code(self, mock_auto_dev_llm): | |
| """Test propose_code_change() generates Python code string.""" | |
| engine = MementoEngine(db=MagicMock(), llm_service=mock_auto_dev_llm) | |
| context = { | |
| "task_description": "Analyze data", | |
| "error_trace": "Analysis failed", | |
| } | |
| result = await engine.propose_code_change(context) | |
| assert isinstance(result, str) | |
| assert len(result) > 0 | |
| async def test_propose_code_change_includes_skill_description(self, mock_auto_dev_llm): | |
| """Test propose_code_change() includes skill description in prompt.""" | |
| engine = MementoEngine(db=MagicMock(), llm_service=mock_auto_dev_llm) | |
| context = { | |
| "task_description": "Process CSV files", | |
| "error_trace": "CSV parsing error", | |
| "tool_calls_attempted": [{"tool_name": "csv_reader"}], | |
| } | |
| result = await engine.propose_code_change(context) | |
| # Verify LLM was called with proper context | |
| call_args = mock_auto_dev_llm.generate_completion.call_args | |
| messages = call_args[1]["messages"] | |
| user_prompt = messages[1]["content"] | |
| assert "Process CSV files" in user_prompt | |
| assert "csv_reader" in user_prompt | |
| async def test_propose_code_change_strips_markdown_fences(self, mock_auto_dev_llm): | |
| """Test propose_code_change() strips markdown fences.""" | |
| engine = MementoEngine(db=MagicMock(), llm_service=mock_auto_dev_llm) | |
| # Mock LLM to return code with fences | |
| async def mock_return(**kwargs): | |
| return {"content": "```python\ndef function(): pass\n```"} | |
| mock_auto_dev_llm.generate_completion = AsyncMock(side_effect=mock_return) | |
| context = {"task_description": "Test"} | |
| result = await engine.propose_code_change(context) | |
| assert "```" not in result | |
| assert "def function(): pass" in result | |
| async def test_propose_code_change_handles_llm_errors(self): | |
| """Test propose_code_change() handles LLM errors.""" | |
| # Mock LLM that raises error | |
| mock_llm = MagicMock() | |
| mock_llm.generate_completion = AsyncMock(side_effect=Exception("LLM error")) | |
| engine = MementoEngine(db=MagicMock(), llm_service=mock_llm) | |
| context = {"task_description": "Test"} | |
| result = await engine.propose_code_change(context) | |
| # Should return error comment | |
| assert "# Skill generation failed" in result | |
| # ============================================================================= | |
| # Validation Tests | |
| # ============================================================================= | |
| class TestMementoEngineValidation: | |
| """Test validate_change() executes in sandbox.""" | |
| async def test_validate_change_calls_sandbox(self, mock_sandbox): | |
| """Test validate_change() calls sandbox.""" | |
| engine = MementoEngine(db=MagicMock(), sandbox=mock_sandbox) | |
| code = "def test(): pass" | |
| test_inputs = [{"input": "test"}] | |
| result = await engine.validate_change(code, test_inputs, "tenant-001") | |
| mock_sandbox.execute_raw_python.assert_called() | |
| assert "passed" in result | |
| async def test_validate_change_passes_test_inputs_to_sandbox(self, mock_sandbox): | |
| """Test validate_change() passes test_inputs to sandbox.""" | |
| engine = MementoEngine(db=MagicMock(), sandbox=mock_sandbox) | |
| code = "print('test')" | |
| test_inputs = [{"data": "value1"}, {"data": "value2"}] | |
| await engine.validate_change(code, test_inputs, "tenant-001") | |
| # Should be called twice (once per input) | |
| assert mock_sandbox.execute_raw_python.call_count == 2 | |
| async def test_validate_change_returns_proxy_signals(self, mock_sandbox): | |
| """Test validate_change() returns proxy_signals.""" | |
| engine = MementoEngine(db=MagicMock(), sandbox=mock_sandbox) | |
| code = "def test(): return True" | |
| test_inputs = [{}] | |
| result = await engine.validate_change(code, test_inputs, "tenant-001") | |
| assert "test_results" in result | |
| assert isinstance(result["test_results"], list) | |
| async def test_validate_change_handles_sandbox_errors(self): | |
| """Test validate_change() handles sandbox errors.""" | |
| # Mock sandbox that raises error | |
| mock_sandbox = MagicMock() | |
| mock_sandbox.execute_raw_python = AsyncMock(side_effect=Exception("Sandbox error")) | |
| engine = MementoEngine(db=MagicMock(), sandbox=mock_sandbox) | |
| code = "def test(): pass" | |
| test_inputs = [{}] | |
| # Should not crash, but return error | |
| result = await engine.validate_change(code, test_inputs, "tenant-001") | |
| assert "passed" in result | |
| # ============================================================================= | |
| # Skill Promotion Tests | |
| # ============================================================================= | |
| class TestMementoEngineSkillPromotion: | |
| """Test promote_skill() creates CommunitySkill.""" | |
| async def test_promote_skill_creates_community_skill(self, auto_dev_db_session, sample_skill_candidate): | |
| """Test promote_skill() creates CommunitySkill.""" | |
| from unittest.mock import patch, MagicMock | |
| # Mock SkillBuilderService and SkillMetadata | |
| mock_builder = MagicMock() | |
| mock_builder.create_skill_package = MagicMock(return_value={"success": True}) | |
| mock_skill_metadata = MagicMock() | |
| # Patch where the import happens, not where it's used | |
| with patch("core.skill_builder_service.SkillBuilderService", return_value=mock_builder), \ | |
| patch("core.skill_builder_service.SkillMetadata", mock_skill_metadata): | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| # Update candidate to validated | |
| sample_skill_candidate.validation_status = "validated" | |
| auto_dev_db_session.commit() | |
| result = await engine.promote_skill(sample_skill_candidate.id, sample_skill_candidate.tenant_id) | |
| # Verify candidate was promoted | |
| auto_dev_db_session.refresh(sample_skill_candidate) | |
| assert sample_skill_candidate.validation_status == "promoted" | |
| assert sample_skill_candidate.promoted_at is not None | |
| async def test_promote_skill_handles_not_validated_candidate(self, auto_dev_db_session, sample_skill_candidate): | |
| """Test promote_skill() handles not validated candidate.""" | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| # Candidate is still pending | |
| result = await engine.promote_skill(sample_skill_candidate.id, sample_skill_candidate.tenant_id) | |
| assert "error" in result | |
| async def test_promote_skill_handles_database_errors(self, auto_dev_db_session): | |
| """Test promote_skill() handles database errors.""" | |
| engine = MementoEngine(db=auto_dev_db_session) | |
| result = await engine.promote_skill("nonexistent-id", "tenant-001") | |
| assert "error" in result | |
| # ============================================================================= | |
| # Event Integration Tests | |
| # ============================================================================= | |
| class TestMementoEngineEventIntegration: | |
| """Test event subscription and triggering.""" | |
| def test_memento_engine_has_base_methods(self): | |
| """Test MementoEngine has all required BaseLearningEngine methods.""" | |
| from core.auto_dev.base_engine import BaseLearningEngine | |
| assert issubclass(MementoEngine, BaseLearningEngine) | |
| # Check all abstract methods are implemented | |
| assert hasattr(MementoEngine, "analyze_episode") | |
| assert hasattr(MementoEngine, "propose_code_change") | |
| assert hasattr(MementoEngine, "validate_change") | |
| # ============================================================================= | |
| # Full Pipeline Tests | |
| # ============================================================================= | |
| class TestMementoEngineFullPipeline: | |
| """Test end-to-end MementoEngine pipeline.""" | |
| async def test_generate_skill_candidate_full_pipeline(self, auto_dev_db_session, mock_auto_dev_llm, mock_sandbox, sample_tenant_id, sample_agent_id): | |
| """Test generate_skill_candidate() full pipeline.""" | |
| try: | |
| from core.models import AgentEpisode | |
| episode = AgentEpisode( | |
| id="ep-pipeline-001", | |
| agent_id=sample_agent_id, | |
| tenant_id=sample_tenant_id, | |
| task_description="Process complex data", | |
| maturity_at_time="AUTONOMOUS", | |
| outcome="failure", | |
| success=False, | |
| status="active", | |
| confidence_score=0.5, | |
| constitutional_score=1.0, | |
| human_intervention_count=0, | |
| step_efficiency=1.0, | |
| access_count=0, | |
| importance_score=0.5, | |
| decay_score=1.0, | |
| ) | |
| auto_dev_db_session.add(episode) | |
| auto_dev_db_session.commit() | |
| engine = MementoEngine(db=auto_dev_db_session, llm_service=mock_auto_dev_llm, sandbox=mock_sandbox) | |
| candidate = await engine.generate_skill_candidate( | |
| tenant_id=sample_tenant_id, | |
| agent_id=sample_agent_id, | |
| episode_id=episode.id, | |
| ) | |
| assert candidate.id is not None | |
| assert candidate.validation_status == "pending" | |
| assert candidate.skill_name is not None | |
| assert candidate.generated_code is not None | |
| except ImportError: | |
| pytest.skip("Episode model not available") | |
| async def test_validate_candidate_full_pipeline(self, auto_dev_db_session, mock_sandbox, sample_skill_candidate): | |
| """Test validate_candidate() updates candidate status.""" | |
| engine = MementoEngine(db=auto_dev_db_session, sandbox=mock_sandbox) | |
| result = await engine.validate_candidate( | |
| candidate_id=sample_skill_candidate.id, | |
| tenant_id=sample_skill_candidate.tenant_id, | |
| test_inputs=[{}], | |
| ) | |
| assert result["candidate_id"] == sample_skill_candidate.id | |
| assert "passed" in result | |
| # Check candidate was updated | |
| auto_dev_db_session.refresh(sample_skill_candidate) | |
| assert sample_skill_candidate.validation_status in ["validated", "failed"] | |
| assert sample_skill_candidate.validated_at is not None | |