from pathlib import Path from string import Template import pytest from pydantic import BaseModel import agentci.prompts from agentci.integrations.opencode.schemas import load_schema from agentci.prompts.library import PromptLibrary from agentci.workflows.model import ( AgentResult, DiscussionReply, PlanArtifact, ReviewFinding, ReviewReport, ) PROMPTS = { "plan_initial": {"context", "request"}, "plan_review": {"context", "artifact"}, "plan_revision": {"artifact", "review"}, "discuss": {"artifact", "message"}, "plan_iterate": {"context", "artifact", "review", "message"}, "implement_initial": { "context", "artifact", "request", "development_environment", }, "implementation_review": {"issue_context", "artifact", "pull_context"}, "implementation_revision": {"review", "development_environment"}, "implementation_iterate": { "context", "review", "message", "development_environment", }, "fix": {"context", "message", "development_environment"}, } SCHEMAS: dict[str, tuple[type[BaseModel], set[str]]] = { "plan.json": (PlanArtifact, {"plan_markdown"}), "discussion.json": (DiscussionReply, {"markdown"}), "agent_result.json": (AgentResult, {"summary_markdown", "tests"}), "review.json": (ReviewReport, {"summary", "findings"}), } def prompt_directory() -> Path: module_file = agentci.prompts.__file__ assert module_file is not None return Path(module_file).parent @pytest.mark.parametrize(("name", "identifiers"), PROMPTS.items()) def test_referenced_prompt_loads_and_renders(name: str, identifiers: set[str]) -> None: directory = prompt_directory() source = (directory / f"{name}.md").read_text() values = {identifier: f"<{identifier}-value>" for identifier in identifiers} assert set(Template(source).get_identifiers()) == identifiers rendered = PromptLibrary(directory).render(name, **values) for value in values.values(): assert value in rendered def test_prompt_resource_set_matches_workflow_references() -> None: names = {path.stem for path in prompt_directory().glob("*.md")} assert names == set(PROMPTS) def test_prompt_render_rejects_missing_template_value() -> None: with pytest.raises(KeyError, match="request"): PromptLibrary(prompt_directory()).render("plan_initial", context="context") @pytest.mark.parametrize(("name", "contract"), SCHEMAS.items()) def test_referenced_schema_matches_result_model( name: str, contract: tuple[type[BaseModel], set[str]] ) -> None: model, required = contract schema = load_schema(prompt_directory() / "schemas", name) assert schema["type"] == "object" assert schema["additionalProperties"] is False assert set(schema["properties"]) == set(model.model_fields) assert set(schema["required"]) == required def test_review_finding_schema_matches_model_and_severity_values() -> None: schema = load_schema(prompt_directory() / "schemas", "review.json") finding = schema["properties"]["findings"]["items"] assert set(finding["properties"]) == set(ReviewFinding.model_fields) assert set(finding["required"]) == set(ReviewFinding.model_fields) assert finding["properties"]["severity"]["enum"] == [ "blocking", "major", "minor", ] assert finding["additionalProperties"] is False def test_schema_resource_set_matches_workflow_references() -> None: schema_directory = prompt_directory() / "schemas" names = {path.name for path in schema_directory.glob("*.json")} assert names == set(SCHEMAS)