Audit fixes for agent-native architecture gaps: - agent_tools/content.py: parameterize healthy_threshold in check_embedding_quality - agent_tools/__init__.py: add __all__ exports and list_tools() runtime discovery - agent_tools/database.py: add CRUD primitives (create_motion, update_motion, delete_report) plus query_embeddings, query_similar_motions, query_compass_positions - tests/agent_tools/test_database_tools.py: add CRUD tool tests - tests/agent_tools/test_content_tools.py: add parameterized threshold test - tests/agent_tools/test_package.py: test list_tools() and package imports Tests: 245 passed, 3 skippedmain
parent
8af27bbf04
commit
efb3a8fbd2
@ -1 +1,119 @@ |
||||
"""Agent tools for Stemwijzer — atomic primitives for agent operation.""" |
||||
"""Agent tools for Stemwijzer — atomic primitives for agent operation. |
||||
|
||||
Import individual modules or use `list_tools()` for runtime discovery. |
||||
""" |
||||
|
||||
from __future__ import annotations |
||||
|
||||
from agent_tools.analysis import ( |
||||
analyze_axis_stability, |
||||
analyze_party_shift, |
||||
validate_svd_labels, |
||||
) |
||||
from agent_tools.content import ( |
||||
check_embedding_quality, |
||||
suggest_svd_label, |
||||
validate_layman_explanations, |
||||
validate_motion_coverage, |
||||
) |
||||
from agent_tools.context import ( |
||||
append_context_note, |
||||
build_context, |
||||
render_context_markdown, |
||||
) |
||||
from agent_tools.database import ( |
||||
create_motion, |
||||
delete_report, |
||||
query_compass_positions, |
||||
query_embeddings, |
||||
query_motions, |
||||
query_party_positions, |
||||
query_pipeline_status, |
||||
query_similar_motions, |
||||
query_svd_vectors, |
||||
query_votes, |
||||
update_motion, |
||||
) |
||||
from agent_tools.pipeline import ( |
||||
pipeline_check_health, |
||||
pipeline_get_logs, |
||||
pipeline_run_full, |
||||
pipeline_run_stage, |
||||
pipeline_validate_output, |
||||
) |
||||
from agent_tools.reports import generate_report |
||||
|
||||
__all__ = [ |
||||
# Database |
||||
"query_motions", |
||||
"query_votes", |
||||
"query_svd_vectors", |
||||
"query_party_positions", |
||||
"query_pipeline_status", |
||||
"query_embeddings", |
||||
"query_similar_motions", |
||||
"query_compass_positions", |
||||
"create_motion", |
||||
"update_motion", |
||||
"delete_report", |
||||
# Pipeline |
||||
"pipeline_run_stage", |
||||
"pipeline_run_full", |
||||
"pipeline_check_health", |
||||
"pipeline_get_logs", |
||||
"pipeline_validate_output", |
||||
# Analysis |
||||
"analyze_party_shift", |
||||
"analyze_axis_stability", |
||||
"validate_svd_labels", |
||||
# Content |
||||
"validate_motion_coverage", |
||||
"validate_layman_explanations", |
||||
"suggest_svd_label", |
||||
"check_embedding_quality", |
||||
# Reports |
||||
"generate_report", |
||||
# Context |
||||
"build_context", |
||||
"render_context_markdown", |
||||
"append_context_note", |
||||
# Discovery |
||||
"list_tools", |
||||
] |
||||
|
||||
|
||||
def list_tools() -> list[dict[str, str]]: |
||||
"""Return a list of all available agent tools with signatures and descriptions. |
||||
|
||||
Useful for runtime capability discovery and prompt injection. |
||||
""" |
||||
return [ |
||||
{"name": "query_motions", "signature": "query_motions(db_path, limit=100, policy_area=None, start_date=None, end_date=None)", "description": "Query motions from the database with optional filters."}, |
||||
{"name": "query_votes", "signature": "query_votes(db_path, motion_id=None, party=None)", "description": "Query vote counts or individual votes."}, |
||||
{"name": "query_svd_vectors", "signature": "query_svd_vectors(db_path, window_id, entity_type='motion')", "description": "Query SVD vectors for a window and entity type."}, |
||||
{"name": "query_party_positions", "signature": "query_party_positions(db_path, window_id='current_parliament')", "description": "Query party axis positions for a window."}, |
||||
{"name": "query_pipeline_status", "signature": "query_pipeline_status(db_path)", "description": "Query pipeline freshness and coverage metrics."}, |
||||
{"name": "query_embeddings", "signature": "query_embeddings(db_path, motion_id=None, model=None, limit=100)", "description": "Query text/fused embeddings."}, |
||||
{"name": "query_similar_motions", "signature": "query_similar_motions(db_path, motion_id, top_k=10)", "description": "Query similar motions from similarity cache."}, |
||||
{"name": "query_compass_positions", "signature": "query_compass_positions(db_path, window_id='current_parliament')", "description": "Query 2D compass positions for parties/MPs."}, |
||||
{"name": "create_motion", "signature": "create_motion(db_path, title, description, date, policy_area='General', voting_results='[]')", "description": "Insert a new motion into the database."}, |
||||
{"name": "update_motion", "signature": "update_motion(db_path, motion_id, **fields)", "description": "Update fields of an existing motion."}, |
||||
{"name": "delete_report", "signature": "delete_report(output_path)", "description": "Delete a generated report file."}, |
||||
{"name": "pipeline_run_stage", "signature": "pipeline_run_stage(db_path, stage, window_id, dry_run=False)", "description": "Run a single pipeline stage."}, |
||||
{"name": "pipeline_run_full", "signature": "pipeline_run_full(db_path, dry_run=False)", "description": "Run the full pipeline end-to-end."}, |
||||
{"name": "pipeline_check_health", "signature": "pipeline_check_health(db_path)", "description": "Run health checks and return report."}, |
||||
{"name": "pipeline_get_logs", "signature": "pipeline_get_logs(stage, lines=50)", "description": "Retrieve recent log output for a stage."}, |
||||
{"name": "pipeline_validate_output", "signature": "pipeline_validate_output(db_path, stage)", "description": "Validate that a stage produced expected output."}, |
||||
{"name": "analyze_party_shift", "signature": "analyze_party_shift(db_path, party, window_start, window_end)", "description": "Compute party position shift between two windows."}, |
||||
{"name": "analyze_axis_stability", "signature": "analyze_axis_stability(db_path, component, windows)", "description": "Compute axis stability across windows."}, |
||||
{"name": "validate_svd_labels", "signature": "validate_svd_labels(db_path, component)", "description": "Compare SVD theme labels to actual party positions."}, |
||||
{"name": "validate_motion_coverage", "signature": "validate_motion_coverage(db_path, start_date, end_date)", "description": "Check motion coverage for a date range."}, |
||||
{"name": "validate_layman_explanations", "signature": "validate_layman_explanations(db_path, sample_size=50)", "description": "Sample motions and check explanation quality."}, |
||||
{"name": "suggest_svd_label", "signature": "suggest_svd_label(db_path, component, top_n=10)", "description": "Suggest a label based on top/bottom motions."}, |
||||
{"name": "check_embedding_quality", "signature": "check_embedding_quality(db_path, window_id, healthy_threshold=0.8)", "description": "Check embedding coverage for a window."}, |
||||
{"name": "generate_report", "signature": "generate_report(db_path, report_type, parameters, output_path)", "description": "Generate a markdown report."}, |
||||
{"name": "build_context", "signature": "build_context(db_path)", "description": "Build runtime context dict for the agent."}, |
||||
{"name": "render_context_markdown", "signature": "render_context_markdown(db_path)", "description": "Render context as markdown for prompt injection."}, |
||||
{"name": "append_context_note", "signature": "append_context_note(note)", "description": "Append a note to the accumulated agent knowledge."}, |
||||
{"name": "list_tools", "signature": "list_tools()", "description": "Return a list of all available agent tools."}, |
||||
] |
||||
|
||||
@ -0,0 +1,39 @@ |
||||
"""Tests for agent_tools package-level utilities.""" |
||||
|
||||
import pytest |
||||
|
||||
|
||||
class TestListTools: |
||||
def test_returns_tool_list(self): |
||||
from agent_tools import list_tools |
||||
|
||||
result = list_tools() |
||||
assert isinstance(result, list) |
||||
assert len(result) > 0 |
||||
|
||||
names = {t["name"] for t in result} |
||||
assert "query_motions" in names |
||||
assert "pipeline_check_health" in names |
||||
assert "generate_report" in names |
||||
assert "list_tools" in names |
||||
|
||||
def test_each_tool_has_required_fields(self): |
||||
from agent_tools import list_tools |
||||
|
||||
result = list_tools() |
||||
for tool in result: |
||||
assert "name" in tool |
||||
assert "signature" in tool |
||||
assert "description" in tool |
||||
|
||||
|
||||
class TestAllExports: |
||||
def test_query_motions_importable(self): |
||||
from agent_tools import query_motions |
||||
|
||||
assert callable(query_motions) |
||||
|
||||
def test_list_tools_importable(self): |
||||
from agent_tools import list_tools |
||||
|
||||
assert callable(list_tools) |
||||
Loading…
Reference in new issue