py-harness-cli 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- finetune/__init__.py +1 -0
- finetune/agent_system.py +41 -0
- finetune/agent_traces.py +157 -0
- finetune/everyday.py +30 -0
- finetune/hf_ollama.py +158 -0
- finetune/huggingface_store.py +144 -0
- finetune/models.py +74 -0
- finetune/paths.py +11 -0
- finetune/python_vibe.py +788 -0
- finetune/splits.py +54 -0
- finetune/systems.py +9 -0
- harness/__init__.py +42 -0
- harness/__main__.py +8 -0
- harness/act/__init__.py +6 -0
- harness/act/autofix/__init__.py +110 -0
- harness/act/autofix/additions.py +217 -0
- harness/act/autofix/conflicts.py +124 -0
- harness/act/autofix/cover.py +419 -0
- harness/act/autofix/mechanical.py +151 -0
- harness/act/autofix/missing_imports.py +50 -0
- harness/act/autofix/moves.py +439 -0
- harness/act/autofix/names.py +339 -0
- harness/act/autofix/scaffold.py +224 -0
- harness/act/code.py +157 -0
- harness/act/gate.py +229 -0
- harness/act/parse.py +247 -0
- harness/act/patch_fix.py +138 -0
- harness/act/tools.py +244 -0
- harness/agent/__init__.py +11 -0
- harness/agent/dispatch.py +235 -0
- harness/agent/loop.py +699 -0
- harness/agent/options.py +144 -0
- harness/agent/policy.py +856 -0
- harness/agent/prompt.py +170 -0
- harness/cli.py +393 -0
- harness/editor_kit.py +265 -0
- harness/guard/__init__.py +6 -0
- harness/guard/fallbacks.py +6 -0
- harness/guard/loop_guard.py +57 -0
- harness/guard/python_vibe.py +68 -0
- harness/guard/run.py +41 -0
- harness/guard/types.py +19 -0
- harness/locate.py +767 -0
- harness/mcp_stdio.py +306 -0
- harness/memory/__init__.py +5 -0
- harness/memory/conversation.py +104 -0
- harness/model/__init__.py +6 -0
- harness/model/chat_backend.py +100 -0
- harness/model/engine.py +165 -0
- harness/model/ollama_generate.py +60 -0
- harness/model/openai_generate.py +156 -0
- harness/model/outbound.py +83 -0
- harness/model/route.py +90 -0
- harness/observe/__init__.py +6 -0
- harness/observe/eval_gate.py +80 -0
- harness/observe/eval_loop.py +185 -0
- harness/observe/eval_tasks.py +399 -0
- harness/observe/report_md.py +102 -0
- harness/observe/trace_record.py +79 -0
- harness/openai_api.py +81 -0
- harness/paths.py +88 -0
- harness/py.typed +0 -0
- harness/scan/__init__.py +6 -0
- harness/scan/app_spec.py +338 -0
- harness/scan/design.py +112 -0
- harness/scan/existing.py +131 -0
- harness/scan/layout.py +254 -0
- harness/scan/names.py +308 -0
- harness/scan/project_brief.py +287 -0
- harness/scan/project_docs.py +42 -0
- harness/scan/project_scan.py +49 -0
- harness/scan/repo_map.py +101 -0
- harness/secrets.py +39 -0
- harness/server.py +199 -0
- harness/ship/__init__.py +1 -0
- harness/ship/bot_pr.py +221 -0
- harness/ship/git_ship.py +262 -0
- harness/ship/identity.py +62 -0
- harness/ship/ticket.py +251 -0
- harness/skillkit/__init__.py +6 -0
- harness/skillkit/catalog.py +241 -0
- harness/skillkit/refuse_change.py +640 -0
- harness/skillkit/refuse_finish.py +295 -0
- harness/skillkit/target.py +238 -0
- harness/task.py +717 -0
- py_harness_cli-0.3.0.dist-info/METADATA +177 -0
- py_harness_cli-0.3.0.dist-info/RECORD +92 -0
- py_harness_cli-0.3.0.dist-info/WHEEL +5 -0
- py_harness_cli-0.3.0.dist-info/entry_points.txt +3 -0
- py_harness_cli-0.3.0.dist-info/licenses/LICENSE +202 -0
- py_harness_cli-0.3.0.dist-info/licenses/NOTICE +6 -0
- py_harness_cli-0.3.0.dist-info/top_level.txt +2 -0
harness/agent/options.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""The inputs and outputs of a run.
|
|
2
|
+
|
|
3
|
+
`AgentOptions` is everything the caller chooses. `AgentResult` is
|
|
4
|
+
everything the run reports back. These two classes are the public interface
|
|
5
|
+
of the harness; the other modules in this package are how the run is
|
|
6
|
+
carried out.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# The everyday model name and system prompt are product defaults that the
|
|
16
|
+
# training side also needs, so they are defined there and imported here.
|
|
17
|
+
# This is the only place `harness` reaches into `finetune`.
|
|
18
|
+
from finetune.agent_system import AGENT_SYSTEM
|
|
19
|
+
from finetune.everyday import DEFAULT_EVERYDAY_OLLAMA
|
|
20
|
+
|
|
21
|
+
DEFAULT_STEPS = 20
|
|
22
|
+
DEFAULT_MAX_TOKENS = 700
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class AgentOptions:
|
|
27
|
+
"""Settings for one run.
|
|
28
|
+
|
|
29
|
+
Fields:
|
|
30
|
+
project: directory the agent may read and write inside.
|
|
31
|
+
task: what the user asked for, in their own words.
|
|
32
|
+
model: name of the Ollama model to use.
|
|
33
|
+
engine: "ollama", "mlx", or "openai" (remote OpenAI-compatible HTTP).
|
|
34
|
+
scope: subdirectory to stay within. Empty means the whole project.
|
|
35
|
+
skills: skill names to load. Empty means choose them from the task.
|
|
36
|
+
steps: maximum number of model turns before the run stops.
|
|
37
|
+
max_tokens: maximum length of one model reply.
|
|
38
|
+
allow_writes: when False, patch, edit and run are refused and the
|
|
39
|
+
project is not modified. Used for the HTTP server and --dry-run.
|
|
40
|
+
record: file to append redacted turns to, for training data.
|
|
41
|
+
None means the project's own `.python-vibe/traces.jsonl`,
|
|
42
|
+
which is the default: a run that records nothing leaves no
|
|
43
|
+
way to measure it later, and every trace thrown away is a
|
|
44
|
+
trace nobody gets back. `keep_no_record` turns it off.
|
|
45
|
+
keep_no_record: write no trace at all.
|
|
46
|
+
system: system prompt template. Placeholders are filled per run.
|
|
47
|
+
on_event: called with progress messages. None means print nothing.
|
|
48
|
+
on_question: called when the agent asks the user something. None
|
|
49
|
+
means nobody is available to answer, and the run stops instead.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
project: Path
|
|
53
|
+
task: str = ""
|
|
54
|
+
model: str = DEFAULT_EVERYDAY_OLLAMA
|
|
55
|
+
engine: str = "ollama"
|
|
56
|
+
scope: str = ""
|
|
57
|
+
skills: tuple[str, ...] = ()
|
|
58
|
+
steps: int = DEFAULT_STEPS
|
|
59
|
+
max_tokens: int = DEFAULT_MAX_TOKENS
|
|
60
|
+
allow_writes: bool = True
|
|
61
|
+
record: Path | None = None
|
|
62
|
+
keep_no_record: bool = False
|
|
63
|
+
system: str = AGENT_SYSTEM
|
|
64
|
+
on_event: Callable[[str, str], None] | None = None
|
|
65
|
+
# Answering a question is optional. No handler means the loop stops
|
|
66
|
+
# and hands the question back rather than guessing silently.
|
|
67
|
+
on_question: Callable[..., str] | None = None
|
|
68
|
+
|
|
69
|
+
def resolved_project(self) -> Path:
|
|
70
|
+
project = self.project.expanduser().resolve()
|
|
71
|
+
if not project.is_dir():
|
|
72
|
+
raise ValueError(f"not a directory: {project}")
|
|
73
|
+
return project
|
|
74
|
+
|
|
75
|
+
def emit(self, kind: str, text: str) -> None:
|
|
76
|
+
if self.on_event is not None:
|
|
77
|
+
self.on_event(kind, text)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True)
|
|
81
|
+
class Step:
|
|
82
|
+
"""One turn of the loop.
|
|
83
|
+
|
|
84
|
+
Fields:
|
|
85
|
+
number: position in the run, starting at 1.
|
|
86
|
+
action: the action the model asked for, or "" if it could not be read.
|
|
87
|
+
path: file the action applied to.
|
|
88
|
+
result: text returned to the model.
|
|
89
|
+
refused: reason the action was not carried out, or "" if it ran.
|
|
90
|
+
draft: the model's full reply for this turn.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
number: int
|
|
94
|
+
action: str
|
|
95
|
+
path: str = ""
|
|
96
|
+
result: str = ""
|
|
97
|
+
refused: str = ""
|
|
98
|
+
draft: str = ""
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def ran(self) -> bool:
|
|
102
|
+
return not self.refused
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class AgentResult:
|
|
107
|
+
"""What happened during a run.
|
|
108
|
+
|
|
109
|
+
Fields:
|
|
110
|
+
ok: True when the agent finished the task.
|
|
111
|
+
summary: the agent's closing sentence, or the reason it stopped.
|
|
112
|
+
stopped: "done", "steps" when the step budget ran out, or
|
|
113
|
+
"question" when the agent needs an answer to continue.
|
|
114
|
+
steps: every turn, in order.
|
|
115
|
+
writes: files that were changed.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
ok: bool
|
|
119
|
+
summary: str
|
|
120
|
+
stopped: str
|
|
121
|
+
steps: tuple[Step, ...] = ()
|
|
122
|
+
writes: tuple[str, ...] = field(default_factory=tuple)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def refusals(self) -> tuple[str, ...]:
|
|
126
|
+
return tuple(step.refused for step in self.steps if step.refused)
|
|
127
|
+
|
|
128
|
+
def as_dict(self) -> dict:
|
|
129
|
+
return {
|
|
130
|
+
"ok": self.ok,
|
|
131
|
+
"summary": self.summary,
|
|
132
|
+
"stopped": self.stopped,
|
|
133
|
+
"steps": [
|
|
134
|
+
{
|
|
135
|
+
"number": step.number,
|
|
136
|
+
"action": step.action,
|
|
137
|
+
"path": step.path,
|
|
138
|
+
"refused": step.refused,
|
|
139
|
+
"result": step.result[:2000],
|
|
140
|
+
}
|
|
141
|
+
for step in self.steps
|
|
142
|
+
],
|
|
143
|
+
"writes": list(self.writes),
|
|
144
|
+
}
|