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
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""Compiler-owned JSON Schema subset validation and normalization."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
SCHEMA_ANNOTATION_KEYWORDS = frozenset({"default", "description"})
|
|
11
|
+
SCHEMA_STRUCTURAL_KEYWORDS = frozenset(
|
|
12
|
+
{"additionalProperties", "const", "enum", "items", "properties", "required", "type"}
|
|
13
|
+
)
|
|
14
|
+
SCHEMA_UNSUPPORTED_BOUND_KEYWORDS = frozenset(
|
|
15
|
+
{"maxItems", "maxLength", "maximum", "minimum"}
|
|
16
|
+
)
|
|
17
|
+
SCHEMA_ALLOWED_KEYWORDS = SCHEMA_ANNOTATION_KEYWORDS | SCHEMA_STRUCTURAL_KEYWORDS
|
|
18
|
+
SCHEMA_SCALAR_TYPES = frozenset({"boolean", "integer", "number", "string"})
|
|
19
|
+
SCHEMA_ALLOWED_TYPES = SCHEMA_SCALAR_TYPES | frozenset({"array", "object"})
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SchemaSubsetError(ValueError):
|
|
23
|
+
"""Raised when a schema falls outside the compiler-owned 03B subset."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate_json_schema_subset(
|
|
27
|
+
schema: Mapping[str, Any], *, field_name: str = "schema"
|
|
28
|
+
) -> MappingProxyType[str, Any]:
|
|
29
|
+
"""Validate, normalize, and deep-freeze an accepted JSON Schema subset."""
|
|
30
|
+
normalized = normalize_json_schema(schema, field_name=field_name)
|
|
31
|
+
return _freeze_json_object(normalized)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def normalize_json_schema(
|
|
35
|
+
schema: Mapping[str, Any], *, field_name: str = "schema"
|
|
36
|
+
) -> dict[str, Any]:
|
|
37
|
+
"""Return a deterministic JSON-compatible schema with annotations stripped."""
|
|
38
|
+
if not isinstance(schema, Mapping):
|
|
39
|
+
raise SchemaSubsetError(f"{field_name} must be a JSON object")
|
|
40
|
+
return _normalize_schema_node(schema, path=f"/{field_name}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def normalized_schema_bytes(schema: Mapping[str, Any]) -> bytes:
|
|
44
|
+
"""Return stable UTF-8 bytes for semantically normalized accepted schemas."""
|
|
45
|
+
normalized = normalize_json_schema(schema)
|
|
46
|
+
return json.dumps(
|
|
47
|
+
normalized,
|
|
48
|
+
allow_nan=False,
|
|
49
|
+
separators=(",", ":"),
|
|
50
|
+
sort_keys=True,
|
|
51
|
+
).encode("utf-8")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def property_schema_compatibility_bytes(schema: Mapping[str, Any]) -> bytes:
|
|
55
|
+
"""Return stable bytes used for top-level argument property compatibility."""
|
|
56
|
+
normalized = normalize_json_schema(schema, field_name="property_schema")
|
|
57
|
+
return json.dumps(
|
|
58
|
+
normalized,
|
|
59
|
+
allow_nan=False,
|
|
60
|
+
separators=(",", ":"),
|
|
61
|
+
sort_keys=True,
|
|
62
|
+
).encode("utf-8")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _normalize_schema_node(schema: Mapping[str, Any], *, path: str) -> dict[str, Any]:
|
|
66
|
+
_validate_object_keys(schema, path=path)
|
|
67
|
+
if "const" in schema:
|
|
68
|
+
return _normalize_const_only_schema(schema, path=path)
|
|
69
|
+
schema_type = schema.get("type")
|
|
70
|
+
if not isinstance(schema_type, str):
|
|
71
|
+
raise SchemaSubsetError(f"{path}/type must be one schema type string")
|
|
72
|
+
if schema_type not in SCHEMA_ALLOWED_TYPES:
|
|
73
|
+
raise SchemaSubsetError(f"{path}/type {schema_type!r} is not supported")
|
|
74
|
+
|
|
75
|
+
normalized: dict[str, Any] = {"type": schema_type}
|
|
76
|
+
if "const" in schema:
|
|
77
|
+
normalized["const"] = _normalize_scalar_literal(
|
|
78
|
+
schema["const"], path=f"{path}/const"
|
|
79
|
+
)
|
|
80
|
+
if "enum" in schema:
|
|
81
|
+
normalized["enum"] = _normalize_enum(schema["enum"], path=f"{path}/enum")
|
|
82
|
+
|
|
83
|
+
if schema_type == "object":
|
|
84
|
+
normalized.update(_normalize_object_schema(schema, path=path))
|
|
85
|
+
elif schema_type == "array":
|
|
86
|
+
normalized.update(_normalize_array_schema(schema, path=path))
|
|
87
|
+
else:
|
|
88
|
+
_reject_keywords(
|
|
89
|
+
schema,
|
|
90
|
+
{"properties", "required", "additionalProperties", "items"},
|
|
91
|
+
path=path,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
_validate_literal_types(normalized, path=path)
|
|
95
|
+
return normalized
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _normalize_const_only_schema(
|
|
99
|
+
schema: Mapping[str, Any], *, path: str
|
|
100
|
+
) -> dict[str, Any]:
|
|
101
|
+
structural_keys = set(schema) & SCHEMA_STRUCTURAL_KEYWORDS
|
|
102
|
+
if structural_keys != {"const"}:
|
|
103
|
+
joined = ", ".join(sorted(structural_keys - {"const"}))
|
|
104
|
+
raise SchemaSubsetError(
|
|
105
|
+
f"{path}/const-only schema cannot include non-annotation keys"
|
|
106
|
+
+ (f": {joined}" if joined else "")
|
|
107
|
+
)
|
|
108
|
+
return {"const": _normalize_scalar_literal(schema["const"], path=f"{path}/const")}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _normalize_object_schema(schema: Mapping[str, Any], *, path: str) -> dict[str, Any]:
|
|
112
|
+
_reject_keywords(schema, {"items"}, path=path)
|
|
113
|
+
additional_properties = schema.get("additionalProperties")
|
|
114
|
+
if additional_properties is not False:
|
|
115
|
+
raise SchemaSubsetError(f"{path}/additionalProperties must be false")
|
|
116
|
+
properties = schema.get("properties", {})
|
|
117
|
+
if not isinstance(properties, Mapping):
|
|
118
|
+
raise SchemaSubsetError(f"{path}/properties must be an object")
|
|
119
|
+
|
|
120
|
+
normalized_properties: dict[str, Any] = {}
|
|
121
|
+
for property_name in sorted(properties):
|
|
122
|
+
if not isinstance(property_name, str) or not property_name:
|
|
123
|
+
raise SchemaSubsetError(f"{path}/properties keys must be nonblank strings")
|
|
124
|
+
property_schema = properties[property_name]
|
|
125
|
+
if not isinstance(property_schema, Mapping):
|
|
126
|
+
raise SchemaSubsetError(
|
|
127
|
+
f"{path}/properties/{property_name} must be a schema object"
|
|
128
|
+
)
|
|
129
|
+
normalized_properties[property_name] = _normalize_schema_node(
|
|
130
|
+
property_schema, path=f"{path}/properties/{property_name}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
required = schema.get("required", [])
|
|
134
|
+
if not isinstance(required, list | tuple):
|
|
135
|
+
raise SchemaSubsetError(f"{path}/required must be an array")
|
|
136
|
+
normalized_required: list[str] = []
|
|
137
|
+
seen_required: set[str] = set()
|
|
138
|
+
for index, item in enumerate(required):
|
|
139
|
+
if not isinstance(item, str) or not item:
|
|
140
|
+
raise SchemaSubsetError(
|
|
141
|
+
f"{path}/required/{index} must be a nonblank string"
|
|
142
|
+
)
|
|
143
|
+
if item in seen_required:
|
|
144
|
+
raise SchemaSubsetError(
|
|
145
|
+
f"{path}/required contains duplicate field {item!r}"
|
|
146
|
+
)
|
|
147
|
+
if item not in normalized_properties:
|
|
148
|
+
raise SchemaSubsetError(f"{path}/required field {item!r} is not a property")
|
|
149
|
+
seen_required.add(item)
|
|
150
|
+
normalized_required.append(item)
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
"additionalProperties": False,
|
|
154
|
+
"properties": normalized_properties,
|
|
155
|
+
"required": sorted(normalized_required),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _normalize_array_schema(schema: Mapping[str, Any], *, path: str) -> dict[str, Any]:
|
|
160
|
+
_reject_keywords(
|
|
161
|
+
schema, {"properties", "required", "additionalProperties"}, path=path
|
|
162
|
+
)
|
|
163
|
+
items = schema.get("items")
|
|
164
|
+
if not isinstance(items, Mapping):
|
|
165
|
+
raise SchemaSubsetError(f"{path}/items must be a schema object")
|
|
166
|
+
return {"items": _normalize_schema_node(items, path=f"{path}/items")}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _normalize_enum(value: Any, *, path: str) -> list[str | int | float | bool | None]:
|
|
170
|
+
if not isinstance(value, list | tuple) or not value:
|
|
171
|
+
raise SchemaSubsetError(f"{path} must be a non-empty array")
|
|
172
|
+
normalized: list[str | int | float | bool | None] = []
|
|
173
|
+
seen: set[tuple[str, Any]] = set()
|
|
174
|
+
for index, item in enumerate(value):
|
|
175
|
+
literal = _normalize_scalar_literal(item, path=f"{path}/{index}")
|
|
176
|
+
key = _scalar_uniqueness_key(literal)
|
|
177
|
+
if key in seen:
|
|
178
|
+
raise SchemaSubsetError(f"{path} contains duplicate scalar literal")
|
|
179
|
+
seen.add(key)
|
|
180
|
+
normalized.append(literal)
|
|
181
|
+
return normalized
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _normalize_scalar_literal(
|
|
185
|
+
value: Any, *, path: str
|
|
186
|
+
) -> str | int | float | bool | None:
|
|
187
|
+
if value is None:
|
|
188
|
+
return None
|
|
189
|
+
if isinstance(value, bool | str):
|
|
190
|
+
return value
|
|
191
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
192
|
+
return value
|
|
193
|
+
if isinstance(value, float):
|
|
194
|
+
json.dumps(value, allow_nan=False)
|
|
195
|
+
return value
|
|
196
|
+
raise SchemaSubsetError(f"{path} must be a scalar string, number, boolean, or null")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _validate_literal_types(schema: Mapping[str, Any], *, path: str) -> None:
|
|
200
|
+
schema_type = schema["type"]
|
|
201
|
+
for key in ("const", "enum"):
|
|
202
|
+
if key not in schema:
|
|
203
|
+
continue
|
|
204
|
+
values = schema[key] if key == "enum" else [schema[key]]
|
|
205
|
+
for value in values:
|
|
206
|
+
if not _literal_matches_type(value, schema_type):
|
|
207
|
+
raise SchemaSubsetError(
|
|
208
|
+
f"{path}/{key} value does not match schema type"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _literal_matches_type(value: Any, schema_type: str) -> bool:
|
|
213
|
+
if schema_type == "boolean":
|
|
214
|
+
return isinstance(value, bool)
|
|
215
|
+
if schema_type == "integer":
|
|
216
|
+
return isinstance(value, int) and not isinstance(value, bool)
|
|
217
|
+
if schema_type == "number":
|
|
218
|
+
return isinstance(value, int | float) and not isinstance(value, bool)
|
|
219
|
+
if schema_type == "string":
|
|
220
|
+
return isinstance(value, str)
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _validate_object_keys(schema: Mapping[str, Any], *, path: str) -> None:
|
|
225
|
+
for key in schema:
|
|
226
|
+
if not isinstance(key, str):
|
|
227
|
+
raise SchemaSubsetError(f"{path} keywords must be strings")
|
|
228
|
+
if key in SCHEMA_UNSUPPORTED_BOUND_KEYWORDS:
|
|
229
|
+
raise SchemaSubsetError(
|
|
230
|
+
f"{path}/{key} is not accepted because runtime does not enforce "
|
|
231
|
+
"schema bounds"
|
|
232
|
+
)
|
|
233
|
+
if key not in SCHEMA_ALLOWED_KEYWORDS:
|
|
234
|
+
raise SchemaSubsetError(f"{path}/{key} is not in the accepted 03B subset")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _reject_keywords(
|
|
238
|
+
schema: Mapping[str, Any], keywords: set[str], *, path: str
|
|
239
|
+
) -> None:
|
|
240
|
+
for keyword in sorted(keywords):
|
|
241
|
+
if keyword in schema:
|
|
242
|
+
raise SchemaSubsetError(
|
|
243
|
+
f"{path}/{keyword} is not valid for type {schema['type']!r}"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _scalar_uniqueness_key(value: str | int | float | bool | None) -> tuple[str, Any]:
|
|
248
|
+
if value is None:
|
|
249
|
+
return ("null", None)
|
|
250
|
+
if isinstance(value, bool):
|
|
251
|
+
return ("boolean", value)
|
|
252
|
+
if isinstance(value, str):
|
|
253
|
+
return ("string", value)
|
|
254
|
+
return ("number", value)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _freeze_json_object(value: Mapping[str, Any]) -> MappingProxyType[str, Any]:
|
|
258
|
+
frozen = _freeze_json_value(dict(value))
|
|
259
|
+
if not isinstance(frozen, MappingProxyType):
|
|
260
|
+
raise SchemaSubsetError("schema must be a JSON object")
|
|
261
|
+
return frozen
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _freeze_json_value(value: Any) -> Any:
|
|
265
|
+
if isinstance(value, Mapping):
|
|
266
|
+
return MappingProxyType(
|
|
267
|
+
{str(key): _freeze_json_value(item) for key, item in value.items()}
|
|
268
|
+
)
|
|
269
|
+
if isinstance(value, list | tuple):
|
|
270
|
+
return tuple(_freeze_json_value(item) for item in value)
|
|
271
|
+
json.dumps(value, allow_nan=False)
|
|
272
|
+
return value
|