millforge 0.1.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.
- millforge/__init__.py +1174 -0
- millforge/_forge/LICENSE +21 -0
- millforge/_forge/PROVENANCE.json +295 -0
- millforge/_forge/UPDATE_POLICY.md +24 -0
- millforge/_forge/__init__.py +14 -0
- millforge/_forge/adapter.py +2232 -0
- millforge/_forge/base_runner.py +121 -0
- millforge/_forge/clients/__init__.py +10 -0
- millforge/_forge/clients/base.py +200 -0
- millforge/_forge/context/__init__.py +23 -0
- millforge/_forge/context/manager.py +178 -0
- millforge/_forge/context/strategies.py +335 -0
- millforge/_forge/core/__init__.py +16 -0
- millforge/_forge/core/inference.py +433 -0
- millforge/_forge/core/messages.py +119 -0
- millforge/_forge/core/runner.py +479 -0
- millforge/_forge/core/steps.py +108 -0
- millforge/_forge/core/workflow.py +400 -0
- millforge/_forge/errors.py +222 -0
- millforge/_forge/guardrails/__init__.py +21 -0
- millforge/_forge/guardrails/error_tracker.py +71 -0
- millforge/_forge/guardrails/guardrails.py +194 -0
- millforge/_forge/guardrails/nudge.py +47 -0
- millforge/_forge/guardrails/response_validator.py +119 -0
- millforge/_forge/guardrails/step_enforcer.py +183 -0
- millforge/_forge/prompts/__init__.py +16 -0
- millforge/_forge/prompts/nudges.py +95 -0
- millforge/_forge/prompts/templates.py +285 -0
- millforge/_version.py +3 -0
- millforge/artifacts.py +570 -0
- millforge/base/__init__.py +97 -0
- millforge/base/composition.py +402 -0
- millforge/base/context.py +285 -0
- millforge/base/harness.py +138 -0
- millforge/base/identity.py +465 -0
- millforge/base/options.py +34 -0
- millforge/base/platform.py +17 -0
- millforge/base/prompt.py +317 -0
- millforge/base/runner.py +546 -0
- millforge/compiled_plan.py +970 -0
- millforge/compiler/__init__.py +231 -0
- millforge/compiler/artifact_validation.py +257 -0
- millforge/compiler/canonicalization.py +169 -0
- millforge/compiler/capabilities.py +66 -0
- millforge/compiler/catalogs.py +500 -0
- millforge/compiler/diagnostics.py +491 -0
- millforge/compiler/graph.py +678 -0
- millforge/compiler/lowering.py +198 -0
- millforge/compiler/output.py +692 -0
- millforge/compiler/parsing.py +1424 -0
- millforge/compiler/requests.py +1180 -0
- millforge/compiler/schema_validation.py +272 -0
- millforge/compiler/semantic.py +490 -0
- millforge/compiler/service.py +448 -0
- millforge/compiler/source.py +375 -0
- millforge/compiler/validators.py +184 -0
- millforge/connectors/__init__.py +95 -0
- millforge/connectors/admission.py +801 -0
- millforge/connectors/broker.py +202 -0
- millforge/connectors/contracts.py +1159 -0
- millforge/connectors/diagnostics.py +189 -0
- millforge/connectors/fake.py +66 -0
- millforge/connectors/runtime.py +236 -0
- millforge/contracts.py +2860 -0
- millforge/custom_tools/__init__.py +67 -0
- millforge/custom_tools/compiler.py +724 -0
- millforge/custom_tools/contracts.py +1093 -0
- millforge/custom_tools/diagnostics.py +205 -0
- millforge/eval_artifacts.py +952 -0
- millforge/eval_boundary.py +2435 -0
- millforge/eval_fixtures/__init__.py +1 -0
- millforge/eval_fixtures/default_pack/__init__.py +1 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.bug_diagnosis.traceback.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.direct_edit.import_sort.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.evidence_discipline.no_source_change.v1.json +51 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.false_closure.visible_green.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.multi_file.api_contract.v1.json +54 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.recovery.malformed_artifact.v1.json +54 -0
- millforge/eval_fixtures/default_pack/manifest.json +12 -0
- millforge/eval_modes.py +1282 -0
- millforge/eval_presets.py +1398 -0
- millforge/eval_reports.py +2517 -0
- millforge/eval_suite.py +2429 -0
- millforge/eval_trials.py +2632 -0
- millforge/eval_workflow.py +794 -0
- millforge/exceptions.py +122 -0
- millforge/model_backend.py +2098 -0
- millforge/protocols.py +340 -0
- millforge/py.typed +0 -0
- millforge/runtime.py +1791 -0
- millforge/testing/__init__.py +1089 -0
- millforge/tools/__init__.py +83 -0
- millforge/tools/builtin_runtime.py +1339 -0
- millforge/tools/builtins.py +773 -0
- millforge/tools/execution.py +1545 -0
- millforge/tools/path_policy.py +155 -0
- millforge/tools/pi_compat/PI_LICENSE +21 -0
- millforge/tools/pi_compat/PROVENANCE.json +55 -0
- millforge/tools/pi_compat/UPDATE_POLICY.md +36 -0
- millforge/tools/pi_compat/__init__.py +34 -0
- millforge/tools/pi_compat/contracts.py +49 -0
- millforge/tools/pi_compat/editing.py +390 -0
- millforge/tools/pi_compat/mutations.py +57 -0
- millforge/tools/pi_compat/operations.py +401 -0
- millforge/tools/pi_compat/paths.py +155 -0
- millforge/tools/pi_compat/process.py +1375 -0
- millforge/tools/pi_compat/search.py +738 -0
- millforge/tools/pi_compat/truncation.py +267 -0
- millforge/tools/pi_compat_catalog.py +396 -0
- millforge/tools/pi_compat_runtime.py +460 -0
- millforge/tools/registry.py +553 -0
- millforge/tools/results.py +533 -0
- millforge-0.1.0.dist-info/METADATA +844 -0
- millforge-0.1.0.dist-info/RECORD +116 -0
- millforge-0.1.0.dist-info/WHEEL +4 -0
- millforge-0.1.0.dist-info/licenses/LICENSE +201 -0
millforge/base/prompt.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""Pi-derived system-prompt assembly for ``millforge-base``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import hashlib
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from pydantic import (
|
|
11
|
+
BaseModel,
|
|
12
|
+
ConfigDict,
|
|
13
|
+
Field,
|
|
14
|
+
StrictBool,
|
|
15
|
+
StrictInt,
|
|
16
|
+
StrictStr,
|
|
17
|
+
field_validator,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from millforge.base.context import (
|
|
21
|
+
MillforgeBaseContextSnapshot,
|
|
22
|
+
_bounded_diagnostic,
|
|
23
|
+
_resolved_absolute_path,
|
|
24
|
+
_truncate_to_utf8_bytes,
|
|
25
|
+
_validate_diagnostics,
|
|
26
|
+
_validate_sha256,
|
|
27
|
+
)
|
|
28
|
+
from millforge.base.options import MillforgeBaseOptions
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"MillforgeBasePromptBudgetError",
|
|
32
|
+
"MillforgeBasePromptSnapshot",
|
|
33
|
+
"build_millforge_base_system_prompt",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
_PROMPT_BYTE_LIMIT = 65_536
|
|
37
|
+
_APPEND_PREFIX = "\n\n"
|
|
38
|
+
_CONTEXT_PREFIX = (
|
|
39
|
+
"\n\n<project_context>\n\nProject-specific instructions and guidelines:\n\n"
|
|
40
|
+
)
|
|
41
|
+
_CONTEXT_SUFFIX = "</project_context>\n"
|
|
42
|
+
|
|
43
|
+
_DEFAULT_SYSTEM_PROMPT = (
|
|
44
|
+
"You are an expert coding assistant operating inside Millforge, a coding agent "
|
|
45
|
+
"harness. You help users by reading files, executing commands, editing code, "
|
|
46
|
+
"and writing new files.\n\n"
|
|
47
|
+
"Available tools:\n"
|
|
48
|
+
"- read: Read file contents\n"
|
|
49
|
+
"- bash: Execute commands in the resolved host shell\n"
|
|
50
|
+
"- edit: Make precise file edits, including multiple disjoint edits in one call\n"
|
|
51
|
+
"- write: Create or overwrite files\n"
|
|
52
|
+
"- grep: Search file contents for patterns (respects .gitignore)\n"
|
|
53
|
+
"- find: Find files by glob pattern (respects .gitignore)\n"
|
|
54
|
+
"- ls: List directory contents\n"
|
|
55
|
+
"- submit: Complete the current run\n"
|
|
56
|
+
"- block: Report that the current run is blocked\n"
|
|
57
|
+
"- reject: Reject the current run\n\n"
|
|
58
|
+
"Guidelines:\n"
|
|
59
|
+
"- Use write only for new files or complete rewrites.\n"
|
|
60
|
+
"- Use edit for precise changes; every edits[].oldText must uniquely match the "
|
|
61
|
+
"original file.\n"
|
|
62
|
+
"- Put multiple disjoint changes to one file in one edit call and never overlap "
|
|
63
|
+
"edit regions.\n"
|
|
64
|
+
"- Be concise in your responses.\n"
|
|
65
|
+
"- Show file paths clearly when working with files.\n"
|
|
66
|
+
"- Filesystem and shell tools run with the permissions of the Millforge process "
|
|
67
|
+
"and are not sandboxed.\n"
|
|
68
|
+
"- Finish by calling submit, block, or reject with the corresponding fixed "
|
|
69
|
+
"terminal_result and a concise summary."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class MillforgeBasePromptBudgetError(ValueError):
|
|
74
|
+
"""Raised when the immutable prompt footer cannot fit its byte budget."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class MillforgeBasePromptSnapshot(BaseModel):
|
|
78
|
+
"""The final bounded system prompt and its deterministic accounting."""
|
|
79
|
+
|
|
80
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
81
|
+
|
|
82
|
+
system_instructions: StrictStr
|
|
83
|
+
effective_prompt_sha256: StrictStr
|
|
84
|
+
byte_count: StrictInt = Field(ge=0)
|
|
85
|
+
prompt_date: datetime.date
|
|
86
|
+
truncated: StrictBool
|
|
87
|
+
diagnostics: tuple[StrictStr, ...]
|
|
88
|
+
|
|
89
|
+
@field_validator("effective_prompt_sha256")
|
|
90
|
+
@classmethod
|
|
91
|
+
def _effective_prompt_sha256_is_valid(cls, value: str) -> str:
|
|
92
|
+
return _validate_sha256(value, "effective_prompt_sha256")
|
|
93
|
+
|
|
94
|
+
@field_validator("diagnostics")
|
|
95
|
+
@classmethod
|
|
96
|
+
def _diagnostics_are_valid(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
97
|
+
return _validate_diagnostics(value)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class _PromptFile:
|
|
102
|
+
content: str
|
|
103
|
+
raw_sha256: str
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _prompt_candidates(
|
|
107
|
+
cwd: Path, home_directory: Path, filename: str
|
|
108
|
+
) -> tuple[Path, ...]:
|
|
109
|
+
return (
|
|
110
|
+
cwd / ".millforge" / filename,
|
|
111
|
+
cwd / ".pi" / filename,
|
|
112
|
+
home_directory / ".millforge" / filename,
|
|
113
|
+
home_directory / ".pi" / "agent" / filename,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _discover_prompt_file(
|
|
118
|
+
*, cwd: Path, home_directory: Path, filename: str, diagnostics: list[str]
|
|
119
|
+
) -> _PromptFile | None:
|
|
120
|
+
for candidate in _prompt_candidates(cwd, home_directory, filename):
|
|
121
|
+
resolved = candidate.resolve()
|
|
122
|
+
if not resolved.exists():
|
|
123
|
+
continue
|
|
124
|
+
try:
|
|
125
|
+
raw = resolved.read_bytes()
|
|
126
|
+
except OSError as error:
|
|
127
|
+
diagnostics.append(
|
|
128
|
+
_bounded_diagnostic(f"Could not read {resolved}: {error}")
|
|
129
|
+
)
|
|
130
|
+
continue
|
|
131
|
+
return _PromptFile(
|
|
132
|
+
content=_truncate_to_utf8_bytes(
|
|
133
|
+
raw.decode("utf-8", errors="replace"), _PROMPT_BYTE_LIMIT
|
|
134
|
+
),
|
|
135
|
+
raw_sha256=hashlib.sha256(raw).hexdigest(),
|
|
136
|
+
)
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _footer(*, prompt_date: datetime.date, cwd: Path) -> str:
|
|
141
|
+
return (
|
|
142
|
+
f"\nCurrent date: {prompt_date.isoformat()}"
|
|
143
|
+
f"\nCurrent working directory: {cwd.as_posix()}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _record_prefix(path: Path) -> str:
|
|
148
|
+
return f'<project_instructions path="{path.as_posix()}">\n'
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _snapshot(
|
|
152
|
+
*,
|
|
153
|
+
prompt: str,
|
|
154
|
+
prompt_date: datetime.date,
|
|
155
|
+
truncated: bool,
|
|
156
|
+
diagnostics: tuple[str, ...],
|
|
157
|
+
) -> MillforgeBasePromptSnapshot:
|
|
158
|
+
encoded = prompt.encode("utf-8")
|
|
159
|
+
return MillforgeBasePromptSnapshot(
|
|
160
|
+
system_instructions=prompt,
|
|
161
|
+
effective_prompt_sha256=hashlib.sha256(encoded).hexdigest(),
|
|
162
|
+
byte_count=len(encoded),
|
|
163
|
+
prompt_date=prompt_date,
|
|
164
|
+
truncated=truncated,
|
|
165
|
+
diagnostics=diagnostics,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def build_millforge_base_system_prompt(
|
|
170
|
+
*,
|
|
171
|
+
options: MillforgeBaseOptions,
|
|
172
|
+
context: MillforgeBaseContextSnapshot,
|
|
173
|
+
cwd: Path,
|
|
174
|
+
home_directory: Path,
|
|
175
|
+
prompt_date: datetime.date,
|
|
176
|
+
) -> MillforgeBasePromptSnapshot:
|
|
177
|
+
"""Assemble the exact finite Pi-derived prompt order and framing."""
|
|
178
|
+
|
|
179
|
+
resolved_cwd = _resolved_absolute_path(cwd, "cwd")
|
|
180
|
+
resolved_home = _resolved_absolute_path(home_directory, "home_directory")
|
|
181
|
+
return _build_millforge_base_system_prompt_resolved(
|
|
182
|
+
options=options,
|
|
183
|
+
context=context,
|
|
184
|
+
cwd=resolved_cwd,
|
|
185
|
+
home_directory=resolved_home,
|
|
186
|
+
prompt_date=prompt_date,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _build_millforge_base_system_prompt_resolved(
|
|
191
|
+
*,
|
|
192
|
+
options: MillforgeBaseOptions,
|
|
193
|
+
context: MillforgeBaseContextSnapshot,
|
|
194
|
+
cwd: Path,
|
|
195
|
+
home_directory: Path,
|
|
196
|
+
prompt_date: datetime.date,
|
|
197
|
+
) -> MillforgeBasePromptSnapshot:
|
|
198
|
+
diagnostics = list(context.diagnostics)
|
|
199
|
+
|
|
200
|
+
if options.system_prompt is None:
|
|
201
|
+
discovered_body = _discover_prompt_file(
|
|
202
|
+
cwd=cwd,
|
|
203
|
+
home_directory=home_directory,
|
|
204
|
+
filename="SYSTEM.md",
|
|
205
|
+
diagnostics=diagnostics,
|
|
206
|
+
)
|
|
207
|
+
body = (
|
|
208
|
+
_DEFAULT_SYSTEM_PROMPT
|
|
209
|
+
if discovered_body is None
|
|
210
|
+
else discovered_body.content
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
body = options.system_prompt
|
|
214
|
+
|
|
215
|
+
if options.append_system_prompt is None:
|
|
216
|
+
discovered_append = _discover_prompt_file(
|
|
217
|
+
cwd=cwd,
|
|
218
|
+
home_directory=home_directory,
|
|
219
|
+
filename="APPEND_SYSTEM.md",
|
|
220
|
+
diagnostics=diagnostics,
|
|
221
|
+
)
|
|
222
|
+
append = None if discovered_append is None else discovered_append.content
|
|
223
|
+
else:
|
|
224
|
+
append = options.append_system_prompt
|
|
225
|
+
|
|
226
|
+
footer = _footer(prompt_date=prompt_date, cwd=cwd)
|
|
227
|
+
footer_bytes = len(footer.encode("utf-8"))
|
|
228
|
+
if footer_bytes > _PROMPT_BYTE_LIMIT:
|
|
229
|
+
raise MillforgeBasePromptBudgetError(
|
|
230
|
+
"millforge-base prompt footer exceeds 65536 UTF-8 bytes"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
body_budget = _PROMPT_BYTE_LIMIT - footer_bytes
|
|
234
|
+
bounded_body = _truncate_to_utf8_bytes(body, body_budget)
|
|
235
|
+
diagnostics_tuple = tuple(diagnostics)
|
|
236
|
+
if bounded_body != body:
|
|
237
|
+
return _snapshot(
|
|
238
|
+
prompt=bounded_body + footer,
|
|
239
|
+
prompt_date=prompt_date,
|
|
240
|
+
truncated=True,
|
|
241
|
+
diagnostics=diagnostics_tuple,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
prompt = bounded_body
|
|
245
|
+
if append:
|
|
246
|
+
if len((prompt + _APPEND_PREFIX + footer).encode("utf-8")) > _PROMPT_BYTE_LIMIT:
|
|
247
|
+
return _snapshot(
|
|
248
|
+
prompt=prompt + footer,
|
|
249
|
+
prompt_date=prompt_date,
|
|
250
|
+
truncated=True,
|
|
251
|
+
diagnostics=diagnostics_tuple,
|
|
252
|
+
)
|
|
253
|
+
append_budget = _PROMPT_BYTE_LIMIT - len(
|
|
254
|
+
(prompt + _APPEND_PREFIX + footer).encode("utf-8")
|
|
255
|
+
)
|
|
256
|
+
bounded_append = _truncate_to_utf8_bytes(append, append_budget)
|
|
257
|
+
prompt += _APPEND_PREFIX + bounded_append
|
|
258
|
+
if bounded_append != append:
|
|
259
|
+
return _snapshot(
|
|
260
|
+
prompt=prompt + footer,
|
|
261
|
+
prompt_date=prompt_date,
|
|
262
|
+
truncated=True,
|
|
263
|
+
diagnostics=diagnostics_tuple,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if not context.files:
|
|
267
|
+
return _snapshot(
|
|
268
|
+
prompt=prompt + footer,
|
|
269
|
+
prompt_date=prompt_date,
|
|
270
|
+
truncated=False,
|
|
271
|
+
diagnostics=diagnostics_tuple,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if (
|
|
275
|
+
len((prompt + _CONTEXT_PREFIX + _CONTEXT_SUFFIX + footer).encode("utf-8"))
|
|
276
|
+
> _PROMPT_BYTE_LIMIT
|
|
277
|
+
):
|
|
278
|
+
return _snapshot(
|
|
279
|
+
prompt=prompt + footer,
|
|
280
|
+
prompt_date=prompt_date,
|
|
281
|
+
truncated=True,
|
|
282
|
+
diagnostics=diagnostics_tuple,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
prompt += _CONTEXT_PREFIX
|
|
286
|
+
for context_file in context.files:
|
|
287
|
+
prefix = _record_prefix(context_file.path)
|
|
288
|
+
suffix = "\n</project_instructions>\n\n"
|
|
289
|
+
if (
|
|
290
|
+
len((prompt + prefix + suffix + _CONTEXT_SUFFIX + footer).encode("utf-8"))
|
|
291
|
+
> _PROMPT_BYTE_LIMIT
|
|
292
|
+
):
|
|
293
|
+
return _snapshot(
|
|
294
|
+
prompt=prompt + _CONTEXT_SUFFIX + footer,
|
|
295
|
+
prompt_date=prompt_date,
|
|
296
|
+
truncated=True,
|
|
297
|
+
diagnostics=diagnostics_tuple,
|
|
298
|
+
)
|
|
299
|
+
content_budget = _PROMPT_BYTE_LIMIT - len(
|
|
300
|
+
(prompt + prefix + suffix + _CONTEXT_SUFFIX + footer).encode("utf-8")
|
|
301
|
+
)
|
|
302
|
+
bounded_content = _truncate_to_utf8_bytes(context_file.content, content_budget)
|
|
303
|
+
prompt += prefix + bounded_content + suffix
|
|
304
|
+
if bounded_content != context_file.content:
|
|
305
|
+
return _snapshot(
|
|
306
|
+
prompt=prompt + _CONTEXT_SUFFIX + footer,
|
|
307
|
+
prompt_date=prompt_date,
|
|
308
|
+
truncated=True,
|
|
309
|
+
diagnostics=diagnostics_tuple,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
return _snapshot(
|
|
313
|
+
prompt=prompt + _CONTEXT_SUFFIX + footer,
|
|
314
|
+
prompt_date=prompt_date,
|
|
315
|
+
truncated=False,
|
|
316
|
+
diagnostics=diagnostics_tuple,
|
|
317
|
+
)
|