-
Notifications
You must be signed in to change notification settings - Fork 11
feat: Add tip provenance tracking and metadata #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,6 +119,22 @@ async def test_save_trajectory_and_retrieve_guidelines(mcp): | |
| guidelines = response.content[0].text | ||
| assert "# Guidelines for: " in guidelines | ||
|
|
||
| # Verify tip provenance in Kaizen backend | ||
| from kaizen.frontend.client.kaizen_client import KaizenClient | ||
| from kaizen.config.kaizen import kaizen_config | ||
|
|
||
| client = KaizenClient() | ||
| entities = client.search_entities( | ||
| namespace_id=kaizen_config.namespace_id, | ||
| filters={"type": "guideline"}, | ||
| limit=10, | ||
| ) | ||
|
Comment on lines
+123
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New The client created at line 125 is never closed within the test body. For the Milvus backend this adds a third connection to the same Milvus Lite instance (alongside the fixture's Consider yielding 🤖 Prompt for AI Agents |
||
| assert len(entities) > 0 | ||
| for entity in entities: | ||
| metadata = entity.metadata or {} | ||
| assert metadata.get("source_task_id") == "123" | ||
| assert metadata.get("creation_mode") == "auto-mcp" | ||
|
|
||
|
|
||
| @pytest.mark.e2e | ||
| async def test_create_entity_without_conflict_resolution(mcp): | ||
|
|
@@ -301,6 +317,6 @@ async def test_create_entity_with_invalid_json_metadata(mcp): | |
|
|
||
| # Should return an error | ||
| assert "error" in result | ||
| assert result["error"] == "Invalid metadata JSON" | ||
| assert result["error"] == "Invalid JSON" | ||
| assert "message" in result | ||
| assert "invalid_metadata" in result | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import json | ||
| import uuid | ||
| import pytest | ||
| from unittest.mock import patch, MagicMock | ||
|
|
||
| from kaizen.frontend.mcp.mcp_server import save_trajectory, create_entity | ||
| from kaizen.schema.conflict_resolution import EntityUpdate | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_get_client(): | ||
| with patch("kaizen.frontend.mcp.mcp_server.get_client") as mock: | ||
| client_instance = mock.return_value | ||
| yield client_instance | ||
|
|
||
|
|
||
| def test_save_trajectory_metadata_injection(mock_get_client): | ||
| # Mock tip generation to prevent actual LLM calls | ||
| with patch("kaizen.frontend.mcp.mcp_server.generate_tips") as mock_generate_tips: | ||
| mock_result = MagicMock() | ||
| mock_tip = MagicMock() | ||
| mock_tip.content = "Always write unit tests" | ||
| mock_tip.category = "testing" | ||
| mock_tip.rationale = "Helps catch bugs early" | ||
| mock_tip.trigger = "writing code" | ||
| mock_result.tips = [mock_tip] | ||
| mock_result.task_description = "Add feature" | ||
| mock_generate_tips.return_value = mock_result | ||
|
|
||
| trajectory_data = json.dumps([{"role": "user", "content": "hi"}]) | ||
| task_id = str(uuid.uuid4()) | ||
|
|
||
| save_trajectory.fn(trajectory_data=trajectory_data, task_id=task_id) | ||
|
|
||
| # Ensure update_entities was called twice (once for trajectory, once for tips) | ||
| assert mock_get_client.update_entities.call_count == 2 | ||
|
|
||
| # Second call is for tips | ||
| call_args = mock_get_client.update_entities.call_args_list[1][1] | ||
| entities = call_args["entities"] | ||
|
|
||
| assert len(entities) == 1 | ||
| tip_entity = entities[0] | ||
| assert tip_entity.type == "guideline" | ||
| assert tip_entity.metadata["source_task_id"] == task_id | ||
| assert tip_entity.metadata["creation_mode"] == "auto-mcp" | ||
|
|
||
|
|
||
| def test_create_entity_metadata_injection_manual_guideline(mock_get_client): | ||
| mock_update = EntityUpdate(id="123", type="guideline", content="docstrings", event="ADD", metadata={"creation_mode": "manual"}) | ||
| mock_get_client.update_entities.return_value = [mock_update] | ||
|
|
||
| # Missing explicit metadata, should auto-inject "manual" | ||
| result_str = create_entity.fn(content="Write clear docstrings", entity_type="guideline") | ||
| result = json.loads(result_str) | ||
| assert result["event"] == "ADD" | ||
| assert "id" in result | ||
|
|
||
| call_args = mock_get_client.update_entities.call_args[1] | ||
| entities = call_args["entities"] | ||
| assert len(entities) == 1 | ||
| entity = entities[0] | ||
|
|
||
| assert entity.type == "guideline" | ||
| assert entity.metadata["creation_mode"] == "manual" | ||
|
|
||
|
|
||
| def test_create_entity_metadata_injection_manual_policy(mock_get_client): | ||
| mock_update = EntityUpdate(id="123", type="policy", content="PR reviews", event="ADD", metadata={"creation_mode": "manual"}) | ||
| mock_get_client.update_entities.return_value = [mock_update] | ||
|
|
||
| result_str = create_entity.fn(content="Require PR reviews", entity_type="policy") | ||
| result = json.loads(result_str) | ||
| assert result["event"] == "ADD" | ||
|
|
||
| call_args = mock_get_client.update_entities.call_args[1] | ||
| entities = call_args["entities"] | ||
| entity = entities[0] | ||
|
|
||
| assert entity.type == "policy" | ||
| assert entity.metadata["creation_mode"] == "manual" | ||
|
|
||
|
|
||
| def test_create_entity_no_metadata_injection_for_other_types(mock_get_client): | ||
| mock_update = EntityUpdate(id="123", type="log", content="App started", event="ADD", metadata={}) | ||
| mock_get_client.update_entities.return_value = [mock_update] | ||
|
|
||
| # A generic log entity shouldn't get creation_mode injected | ||
| result_str = create_entity.fn(content="App started", entity_type="log") | ||
| result = json.loads(result_str) | ||
| assert result["event"] == "ADD" | ||
|
|
||
| call_args = mock_get_client.update_entities.call_args[1] | ||
| entities = call_args["entities"] | ||
| entity = entities[0] | ||
|
|
||
| assert entity.type == "log" | ||
| assert "creation_mode" not in (entity.metadata or {}) | ||
|
Comment on lines
+17
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing All four tests are pure unit tests (fully mocked, no I/O) but none carry the 📝 Proposed fix+@pytest.mark.unit
def test_save_trajectory_metadata_injection(mock_get_client):
+@pytest.mark.unit
def test_create_entity_metadata_injection_manual_guideline(mock_get_client):
+@pytest.mark.unit
def test_create_entity_metadata_injection_manual_policy(mock_get_client):
+@pytest.mark.unit
def test_create_entity_no_metadata_injection_for_other_types(mock_get_client):Alternatively, add a module-level +pytestmark = pytest.mark.unit🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "audibility" → "auditability"
audibilityis not the intended word here.📝 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents