ski-schemas 0.1.0__tar.gz
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.
- ski_schemas-0.1.0/PKG-INFO +37 -0
- ski_schemas-0.1.0/README.md +20 -0
- ski_schemas-0.1.0/pyproject.toml +30 -0
- ski_schemas-0.1.0/setup.cfg +4 -0
- ski_schemas-0.1.0/src/ski_schemas/__init__.py +37 -0
- ski_schemas-0.1.0/src/ski_schemas/envelope.py +159 -0
- ski_schemas-0.1.0/src/ski_schemas/measurement.py +37 -0
- ski_schemas-0.1.0/src/ski_schemas/py.typed +0 -0
- ski_schemas-0.1.0/src/ski_schemas/transcript.py +104 -0
- ski_schemas-0.1.0/src/ski_schemas.egg-info/PKG-INFO +37 -0
- ski_schemas-0.1.0/src/ski_schemas.egg-info/SOURCES.txt +12 -0
- ski_schemas-0.1.0/src/ski_schemas.egg-info/dependency_links.txt +1 -0
- ski_schemas-0.1.0/src/ski_schemas.egg-info/requires.txt +1 -0
- ski_schemas-0.1.0/src/ski_schemas.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ski-schemas
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SKI Framework wire models: verdict envelope, signed LLM transcript, measurement record. One definition, no drift (RFC 0003).
|
|
5
|
+
Author: KpiFinity Inc.
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/kpifinity/ski-framework
|
|
8
|
+
Project-URL: Documentation, https://kpifinity.github.io/ski-framework/
|
|
9
|
+
Project-URL: Issues, https://github.com/kpifinity/ski-framework/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: pydantic<3,>=2.5
|
|
17
|
+
|
|
18
|
+
# ski-schemas
|
|
19
|
+
|
|
20
|
+
> Install from PyPI: `pip install ski-schemas`
|
|
21
|
+
|
|
22
|
+
The [SKI Framework](https://github.com/kpifinity/ski-framework) wire
|
|
23
|
+
models — the v3 verdict envelope, the signed LLM transcript, and the
|
|
24
|
+
measurement record — extracted into one dependency-light package per
|
|
25
|
+
RFC 0003. The server, the `ski-sdk` client, and the conformance suite
|
|
26
|
+
all import these models, so the wire contract has exactly one
|
|
27
|
+
definition.
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from ski_schemas import V3VerdictEnvelope
|
|
31
|
+
|
|
32
|
+
envelope = V3VerdictEnvelope.model_validate_json(raw)
|
|
33
|
+
print(envelope.verdict, envelope.model_provenance.kg_version_hash)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Versioned independently of the framework (it carries a wire contract,
|
|
37
|
+
not a runtime). Dependencies: `pydantic` only.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# ski-schemas
|
|
2
|
+
|
|
3
|
+
> Install from PyPI: `pip install ski-schemas`
|
|
4
|
+
|
|
5
|
+
The [SKI Framework](https://github.com/kpifinity/ski-framework) wire
|
|
6
|
+
models — the v3 verdict envelope, the signed LLM transcript, and the
|
|
7
|
+
measurement record — extracted into one dependency-light package per
|
|
8
|
+
RFC 0003. The server, the `ski-sdk` client, and the conformance suite
|
|
9
|
+
all import these models, so the wire contract has exactly one
|
|
10
|
+
definition.
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from ski_schemas import V3VerdictEnvelope
|
|
14
|
+
|
|
15
|
+
envelope = V3VerdictEnvelope.model_validate_json(raw)
|
|
16
|
+
print(envelope.verdict, envelope.model_provenance.kg_version_hash)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Versioned independently of the framework (it carries a wire contract,
|
|
20
|
+
not a runtime). Dependencies: `pydantic` only.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ski-schemas"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "SKI Framework wire models: verdict envelope, signed LLM transcript, measurement record. One definition, no drift (RFC 0003)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
authors = [{name = "KpiFinity Inc."}]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
]
|
|
19
|
+
dependencies = ["pydantic>=2.5,<3"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/kpifinity/ski-framework"
|
|
23
|
+
Documentation = "https://kpifinity.github.io/ski-framework/"
|
|
24
|
+
Issues = "https://github.com/kpifinity/ski-framework/issues"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["src"]
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.package-data]
|
|
30
|
+
ski_schemas = ["py.typed"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""ski-schemas — the SKI Framework wire models, defined once (RFC 0003 PR 1).
|
|
2
|
+
|
|
3
|
+
The verdict envelope, the signed LLM transcript, and the measurement
|
|
4
|
+
record are normative (spec v3.0 §4, §6). This dependency-light package
|
|
5
|
+
is the single source of truth the server, the ski-sdk client, and the
|
|
6
|
+
conformance suite all import — one definition, no drift.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .envelope import (
|
|
10
|
+
FormalizableAssertion,
|
|
11
|
+
KGCitation,
|
|
12
|
+
KGCitationRole,
|
|
13
|
+
ModelProvenance,
|
|
14
|
+
V3Verdict,
|
|
15
|
+
V3VerdictEnvelope,
|
|
16
|
+
VerifierResult,
|
|
17
|
+
VerifierStatus,
|
|
18
|
+
)
|
|
19
|
+
from .measurement import MeasurementRecord
|
|
20
|
+
from .transcript import LLMTranscript, hash_pair, signing_message
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"FormalizableAssertion",
|
|
26
|
+
"KGCitation",
|
|
27
|
+
"KGCitationRole",
|
|
28
|
+
"LLMTranscript",
|
|
29
|
+
"MeasurementRecord",
|
|
30
|
+
"ModelProvenance",
|
|
31
|
+
"V3Verdict",
|
|
32
|
+
"V3VerdictEnvelope",
|
|
33
|
+
"VerifierResult",
|
|
34
|
+
"VerifierStatus",
|
|
35
|
+
"hash_pair",
|
|
36
|
+
"signing_message",
|
|
37
|
+
]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Pydantic models for the v3 verdict envelope per spec v3.0 §4.
|
|
2
|
+
|
|
3
|
+
* §4.1 — the five-verdict taxonomy (preserved from v2.1).
|
|
4
|
+
* §4.2 — required + optional envelope fields.
|
|
5
|
+
* §4.3 — KG citations with node_id / version / role.
|
|
6
|
+
* §4.4 — formalizable assertions the symbolic verifier can check.
|
|
7
|
+
* §4.5 — verifier result with status enum.
|
|
8
|
+
* §4.6 — model provenance (six required hash + id fields).
|
|
9
|
+
|
|
10
|
+
These models are the contract every v3 verdict honours. They MUST
|
|
11
|
+
round-trip through JSON without loss; the runtime's envelope tests
|
|
12
|
+
enforce this. This module is the single source of truth shared by the
|
|
13
|
+
server, the SDK, and the conformance suite (RFC 0003 PR 1).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from typing import Any, Dict, List, Optional, Union
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
22
|
+
|
|
23
|
+
# ---- Verdict taxonomy (§4.1) ----
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class V3Verdict(str, Enum):
|
|
27
|
+
"""The five-verdict taxonomy, preserved from v2.1."""
|
|
28
|
+
|
|
29
|
+
CLEAR = "CLEAR"
|
|
30
|
+
FLAG = "FLAG"
|
|
31
|
+
NULL_UNMAPPED = "NULL_UNMAPPED"
|
|
32
|
+
NULL_STALE = "NULL_STALE"
|
|
33
|
+
DISCRETIONARY = "DISCRETIONARY"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---- Verifier status (§4.5) ----
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class VerifierStatus(str, Enum):
|
|
40
|
+
"""Status returned by the Symbolic Verifier (spec §5.3)."""
|
|
41
|
+
|
|
42
|
+
AGREED = "AGREED"
|
|
43
|
+
LLM_CONTRADICTION = "LLM_CONTRADICTION"
|
|
44
|
+
NEURO_SYMBOLIC_DIVERGENCE = "NEURO_SYMBOLIC_DIVERGENCE"
|
|
45
|
+
UNVERIFIABLE = "UNVERIFIABLE"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---- KG citation (§4.3) ----
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class KGCitationRole(str, Enum):
|
|
52
|
+
"""The role a cited KG node played in the verdict."""
|
|
53
|
+
|
|
54
|
+
OBLIGATION = "obligation"
|
|
55
|
+
DEFINITION_RESOLVED = "definition_resolved"
|
|
56
|
+
EXEMPTION_CONSIDERED = "exemption_considered"
|
|
57
|
+
PRECEDENT_REFERENCED = "precedent_referenced"
|
|
58
|
+
JURISDICTION_MATCHED = "jurisdiction_matched"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class KGCitation(BaseModel):
|
|
62
|
+
"""A single KG node the LLM cited as informing the verdict."""
|
|
63
|
+
|
|
64
|
+
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
65
|
+
|
|
66
|
+
node_id: str
|
|
67
|
+
version: str
|
|
68
|
+
role: KGCitationRole
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ---- Formalizable assertion (§4.4) ----
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class FormalizableAssertion(BaseModel):
|
|
75
|
+
"""A structured assertion the symbolic verifier can mechanically check."""
|
|
76
|
+
|
|
77
|
+
model_config = ConfigDict(extra="forbid")
|
|
78
|
+
|
|
79
|
+
predicate: str = Field(..., description="Predicate type, e.g. 'must_not_exceed'.")
|
|
80
|
+
metric: str = Field(..., description="Dotted-path into the measurement object.")
|
|
81
|
+
value: Union[float, int, str, List[Any], None] = None
|
|
82
|
+
observed: Union[float, int, str, List[Any], None] = None
|
|
83
|
+
satisfied: bool
|
|
84
|
+
obligation_id: str = Field(..., description="The KG obligation this assertion checks.")
|
|
85
|
+
window_seconds: Optional[int] = Field(
|
|
86
|
+
default=None,
|
|
87
|
+
ge=1,
|
|
88
|
+
description=(
|
|
89
|
+
"Time-window length in seconds for stateful predicates "
|
|
90
|
+
"(e.g. 86400 for a 24h rolling average). None for stateless "
|
|
91
|
+
"predicates."
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ---- Verifier result (§4.5) ----
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class VerifierResult(BaseModel):
|
|
100
|
+
"""The symbolic verifier's per-assertion result."""
|
|
101
|
+
|
|
102
|
+
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
103
|
+
|
|
104
|
+
status: VerifierStatus
|
|
105
|
+
checked_assertions: int = Field(default=0, ge=0)
|
|
106
|
+
divergences: List[str] = Field(default_factory=list)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ---- Model provenance (§4.6) ----
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ModelProvenance(BaseModel):
|
|
113
|
+
"""Inference provenance metadata required for v3 replay.
|
|
114
|
+
|
|
115
|
+
All hash values MUST be lowercase hex prefixed with ``sha256:`` per
|
|
116
|
+
spec §4.6. The Pydantic layer enforces the prefix.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
# model_weight_hash collides with Pydantic's reserved model_
|
|
120
|
+
# namespace; opt out explicitly. The field name is normative per
|
|
121
|
+
# spec §4.6 and cannot be renamed.
|
|
122
|
+
model_config = ConfigDict(extra="forbid", protected_namespaces=())
|
|
123
|
+
|
|
124
|
+
model_weight_hash: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
125
|
+
kg_version_hash: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
126
|
+
prompt_template_id: str = Field(..., description="Stable id, e.g. 'ski.v3.evaluate.1'.")
|
|
127
|
+
prompt_template_hash: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
128
|
+
decoder_seed: int = Field(..., ge=0)
|
|
129
|
+
structured_grammar_hash: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ---- Verdict envelope (§4.2) ----
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class V3VerdictEnvelope(BaseModel):
|
|
136
|
+
"""The complete v3 verdict envelope.
|
|
137
|
+
|
|
138
|
+
Every required field is enforced at the Pydantic layer. The
|
|
139
|
+
envelope MUST round-trip through JSON without loss; that property
|
|
140
|
+
is what makes the audit ledger's hash chain meaningful.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
# model_provenance collides with Pydantic's reserved model_
|
|
144
|
+
# namespace; opt out explicitly. The field name is normative per
|
|
145
|
+
# spec §4.2 and cannot be renamed.
|
|
146
|
+
model_config = ConfigDict(use_enum_values=True, extra="forbid", protected_namespaces=())
|
|
147
|
+
|
|
148
|
+
verdict: V3Verdict
|
|
149
|
+
reasoning: str
|
|
150
|
+
kg_citations: List[KGCitation] = Field(default_factory=list)
|
|
151
|
+
formalizable_assertions: List[FormalizableAssertion] = Field(default_factory=list)
|
|
152
|
+
verifier_result: VerifierResult
|
|
153
|
+
model_provenance: ModelProvenance
|
|
154
|
+
transcript_ref: str = Field(..., description="Pointer to the LLM transcript in the ledger.")
|
|
155
|
+
|
|
156
|
+
# Optional fields per spec §4.2 + §5.4 + §5.6.
|
|
157
|
+
human_attestation: Optional[Dict[str, Any]] = None
|
|
158
|
+
notes: List[str] = Field(default_factory=list)
|
|
159
|
+
verdict_path: Optional[str] = Field(default=None, description="Per §5.6: 'fast' or None.")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Measurement record — the input shape of ``POST /api/evaluate`` (RFC 0003)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MeasurementRecord(BaseModel):
|
|
11
|
+
"""Measurement input for /api/evaluate.
|
|
12
|
+
|
|
13
|
+
The v3 evaluator consumes the full measurement; the LLM scopes the KG
|
|
14
|
+
snapshot to the measurement's jurisdiction and effective date. PR 10b
|
|
15
|
+
passes the full KG as the snapshot. Jurisdiction-scoped snapshots are a
|
|
16
|
+
follow-up.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
measurement_id: str = Field(..., description="Stable id for replay correlation.")
|
|
20
|
+
timestamp: str = Field(..., description="ISO-8601 UTC.")
|
|
21
|
+
subject: str = Field(..., description="Subject token (e.g. data source identifier).")
|
|
22
|
+
measurement: Dict[str, Any] = Field(
|
|
23
|
+
default_factory=dict,
|
|
24
|
+
description="Structured measurement values keyed by metric name.",
|
|
25
|
+
)
|
|
26
|
+
jurisdiction: Optional[str] = Field(
|
|
27
|
+
default=None,
|
|
28
|
+
description=(
|
|
29
|
+
"Tenant-declared jurisdiction (e.g. 'us-ca', 'eu', 'global'). When set, "
|
|
30
|
+
"the KG is scoped to obligations whose jurisdiction matches or is "
|
|
31
|
+
"universal ('global', '*'). None means 'no restriction' — all rules "
|
|
32
|
+
"effective at the measurement's timestamp are sent to the LLM."
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ["MeasurementRecord"]
|
|
File without changes
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Provider-neutral LLM transcript record (spec v3.0 §6).
|
|
2
|
+
|
|
3
|
+
An :class:`LLMTranscript` captures the canonical input/output pair of a
|
|
4
|
+
single LLM evaluation call so the verdict can be independently replayed by
|
|
5
|
+
an auditor. The transcript is **provider-neutral by design**: no
|
|
6
|
+
Anthropic Messages blob, no OpenAI ChatCompletion shape, no Ollama
|
|
7
|
+
response envelope leaks into the audit trail. Each backend is responsible
|
|
8
|
+
for normalising its own request / response into the canonical strings the
|
|
9
|
+
transcript records.
|
|
10
|
+
|
|
11
|
+
Fields (spec §6.2):
|
|
12
|
+
|
|
13
|
+
* ``request_canonical`` — the prompt as actually sent to the LLM,
|
|
14
|
+
UTF-8 text. For chat-style APIs this is the formatted single string
|
|
15
|
+
the backend constructed; for raw-completion APIs it is the prompt
|
|
16
|
+
itself.
|
|
17
|
+
* ``request_hash`` — sha256 of ``request_canonical``.
|
|
18
|
+
* ``response_canonical`` — the structured-output dict matching
|
|
19
|
+
the framework's ``RESPONSE_GRAMMAR``. Vendor-specific
|
|
20
|
+
fields MUST be filtered out before canonicalisation.
|
|
21
|
+
* ``response_hash`` — sha256 of the canonical JSON serialisation of
|
|
22
|
+
``response_canonical`` (sort_keys, compact separators).
|
|
23
|
+
* ``signature_hex`` — ed25519 signature over
|
|
24
|
+
``request_hash || "|" || response_hash`` produced by the runtime's
|
|
25
|
+
the runtime's ``TranscriptSigner``.
|
|
26
|
+
* ``signing_key_id`` — sha256 of the public-key bytes; auditors look
|
|
27
|
+
up the corresponding public key to verify.
|
|
28
|
+
* ``backend_name`` — opaque observability tag (e.g. ``"fake-llm"``,
|
|
29
|
+
``"ollama-v3:qwen2.5-7b"``). Not part of the audit contract.
|
|
30
|
+
* ``backend_metadata`` — opaque per-backend dict. Use freely for
|
|
31
|
+
observability; NEVER for contract.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import hashlib
|
|
37
|
+
import json
|
|
38
|
+
from datetime import datetime
|
|
39
|
+
from typing import Any, Dict, Tuple
|
|
40
|
+
|
|
41
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def canonical_request(request_text: str) -> bytes:
|
|
45
|
+
"""Canonical request bytes — UTF-8 encoding of the request string."""
|
|
46
|
+
return request_text.encode("utf-8")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def canonical_response(response: Dict[str, Any]) -> bytes:
|
|
50
|
+
"""Canonical response bytes — sorted-keys compact JSON, UTF-8.
|
|
51
|
+
|
|
52
|
+
Vendor-specific fields are NOT filtered here — callers MUST construct
|
|
53
|
+
``response`` from the structured-output portion of the LLM reply only.
|
|
54
|
+
The the framework's ``RESPONSE_GRAMMAR`` is the contract.
|
|
55
|
+
"""
|
|
56
|
+
return json.dumps(response, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def hash_pair(*, request_text: str, response: Dict[str, Any]) -> Tuple[str, str]:
|
|
60
|
+
"""Compute ``(request_hash, response_hash)`` as sha256-prefixed hex."""
|
|
61
|
+
req_hash = "sha256:" + hashlib.sha256(canonical_request(request_text)).hexdigest()
|
|
62
|
+
resp_hash = "sha256:" + hashlib.sha256(canonical_response(response)).hexdigest()
|
|
63
|
+
return req_hash, resp_hash
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def signing_message(*, request_hash: str, response_hash: str) -> bytes:
|
|
67
|
+
"""Bytes to be signed — concatenation of the two hashes with a separator."""
|
|
68
|
+
return f"{request_hash}|{response_hash}".encode("ascii")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class LLMTranscript(BaseModel):
|
|
72
|
+
"""A signed transcript of one LLM evaluation call.
|
|
73
|
+
|
|
74
|
+
Round-trips through JSON; auditors deserialise from the ledger and call
|
|
75
|
+
:func:`ski_model.v3.signing.verify_signature` to prove integrity.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
model_config = ConfigDict(extra="forbid")
|
|
79
|
+
|
|
80
|
+
transcript_id: str = Field(..., description="Globally unique id for this transcript.")
|
|
81
|
+
request_canonical: str = Field(..., description="The prompt as actually sent.")
|
|
82
|
+
request_hash: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
83
|
+
response_canonical: Dict[str, Any] = Field(
|
|
84
|
+
..., description="Structured output matching RESPONSE_GRAMMAR."
|
|
85
|
+
)
|
|
86
|
+
response_hash: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
87
|
+
signature_hex: str = Field(..., description="Ed25519 signature, hex-encoded.")
|
|
88
|
+
signing_key_id: str = Field(..., pattern=r"^sha256:[0-9a-f]+$")
|
|
89
|
+
backend_name: str = Field(..., description="Observability tag for the LLM backend; not contract.")
|
|
90
|
+
backend_metadata: Dict[str, Any] = Field(
|
|
91
|
+
default_factory=dict,
|
|
92
|
+
description="Opaque backend-specific metadata. Never contract.",
|
|
93
|
+
)
|
|
94
|
+
started_at: datetime
|
|
95
|
+
completed_at: datetime
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
__all__ = [
|
|
99
|
+
"LLMTranscript",
|
|
100
|
+
"canonical_request",
|
|
101
|
+
"canonical_response",
|
|
102
|
+
"hash_pair",
|
|
103
|
+
"signing_message",
|
|
104
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ski-schemas
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SKI Framework wire models: verdict envelope, signed LLM transcript, measurement record. One definition, no drift (RFC 0003).
|
|
5
|
+
Author: KpiFinity Inc.
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/kpifinity/ski-framework
|
|
8
|
+
Project-URL: Documentation, https://kpifinity.github.io/ski-framework/
|
|
9
|
+
Project-URL: Issues, https://github.com/kpifinity/ski-framework/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: pydantic<3,>=2.5
|
|
17
|
+
|
|
18
|
+
# ski-schemas
|
|
19
|
+
|
|
20
|
+
> Install from PyPI: `pip install ski-schemas`
|
|
21
|
+
|
|
22
|
+
The [SKI Framework](https://github.com/kpifinity/ski-framework) wire
|
|
23
|
+
models — the v3 verdict envelope, the signed LLM transcript, and the
|
|
24
|
+
measurement record — extracted into one dependency-light package per
|
|
25
|
+
RFC 0003. The server, the `ski-sdk` client, and the conformance suite
|
|
26
|
+
all import these models, so the wire contract has exactly one
|
|
27
|
+
definition.
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from ski_schemas import V3VerdictEnvelope
|
|
31
|
+
|
|
32
|
+
envelope = V3VerdictEnvelope.model_validate_json(raw)
|
|
33
|
+
print(envelope.verdict, envelope.model_provenance.kg_version_hash)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Versioned independently of the framework (it carries a wire contract,
|
|
37
|
+
not a runtime). Dependencies: `pydantic` only.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/ski_schemas/__init__.py
|
|
4
|
+
src/ski_schemas/envelope.py
|
|
5
|
+
src/ski_schemas/measurement.py
|
|
6
|
+
src/ski_schemas/py.typed
|
|
7
|
+
src/ski_schemas/transcript.py
|
|
8
|
+
src/ski_schemas.egg-info/PKG-INFO
|
|
9
|
+
src/ski_schemas.egg-info/SOURCES.txt
|
|
10
|
+
src/ski_schemas.egg-info/dependency_links.txt
|
|
11
|
+
src/ski_schemas.egg-info/requires.txt
|
|
12
|
+
src/ski_schemas.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pydantic<3,>=2.5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ski_schemas
|