openverify 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.
Files changed (125) hide show
  1. openverify/__init__.py +38 -0
  2. openverify/adapters/__init__.py +28 -0
  3. openverify/adapters/demo.py +102 -0
  4. openverify/authoring/__init__.py +6 -0
  5. openverify/authoring/graph.py +262 -0
  6. openverify/authoring/references.py +21 -0
  7. openverify/cli.py +58 -0
  8. openverify/conformance/__init__.py +21 -0
  9. openverify/data/profile.json +97 -0
  10. openverify/data/schemas/activation.schema.json +24 -0
  11. openverify/data/schemas/admission.schema.json +25 -0
  12. openverify/data/schemas/artifact-ref.schema.json +43 -0
  13. openverify/data/schemas/cancellation-event.schema.json +29 -0
  14. openverify/data/schemas/decision.schema.json +58 -0
  15. openverify/data/schemas/evidence.schema.json +53 -0
  16. openverify/data/schemas/execution-observation.schema.json +51 -0
  17. openverify/data/schemas/extension.schema.json +28 -0
  18. openverify/data/schemas/graph.schema.json +158 -0
  19. openverify/data/schemas/input-port.schema.json +50 -0
  20. openverify/data/schemas/input-resolution.schema.json +31 -0
  21. openverify/data/schemas/operation-requirement.schema.json +22 -0
  22. openverify/data/schemas/output-port.schema.json +32 -0
  23. openverify/data/schemas/policy-evaluation.schema.json +67 -0
  24. openverify/data/schemas/profile.schema.json +77 -0
  25. openverify/data/schemas/provider-binding.schema.json +33 -0
  26. openverify/data/schemas/resource-dimension.schema.json +35 -0
  27. openverify/data/schemas/resource.schema.json +59 -0
  28. openverify/data/schemas/result.schema.json +145 -0
  29. openverify/data/schemas/run-record.schema.json +269 -0
  30. openverify/data/schemas/selection.schema.json +23 -0
  31. openverify/data/schemas/severity-domain.schema.json +37 -0
  32. openverify/data/schemas/value-source.schema.json +76 -0
  33. openverify/data/schemas/verification-definition.schema.json +235 -0
  34. openverify/dataflow.py +487 -0
  35. openverify/engine/__init__.py +295 -0
  36. openverify/engine/admission.py +30 -0
  37. openverify/engine/aggregation.py +98 -0
  38. openverify/engine/binding.py +69 -0
  39. openverify/engine/policy.py +80 -0
  40. openverify/error_codes.py +41 -0
  41. openverify/errors.py +32 -0
  42. openverify/fingerprint/__init__.py +11 -0
  43. openverify/inspection.py +68 -0
  44. openverify/json_schema.py +156 -0
  45. openverify/lifecycle.py +84 -0
  46. openverify/model/__init__.py +340 -0
  47. openverify/policy.py +84 -0
  48. openverify/provenance/__init__.py +41 -0
  49. openverify/provider/__init__.py +81 -0
  50. openverify/provider/conformance.py +67 -0
  51. openverify/provider/data/schemas/cancel-request.schema.json +28 -0
  52. openverify/provider/data/schemas/capability.schema.json +80 -0
  53. openverify/provider/data/schemas/initialize-request.schema.json +38 -0
  54. openverify/provider/data/schemas/initialize-result.schema.json +27 -0
  55. openverify/provider/data/schemas/invocation.schema.json +60 -0
  56. openverify/provider/data/schemas/outcome.schema.json +92 -0
  57. openverify/provider/data/schemas/provider-error.schema.json +38 -0
  58. openverify/provider/data/schemas/provider-event.schema.json +131 -0
  59. openverify/provider/data/schemas/provider-manifest.schema.json +56 -0
  60. openverify/provider/lifecycle.py +229 -0
  61. openverify/provider/model.py +107 -0
  62. openverify/provider/stdio.py +264 -0
  63. openverify/provider/validation.py +366 -0
  64. openverify/provider_codes.py +14 -0
  65. openverify/py.typed +0 -0
  66. openverify/relay/__init__.py +8 -0
  67. openverify/relay/adapters/__init__.py +1 -0
  68. openverify/relay/adapters/binder.py +31 -0
  69. openverify/relay/adapters/catalog.py +83 -0
  70. openverify/relay/adapters/inprocess.py +55 -0
  71. openverify/relay/adapters/jsonl.py +28 -0
  72. openverify/relay/adapters/local.py +110 -0
  73. openverify/relay/adapters/logging.py +25 -0
  74. openverify/relay/adapters/memory.py +21 -0
  75. openverify/relay/adapters/process.py +124 -0
  76. openverify/relay/adapters/server.py +179 -0
  77. openverify/relay/adapters/stdio.py +154 -0
  78. openverify/relay/adapters/windows_job.py +81 -0
  79. openverify/relay/api.py +165 -0
  80. openverify/relay/application/__init__.py +1 -0
  81. openverify/relay/application/binding.py +58 -0
  82. openverify/relay/application/contract.py +37 -0
  83. openverify/relay/application/execution.py +198 -0
  84. openverify/relay/application/policies.py +123 -0
  85. openverify/relay/application/preparation.py +39 -0
  86. openverify/relay/application/run.py +310 -0
  87. openverify/relay/config.py +21 -0
  88. openverify/relay/core/__init__.py +1 -0
  89. openverify/relay/core/aggregation.py +99 -0
  90. openverify/relay/core/readiness.py +20 -0
  91. openverify/relay/core/state.py +25 -0
  92. openverify/relay/entrypoints/__init__.py +1 -0
  93. openverify/relay/entrypoints/cli.py +208 -0
  94. openverify/relay/entrypoints/http.py +54 -0
  95. openverify/relay/entrypoints/mcp.py +38 -0
  96. openverify/relay/entrypoints/service.py +59 -0
  97. openverify/relay/error_codes.py +16 -0
  98. openverify/relay/errors.py +17 -0
  99. openverify/relay/ports/__init__.py +75 -0
  100. openverify/relay/ports/binding.py +28 -0
  101. openverify/relay/providers.py +166 -0
  102. openverify/relay/templates.py +129 -0
  103. openverify/serialization/__init__.py +134 -0
  104. openverify/standard/__init__.py +68 -0
  105. openverify/standard/astgrep.py +75 -0
  106. openverify/standard/command.py +86 -0
  107. openverify/standard/common.py +64 -0
  108. openverify/standard/control.py +53 -0
  109. openverify/standard/filesystem.py +124 -0
  110. openverify/standard/git.py +210 -0
  111. openverify/standard/paths.py +26 -0
  112. openverify/standard/pattern_worker.py +16 -0
  113. openverify/standard/pytest.py +44 -0
  114. openverify/standard/ruff.py +86 -0
  115. openverify/standard/scope.py +105 -0
  116. openverify/standard/structured.py +37 -0
  117. openverify/standard/text.py +86 -0
  118. openverify/standard/tools.py +52 -0
  119. openverify/validation/__init__.py +644 -0
  120. openverify-0.1.0.dist-info/METADATA +481 -0
  121. openverify-0.1.0.dist-info/RECORD +125 -0
  122. openverify-0.1.0.dist-info/WHEEL +5 -0
  123. openverify-0.1.0.dist-info/entry_points.txt +2 -0
  124. openverify-0.1.0.dist-info/licenses/LICENSE +22 -0
  125. openverify-0.1.0.dist-info/top_level.txt +1 -0
