File size: 5,247 Bytes
fccd7f6
 
 
 
c6231ab
ea15e09
 
 
 
 
 
 
76e192b
 
 
878b473
 
 
 
fccd7f6
 
 
878b473
 
 
76e192b
878b473
 
76e192b
878b473
 
 
c6231ab
878b473
 
 
 
 
 
 
 
 
 
 
 
 
 
c6231ab
878b473
 
 
 
76e192b
878b473
 
76e192b
878b473
 
 
c6231ab
878b473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ea15e09
878b473
 
 
 
76e192b
 
878b473
76e192b
878b473
 
 
 
 
 
 
 
 
 
ea15e09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fccd7f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
from multi_agent_sdlc.tools.tester.validation import ProjectVerificationResult
from multi_agent_sdlc.tools.tester.descriptions import (
    TESTER_RUN_PROJECT_VERIFICATION_DESCRIPTION,
)
from multi_agent_sdlc.tools.tester.validation import StandardInput
from multi_agent_sdlc.tools.tester.descriptions import (
    TESTER_RUN_VERIFICATION_COMMAND_DESCRIPTION,
)
from multi_agent_sdlc.tools.tester.validation import PythonModuleName
from multi_agent_sdlc.tools.tester.validation import ExecutionTimeout
from multi_agent_sdlc.tools.tester.validation import ApplicationArguments
from multi_agent_sdlc.tools.tester.validation import EntryPoint
from multi_agent_sdlc.tools.tester.descriptions import SYNC_PROJECT_DESCRIPTION
from multi_agent_sdlc.tools.tester.descriptions import RUN_PYTHON_MODULE_DESCRIPTION
from multi_agent_sdlc.tools.tester.descriptions import RUN_APPLICATION_DESCRIPTION
from multi_agent_sdlc.runtime.process import execute_process
from multi_agent_sdlc.runtime.workspace import get_project_directory
from multi_agent_sdlc.state import DevState
from langchain.tools import ToolRuntime, tool
from langchain_core.messages import ToolMessage
from langgraph.types import Command
import json


@tool(
    "tester_run_application",
    description=RUN_APPLICATION_DESCRIPTION,
)
def tester_run_application(
    entry_point: EntryPoint,
    runtime: ToolRuntime[DevState],
    arguments: ApplicationArguments | None = None,
    stdin_text: StandardInput | None = None,
    timeout_seconds: ExecutionTimeout = 15,
) -> dict[str, object]:

    project_directory = get_project_directory(runtime)

    return execute_process(
        [
            "uv",
            "run",
            entry_point,
            *(arguments or []),
        ],
        project_directory=project_directory,
        timeout_seconds=timeout_seconds,
        stdin_text=stdin_text,
    )


@tool(
    "tester_run_python_module",
    description=RUN_PYTHON_MODULE_DESCRIPTION,
)
def tester_run_python_module(
    module: PythonModuleName,
    runtime: ToolRuntime[DevState],
    arguments: ApplicationArguments | None = None,
    stdin_text: StandardInput | None = None,
    timeout_seconds: ExecutionTimeout = 15,
) -> dict[str, object]:

    project_directory = get_project_directory(runtime)

    return execute_process(
        [
            "uv",
            "run",
            "python",
            "-m",
            module,
            *(arguments or []),
        ],
        project_directory=project_directory,
        timeout_seconds=timeout_seconds,
        stdin_text=stdin_text,
    )


@tool(
    "tester_sync_project",
    description=SYNC_PROJECT_DESCRIPTION,
)
def tester_sync_project(
    runtime: ToolRuntime[DevState],
    timeout_seconds: ExecutionTimeout = 120,
) -> dict[str, object]:
    project_directory = get_project_directory(runtime)

    return execute_process(
        ["uv", "sync"],
        project_directory=project_directory,
        timeout_seconds=timeout_seconds,
    )


from typing import Annotated, Literal
from pydantic import Field

VerificationCommand = Annotated[
    Literal[
        "pytest",
        "ruff",
        "mypy",
        "coverage",
    ],
    Field(
        description=(
            "Approved development or verification executable to run "
            "inside the project's uv-managed environment."
        )
    ),
]


@tool(
    "tester_run_verification_command",
    description=TESTER_RUN_VERIFICATION_COMMAND_DESCRIPTION,
)
def tester_run_verification_command(
    command: VerificationCommand,
    runtime: ToolRuntime[DevState],
    arguments: str | None = None,
    timeout_seconds: ExecutionTimeout = 120,
) -> dict[str, object]:
    project_directory = get_project_directory(runtime)

    return execute_process(
        ["uv", "run", command, *(arguments or [])],
        project_directory=project_directory,
        timeout_seconds=timeout_seconds,
    )


@tool(
    "tester_run_project_verification",
    description=TESTER_RUN_PROJECT_VERIFICATION_DESCRIPTION,
)
def tester_run_project_verification(
    runtime: ToolRuntime[DevState],
    timeout_seconds: ExecutionTimeout = 200,
) -> ProjectVerificationResult:
    commands = [
        ["uv", "run", "ruff", "check", "."],
        ["uv", "run", "ruff", "format", "--check", "."],
        ["uv", "run", "mypy", "src"],
        ["uv", "run", "pytest"],
    ]

    project_directory = get_project_directory(runtime)

    checks = [
        execute_process(
            command=command,
            project_directory=project_directory,
            timeout_seconds=timeout_seconds,
        )
        for command in commands
    ]

    passed = all(not check["timed_out"] and check["exit_code"] == 0 for check in checks)

    result: ProjectVerificationResult = {
        "verification_type": "complete_project_verification",
        "passed": passed,
        "overall_exit_code": 0 if passed else 1,
        "checks": checks,
    }

    return Command(
        update={
            "current_project_verification_result": result,
            "tester_messages": [
                ToolMessage(
                    content=json.dumps(result),
                    tool_call_id=runtime.tool_call_id,
                )
            ],
        }
    )