openverify/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ """Draft domain-neutral verification protocol implementation."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from openverify.authoring import Graph
6
+ from openverify.dataflow import validate_run_inputs
7
+ from openverify.errors import ProtocolError
8
+ from openverify.fingerprint import fingerprint
9
+ from openverify.inspection import flattened_status, inspect_definition
10
+ from openverify.relay import Relay
11
+ from openverify.serialization import serialize
12
+ from openverify.validation import (
13
+ load_schema,
14
+ parse_definition,
15
+ parse_result,
16
+ parse_run_record,
17
+ validate_definition,
18
+ validate_executable_definition,
19
+ validate_result,
20
+ )
21
+
22
+ __all__ = [
23
+ "Graph",
24
+ "Relay",
25
+ "ProtocolError",
26
+ "fingerprint",
27
+ "flattened_status",
28
+ "inspect_definition",
29
+ "load_schema",
30
+ "parse_definition",
31
+ "parse_result",
32
+ "parse_run_record",
33
+ "serialize",
34
+ "validate_definition",
35
+ "validate_executable_definition",
36
+ "validate_result",
37
+ "validate_run_inputs",
38
+ ]
@@ -0,0 +1,28 @@
1
+ """Trusted local implementations bound to exact capability ID/version requirements."""
2
+
3
+ from typing import Protocol
4
+
5
+ from openverify.model import Context, Decision, ExecutionObservation, Result
6
+
7
+
8
+ class Adapter(Protocol):
9
+ def execute(
10
+ self, inputs: Context, config: Context, /
11
+ ) -> ExecutionObservation | Context | Result | Decision: ...
12
+
13
+
14
+ class Registry:
15
+ def __init__(self) -> None:
16
+ self.operations: dict[tuple[str, str], Adapter] = {}
17
+
18
+ def register(self, capability_id: str, capability_version: str, adapter: Adapter) -> None:
19
+ self.operations[(capability_id, capability_version)] = adapter
20
+
21
+ def register_derive(self, name: str, adapter: Adapter, *, version: str = "1") -> None:
22
+ self.register(name, version, adapter)
23
+
24
+ def register_evaluate(self, name: str, adapter: Adapter, *, version: str = "1") -> None:
25
+ self.register(name, version, adapter)
26
+
27
+ def register_control(self, name: str, adapter: Adapter, *, version: str = "1") -> None:
28
+ self.register(name, version, adapter)
@@ -0,0 +1,102 @@
1
+ """Small local examples, not protocol primitives or an untrusted code runner."""
2
+
3
+ import re
4
+ from typing import cast
5
+
6
+ from openverify.adapters import Registry
7
+ from openverify.errors import ProtocolError
8
+ from openverify.model import Context, Decision, Evidence, Result
9
+ from openverify.validation import parse_result
10
+
11
+
12
+ def known(verdict: str | None = None, score: float | None = None) -> Result:
13
+ return parse_result(
14
+ {
15
+ "protocol": "draft",
16
+ "disposition": "EXECUTED",
17
+ "execution_outcome": "SUCCESS",
18
+ "applicability": "APPLICABLE",
19
+ "determination": "KNOWN",
20
+ "verdict": verdict,
21
+ "score": score,
22
+ }
23
+ )
24
+
25
+
26
+ class ConstantEvaluator:
27
+ version = "1"
28
+
29
+ def execute(self, inputs: Context, config: Context) -> Result:
30
+ return parse_result(config["result"])
31
+
32
+
33
+ class RegexEvaluator:
34
+ version = "1"
35
+
36
+ def execute(self, inputs: Context, config: Context) -> Result:
37
+ text = inputs[cast(str, config["key"])]
38
+ pattern = config["pattern"]
39
+ must_match = config["must_match"]
40
+ if (
41
+ not isinstance(text, str)
42
+ or not isinstance(pattern, str)
43
+ or type(must_match) is not bool
44
+ ):
45
+ raise ProtocolError(
46
+ "EXECUTOR_ERROR", "Regex demo needs text, pattern, and boolean must_match"
47
+ )
48
+ matched = re.search(pattern, text) is not None
49
+ result = known("PASS" if matched == must_match else "FAIL")
50
+ return result.model_copy(
51
+ update={"evidence": [Evidence(message=f"Pattern matched: {matched}")]}
52
+ )
53
+
54
+
55
+ class RangeEvaluator:
56
+ version = "1"
57
+
58
+ def execute(self, inputs: Context, config: Context) -> Result:
59
+ value = inputs[cast(str, config["key"])]
60
+ low, high = config["minimum"], config["maximum"]
61
+ if any(type(v) not in (int, float) for v in (value, low, high)):
62
+ raise ProtocolError("EXECUTOR_ERROR", "Range demo requires numeric values")
63
+ if cast(float, low) > cast(float, high):
64
+ raise ProtocolError("EXECUTOR_ERROR", "Inverted range")
65
+ return known(
66
+ "PASS" if cast(float, low) <= cast(float, value) <= cast(float, high) else "FAIL"
67
+ )
68
+
69
+
70
+ class LengthDeriver:
71
+ version = "1"
72
+
73
+ def execute(self, inputs: Context, config: Context) -> Context:
74
+ value = inputs[cast(str, config["key"])]
75
+ if not isinstance(value, str):
76
+ raise ProtocolError("EXECUTOR_ERROR", "Length demo requires text")
77
+ return {cast(str, config["output_key"]): len(value)}
78
+
79
+
80
+ class UnknownRouter:
81
+ version = "1"
82
+
83
+ def execute(self, inputs: Context, config: Context) -> Decision:
84
+ if any(
85
+ isinstance(r, dict) and r.get("determination") == "UNKNOWN" for r in inputs.values()
86
+ ):
87
+ return Decision(
88
+ kind="ROUTE",
89
+ destination=cast(str, config["destination"]),
90
+ reason="Uncertain evaluation",
91
+ )
92
+ return Decision(kind="CONTINUE")
93
+
94
+
95
+ def demo_registry() -> Registry:
96
+ registry = Registry()
97
+ registry.register_evaluate("com.example.demo.constant", ConstantEvaluator())
98
+ registry.register_evaluate("com.example.demo.regex", RegexEvaluator())
99
+ registry.register_evaluate("com.example.demo.range", RangeEvaluator())
100
+ registry.register_derive("com.example.demo.length", LengthDeriver())
101
+ registry.register_control("com.example.demo.unknown_route", UnknownRouter())
102
+ return registry
@@ -0,0 +1,6 @@
1
+ """Portable VCP graph authoring."""
2
+
3
+ from .graph import Graph, NodeRef
4
+ from .references import Ref, constant, external
5
+
6
+ __all__ = ("Graph", "NodeRef", "Ref", "constant", "external")
@@ -0,0 +1,262 @@
1
+ """An explicit, small VCP builder. build() always returns a validated SDK definition."""
2
+
3
+ from copy import deepcopy
4
+ from dataclasses import dataclass
5
+ from typing import Literal, TypedDict, Unpack, cast
6
+
7
+ from pydantic import JsonValue
8
+
9
+ from openverify.authoring.references import Ref, constant
10
+ from openverify.inspection import inspect_definition
11
+ from openverify.model import (
12
+ AdmissionPolicy,
13
+ ArtifactRef,
14
+ Capability,
15
+ Constant,
16
+ Context,
17
+ Criterion,
18
+ Edge,
19
+ ExecutionPolicy,
20
+ Guard,
21
+ InputPort,
22
+ JsonSchema,
23
+ Node,
24
+ OperationRequirement,
25
+ OutputPort,
26
+ ResourceVector,
27
+ RunInput,
28
+ UpstreamOutput,
29
+ VerificationDefinition,
30
+ )
31
+ from openverify.model import (
32
+ Graph as VCPGraph,
33
+ )
34
+ from openverify.serialization import serialize, to_data
35
+ from openverify.validation import HANDLING, parse_definition
36
+
37
+ type NodeKind = Literal["DERIVE", "EVALUATE", "CONTROL", "AGGREGATE"]
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class NodeRef:
42
+ id: str
43
+ outputs: dict[str, Ref]
44
+
45
+ def __getitem__(self, name: str) -> Ref:
46
+ return self.outputs[name]
47
+
48
+ @property
49
+ def result(self) -> Ref:
50
+ return self["result"]
51
+
52
+ @property
53
+ def decision(self) -> Ref:
54
+ return self["decision"]
55
+
56
+
57
+ class NodeOptions(TypedDict, total=False):
58
+ version: str
59
+ inputs: dict[str, Ref] | None
60
+ outputs: dict[str, JsonSchema] | None
61
+ config: Context | None
62
+ config_schema: JsonSchema | None
63
+ dependencies: list[NodeRef] | None
64
+ input_ports: list[InputPort] | None
65
+ aggregate_inputs: list[str] | None
66
+ resource_estimate: ResourceVector | None
67
+
68
+
69
+ class Graph:
70
+ def __init__(
71
+ self,
72
+ id: str,
73
+ *,
74
+ version: str = "1",
75
+ subject: ArtifactRef | None = None,
76
+ criterion: Criterion | None = None,
77
+ judgment_schema: JsonSchema = True,
78
+ execution_policy: ExecutionPolicy | None = None,
79
+ ) -> None:
80
+ self.id = id
81
+ self.version = version
82
+ self.subject = subject or ArtifactRef(id=id, type="declared-input", locator={"graph": id})
83
+ self.criterion = criterion or Criterion(id=id, description=f"Verification defined by {id}")
84
+ self.judgment_schema = judgment_schema
85
+ self.execution_policy = execution_policy or ExecutionPolicy(
86
+ activation=Capability(executor_ref="MANUAL"),
87
+ selection=Capability(executor_ref="EXPLICIT"),
88
+ admission=AdmissionPolicy(strategy="ALWAYS"),
89
+ )
90
+ self._inputs: list[InputPort] = []
91
+ self._nodes: list[Node] = []
92
+ self._edges: list[Edge] = []
93
+
94
+ def input(
95
+ self,
96
+ name: str,
97
+ schema: JsonSchema,
98
+ *,
99
+ required: bool = True,
100
+ default: Constant | None = None,
101
+ ) -> Ref:
102
+ self._inputs.append(
103
+ InputPort(
104
+ name=name,
105
+ schema=deepcopy(schema),
106
+ required=required,
107
+ on_unavailable="BLOCK" if required else "DEFAULT" if default else "OMIT",
108
+ default=default,
109
+ )
110
+ )
111
+ return Ref(RunInput(name=name), deepcopy(schema))
112
+
113
+ def node(
114
+ self,
115
+ id: str,
116
+ kind: NodeKind,
117
+ capability: str,
118
+ *,
119
+ version: str = "1",
120
+ inputs: dict[str, Ref] | None = None,
121
+ outputs: dict[str, JsonSchema] | None = None,
122
+ config: Context | None = None,
123
+ config_schema: JsonSchema | None = None,
124
+ dependencies: list[NodeRef] | None = None,
125
+ input_ports: list[InputPort] | None = None,
126
+ aggregate_inputs: list[str] | None = None,
127
+ resource_estimate: ResourceVector | None = None,
128
+ ) -> NodeRef:
129
+ bindings = dict(inputs or {})
130
+ if kind in ("EVALUATE", "AGGREGATE"):
131
+ if "subject" in bindings or "criterion" in bindings:
132
+ raise ValueError(
133
+ "Builder binds definition subject and criterion; "
134
+ "use raw VCP for explicit declarations"
135
+ )
136
+ bindings.update(
137
+ subject=constant(to_data(self.subject), {"type": "object"}),
138
+ criterion=constant(to_data(self.criterion), {"type": "object"}),
139
+ )
140
+ ports = {p.name: p for p in input_ports or []}
141
+ for name, ref in bindings.items():
142
+ if name not in ports:
143
+ ports[name] = InputPort(
144
+ name=name, schema=deepcopy(ref.schema), required=True, on_unavailable="BLOCK"
145
+ )
146
+ declared = [
147
+ OutputPort(name=name, schema=deepcopy(schema), role="INFORMATION")
148
+ for name, schema in (outputs or {}).items()
149
+ ]
150
+ if kind in ("EVALUATE", "AGGREGATE", "CONTROL"):
151
+ role = "DECISION" if kind == "CONTROL" else "RESULT"
152
+ declared.append(OutputPort(name=role.lower(), schema={"type": "object"}, role=role))
153
+ self._nodes.append(
154
+ Node(
155
+ id=id,
156
+ kind=kind,
157
+ operation=OperationRequirement(
158
+ capability_id=capability, capability_version=version
159
+ ),
160
+ inputs=list(ports.values()),
161
+ outputs=declared,
162
+ bindings={n: deepcopy(r.source) for n, r in bindings.items()},
163
+ aggregate_inputs=aggregate_inputs or [],
164
+ config=deepcopy(config or {}),
165
+ config_schema=deepcopy(config_schema),
166
+ resource_estimate=deepcopy(resource_estimate or {}),
167
+ )
168
+ )
169
+ predecessors = [
170
+ r.source.node_id for r in bindings.values() if isinstance(r.source, UpstreamOutput)
171
+ ]
172
+ predecessors.extend(n.id for n in dependencies or [])
173
+ for predecessor in dict.fromkeys(predecessors):
174
+ self._edges.append(Edge(from_node=predecessor, to_node=id))
175
+ return NodeRef(
176
+ id,
177
+ {
178
+ p.name: Ref(UpstreamOutput(node_id=id, output=p.name), p.value_schema)
179
+ for p in declared
180
+ },
181
+ )
182
+
183
+ def derive(self, id: str, capability: str, **kwargs: Unpack[NodeOptions]) -> NodeRef:
184
+ return self.node(id, "DERIVE", capability, **kwargs)
185
+
186
+ def evaluate(self, id: str, capability: str, **kwargs: Unpack[NodeOptions]) -> NodeRef:
187
+ return self.node(id, "EVALUATE", capability, **kwargs)
188
+
189
+ def control(self, id: str, capability: str, **kwargs: Unpack[NodeOptions]) -> NodeRef:
190
+ return self.node(id, "CONTROL", capability, **kwargs)
191
+
192
+ def aggregate(
193
+ self,
194
+ id: str,
195
+ members: dict[str, Ref],
196
+ *,
197
+ reducer: str = "ALL",
198
+ handling: dict[str, str] | None = None,
199
+ config: Context | None = None,
200
+ capability: str = "openverify.aggregate",
201
+ version: str = "1",
202
+ ) -> NodeRef:
203
+ names = list(members)
204
+ if set(names) & {"subject", "criterion", "members"}:
205
+ raise ValueError("Reserved aggregate member name")
206
+ settings: Context = dict(config or {})
207
+ if capability == "openverify.aggregate":
208
+ settings.update(
209
+ reducer=reducer, handling=dict(handling or {k: "REJECT" for k in sorted(HANDLING)})
210
+ )
211
+ return self.node(
212
+ id,
213
+ "AGGREGATE",
214
+ capability,
215
+ version=version,
216
+ inputs={
217
+ **members,
218
+ "members": constant(
219
+ cast(JsonValue, names), {"type": "array", "items": {"type": "string"}}
220
+ ),
221
+ },
222
+ aggregate_inputs=names,
223
+ config=settings,
224
+ )
225
+
226
+ def edge(self, predecessor: NodeRef, successor: NodeRef, *, guard: Guard | None = None) -> None:
227
+ self._edges = [
228
+ e for e in self._edges if (e.from_node, e.to_node) != (predecessor.id, successor.id)
229
+ ]
230
+ self._edges.append(Edge(from_node=predecessor.id, to_node=successor.id, guard=guard))
231
+
232
+ def add_node(self, node: Node) -> NodeRef:
233
+ self._nodes.append(deepcopy(node))
234
+ return NodeRef(
235
+ node.id,
236
+ {
237
+ p.name: Ref(UpstreamOutput(node_id=node.id, output=p.name), p.value_schema)
238
+ for p in node.outputs
239
+ },
240
+ )
241
+
242
+ def build(self) -> VerificationDefinition:
243
+ definition = VerificationDefinition(
244
+ id=self.id,
245
+ version=self.version,
246
+ subject=self.subject,
247
+ criterion=self.criterion,
248
+ judgment_schema=self.judgment_schema,
249
+ run_inputs=self._inputs,
250
+ graph=VCPGraph(nodes=self._nodes, edges=self._edges),
251
+ execution_policy=self.execution_policy,
252
+ )
253
+ return parse_definition(serialize(definition))
254
+
255
+ def validate(self) -> None:
256
+ self.build()
257
+
258
+ def inspect(self) -> Context:
259
+ return inspect_definition(self.build())
260
+
261
+ def to_json(self) -> str:
262
+ return serialize(self.build())
@@ -0,0 +1,21 @@
1
+ """Typed references compile directly into VCP ValueSource objects."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from pydantic import JsonValue
6
+
7
+ from openverify.model import ArtifactRef, Constant, ExternalReference, JsonSchema, ValueSource
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class Ref:
12
+ source: ValueSource
13
+ schema: JsonSchema
14
+
15
+
16
+ def constant(value: JsonValue, schema: JsonSchema = True) -> Ref:
17
+ return Ref(Constant(value=value), schema)
18
+
19
+
20
+ def external(reference: ArtifactRef) -> Ref:
21
+ return Ref(ExternalReference(reference=reference), {"type": "object"})
openverify/cli.py ADDED
@@ -0,0 +1,58 @@
1
+ """Non-normative local convenience commands using an explicit demo registry."""
2
+
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+ from typing import cast
7
+
8
+ from openverify.adapters.demo import demo_registry
9
+ from openverify.engine import Engine
10
+ from openverify.errors import ProtocolError
11
+ from openverify.inspection import inspect_definition
12
+ from openverify.model import Context
13
+ from openverify.serialization import decode, serialize
14
+ from openverify.validation import parse_definition, parse_object
15
+
16
+
17
+ def main() -> int:
18
+ parser = argparse.ArgumentParser(description="Draft Openverify local SDK/reference commands")
19
+ commands = parser.add_subparsers(dest="command", required=True)
20
+ validate = commands.add_parser("validate")
21
+ validate.add_argument("file", type=Path)
22
+ validate.add_argument("--type", default="verification-definition")
23
+ inspect = commands.add_parser("inspect")
24
+ inspect.add_argument("file", type=Path)
25
+ run = commands.add_parser("run")
26
+ run.add_argument("file", type=Path)
27
+ run.add_argument("--run-inputs", type=Path, required=True)
28
+ run.add_argument("--opportunity-id", required=True)
29
+ run.add_argument("--output", type=Path)
30
+ args = parser.parse_args()
31
+ try:
32
+ data = cast(Path, args.file).read_bytes()
33
+ if args.command == "validate":
34
+ parse_object(args.type, data)
35
+ print(json.dumps({"valid": True}))
36
+ elif args.command == "inspect":
37
+ print(json.dumps(inspect_definition(parse_definition(data)), indent=2))
38
+ else:
39
+ run_inputs = decode(cast(Path, args.run_inputs).read_bytes())
40
+ if not isinstance(run_inputs, dict):
41
+ raise ProtocolError("PROTOCOL_STRUCTURE", "Run inputs must be a mapping")
42
+ record = Engine(demo_registry()).run(
43
+ parse_definition(data),
44
+ cast(Context, run_inputs),
45
+ opportunity_id=args.opportunity_id,
46
+ )
47
+ output = serialize(record)
48
+ if args.output:
49
+ cast(Path, args.output).write_text(output + "\n", encoding="utf-8")
50
+ else:
51
+ print(output)
52
+ return 0
53
+ except ProtocolError as exc:
54
+ print(json.dumps(exc.as_dict()))
55
+ return 1
56
+ except OSError as exc:
57
+ print(json.dumps({"code": "IO_ERROR", "message": str(exc)}))
58
+ return 1
@@ -0,0 +1,21 @@
1
+ """Run a portable manifest using this SDK; no engine execution is required."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from openverify.errors import ProtocolError
7
+ from openverify.validation import parse_object
8
+
9
+
10
+ def check_manifest(directory: Path) -> list[str]:
11
+ failures: list[str] = []
12
+ manifest = json.loads((directory / "manifest.json").read_text(encoding="utf-8"))
13
+ for case in manifest["cases"]:
14
+ try:
15
+ parse_object(case["object_type"], (directory / case["input"]).read_bytes())
16
+ code = None
17
+ except ProtocolError as exc:
18
+ code = exc.code
19
+ if code != case.get("error_code"):
20
+ failures.append(case["id"])
21
+ return failures
@@ -0,0 +1,97 @@
1
+ {
2
+ "protocol": "draft",
3
+ "id": "standard-draft",
4
+ "version": "0.1.0",
5
+ "flattened_statuses": [
6
+ "PASS",
7
+ "FAIL",
8
+ "UNKNOWN",
9
+ "ERROR",
10
+ "SKIP"
11
+ ],
12
+ "severity_domain": {
13
+ "id": "standard-draft",
14
+ "levels": [
15
+ {
16
+ "label": "INFO",
17
+ "rank": 10
18
+ },
19
+ {
20
+ "label": "WARNING",
21
+ "rank": 20
22
+ },
23
+ {
24
+ "label": "ERROR",
25
+ "rank": 30
26
+ },
27
+ {
28
+ "label": "BLOCKER",
29
+ "rank": 40
30
+ }
31
+ ]
32
+ },
33
+ "aggregators": [
34
+ "ALL",
35
+ "ANY",
36
+ "MAJORITY",
37
+ "MIN_SCORE",
38
+ "MAX_SCORE",
39
+ "AVERAGE",
40
+ "WEIGHTED_AVERAGE",
41
+ "WORST_SEVERITY"
42
+ ],
43
+ "admission_policies": [
44
+ "ALWAYS",
45
+ "PROBABILITY"
46
+ ],
47
+ "decisions": [
48
+ "CONTINUE",
49
+ "STOP_SUCCESS",
50
+ "STOP_FAILURE",
51
+ "ROUTE",
52
+ "RETRY",
53
+ "DEFER"
54
+ ],
55
+ "resource_dimensions": [
56
+ {
57
+ "id": "wall_time",
58
+ "unit": "s",
59
+ "description": "Elapsed wall-clock duration"
60
+ },
61
+ {
62
+ "id": "money",
63
+ "unit": "currency",
64
+ "description": "Monetary usage; measurement supplies the currency unit"
65
+ },
66
+ {
67
+ "id": "input_tokens",
68
+ "unit": "token",
69
+ "description": "Input tokens consumed"
70
+ },
71
+ {
72
+ "id": "output_tokens",
73
+ "unit": "token",
74
+ "description": "Output tokens consumed"
75
+ },
76
+ {
77
+ "id": "cpu_time",
78
+ "unit": "s",
79
+ "description": "CPU time consumed"
80
+ },
81
+ {
82
+ "id": "gpu_time",
83
+ "unit": "s",
84
+ "description": "GPU time consumed"
85
+ },
86
+ {
87
+ "id": "human_time",
88
+ "unit": "s",
89
+ "description": "Reviewer time consumed"
90
+ },
91
+ {
92
+ "id": "network_bytes",
93
+ "unit": "byte",
94
+ "description": "Network bytes transferred"
95
+ }
96
+ ]
97
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:verification:draft:activation",
4
+ "description": "Typed explicit executable dataflow; see spec/draft/dataflow.md.",
5
+ "type": "object",
6
+ "properties": {
7
+ "outcome": {
8
+ "type": "string",
9
+ "enum": [
10
+ "ACTIVATE",
11
+ "DO_NOT_ACTIVATE"
12
+ ]
13
+ },
14
+ "reason": {
15
+ "type": "string",
16
+ "minLength": 1
17
+ }
18
+ },
19
+ "required": [
20
+ "outcome",
21
+ "reason"
22
+ ],
23
+ "additionalProperties": false
24
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:verification:draft:admission",
4
+ "description": "Typed explicit executable dataflow; see spec/draft/dataflow.md.",
5
+ "type": "object",
6
+ "properties": {
7
+ "outcome": {
8
+ "type": "string",
9
+ "enum": [
10
+ "RUN",
11
+ "SKIP",
12
+ "DEFER"
13
+ ]
14
+ },
15
+ "reason": {
16
+ "type": "string",
17
+ "minLength": 1
18
+ }
19
+ },
20
+ "required": [
21
+ "outcome",
22
+ "reason"
23
+ ],
24
+ "additionalProperties": false
25
+ }