@fluxpointstudios/orynq-sdk-process-trace 0.2.0 → 0.3.0
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.
- package/LICENSE +21 -21
- package/dist/index.cjs +574 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +573 -9
- package/dist/index.d.ts +573 -9
- package/dist/index.js +562 -9
- package/dist/index.js.map +1 -1
- package/package.json +17 -3
- package/src/__tests__/bundle.test.ts +942 -860
- package/src/__tests__/governance-round4.test.ts +149 -0
- package/src/__tests__/governance.test.ts +382 -0
- package/src/__tests__/hardening-round2.test.ts +173 -0
- package/src/__tests__/hardening-round3.test.ts +208 -0
- package/src/__tests__/integration.test.ts +611 -611
- package/src/__tests__/merkle.test.ts +756 -756
- package/src/__tests__/model-manifest.test.ts +136 -0
- package/src/__tests__/rolling-hash.test.ts +622 -622
- package/src/__tests__/trace-builder.test.ts +1012 -1012
- package/src/__tests__/types.test.ts +420 -414
- package/src/bundle.ts +1004 -810
- package/src/disclosure.ts +527 -527
- package/src/governance.ts +711 -0
- package/src/index.ts +334 -265
- package/src/manifest.ts +687 -687
- package/src/merkle.ts +428 -428
- package/src/model-manifest.ts +0 -0
- package/src/rolling-hash.ts +375 -366
- package/src/trace-builder.ts +791 -725
- package/src/types.ts +713 -522
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Tests for pre-execution model-manifest pinning (issue #59).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
createTrace,
|
|
8
|
+
addSpan,
|
|
9
|
+
addEvent,
|
|
10
|
+
closeSpan,
|
|
11
|
+
finalizeTrace,
|
|
12
|
+
verifyBundle,
|
|
13
|
+
computeModelManifestHash,
|
|
14
|
+
validateModelManifest,
|
|
15
|
+
manifestFromHuggingFace,
|
|
16
|
+
manifestFromOpenAI,
|
|
17
|
+
manifestFromAnthropic,
|
|
18
|
+
} from "../index.js";
|
|
19
|
+
import type { ModelManifest } from "../index.js";
|
|
20
|
+
|
|
21
|
+
async function sampleManifest(): Promise<ModelManifest> {
|
|
22
|
+
return manifestFromHuggingFace({
|
|
23
|
+
modelId: "meta-llama/Llama-3.1-8B",
|
|
24
|
+
revision: "0e9e39f249a16976918f6564b8830bc894c89659",
|
|
25
|
+
tokenizerHash: "sha256:deadbeef",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("model-manifest builders", () => {
|
|
30
|
+
it("produces a deterministic manifestHash for identical inputs", async () => {
|
|
31
|
+
const a = await manifestFromHuggingFace({ modelId: "m", revision: "abc" });
|
|
32
|
+
const b = await manifestFromHuggingFace({ modelId: "m", revision: "abc" });
|
|
33
|
+
expect(await computeModelManifestHash(a)).toBe(await computeModelManifestHash(b));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("changes the manifestHash when the revision changes", async () => {
|
|
37
|
+
const a = await manifestFromHuggingFace({ modelId: "m", revision: "abc" });
|
|
38
|
+
const c = await manifestFromHuggingFace({ modelId: "m", revision: "def" });
|
|
39
|
+
expect(await computeModelManifestHash(a)).not.toBe(await computeModelManifestHash(c));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("records the framework and identity for OpenAI / Anthropic", async () => {
|
|
43
|
+
const openai = await manifestFromOpenAI({ model: "gpt-4o", snapshotId: "gpt-4o-2024-08-06" });
|
|
44
|
+
expect(openai.framework).toBe("openai");
|
|
45
|
+
expect(openai.modelId).toBe("gpt-4o");
|
|
46
|
+
expect(openai.modelHash.startsWith("sha256:")).toBe(true);
|
|
47
|
+
|
|
48
|
+
const anthropic = await manifestFromAnthropic({
|
|
49
|
+
model: "claude-3-5-sonnet",
|
|
50
|
+
snapshotId: "claude-3-5-sonnet-20241022",
|
|
51
|
+
});
|
|
52
|
+
expect(anthropic.framework).toBe("anthropic");
|
|
53
|
+
expect(anthropic.revision).toBe("claude-3-5-sonnet-20241022");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("hashes a raw system prompt into systemPromptHash", async () => {
|
|
57
|
+
const m = await manifestFromHuggingFace({
|
|
58
|
+
modelId: "m",
|
|
59
|
+
systemPrompt: "You are a helpful assistant.",
|
|
60
|
+
});
|
|
61
|
+
expect(m.systemPromptHash?.startsWith("sha256:")).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("validateModelManifest requires a modelHash", () => {
|
|
65
|
+
expect(() => validateModelManifest({ modelHash: "" } as ModelManifest)).toThrow(/modelHash/);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("createTrace manifest pinning", () => {
|
|
70
|
+
it("pins the manifest hash at creation and freezes the manifest", async () => {
|
|
71
|
+
const manifest = await sampleManifest();
|
|
72
|
+
const run = await createTrace({ agentId: "agent-1", manifest });
|
|
73
|
+
|
|
74
|
+
expect(run.modelManifestHash).toBeDefined();
|
|
75
|
+
expect(run.modelManifestHash).toBe(await computeModelManifestHash(manifest));
|
|
76
|
+
expect(run.modelManifest).toBe(manifest);
|
|
77
|
+
|
|
78
|
+
// Immutability: mutating the pinned manifest throws (frozen).
|
|
79
|
+
expect(() => {
|
|
80
|
+
(run.modelManifest as unknown as Record<string, unknown>).modelHash = "tampered";
|
|
81
|
+
}).toThrow();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("rejects strict createTrace without a manifest", async () => {
|
|
85
|
+
await expect(createTrace({ agentId: "agent-1", strict: true })).rejects.toThrow(
|
|
86
|
+
/strict mode requires/i
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("finalizeTrace manifest enforcement", () => {
|
|
92
|
+
it("carries the manifest onto the bundle and public view, and verifies", async () => {
|
|
93
|
+
const manifest = await manifestFromOpenAI({
|
|
94
|
+
model: "gpt-4o",
|
|
95
|
+
snapshotId: "gpt-4o-2024-08-06",
|
|
96
|
+
});
|
|
97
|
+
const run = await createTrace({ agentId: "agent-1", manifest, strict: true });
|
|
98
|
+
const span = addSpan(run, { name: "infer", visibility: "public" });
|
|
99
|
+
await addEvent(run, span.id, {
|
|
100
|
+
kind: "observation",
|
|
101
|
+
observation: "ok",
|
|
102
|
+
visibility: "public",
|
|
103
|
+
});
|
|
104
|
+
await closeSpan(run, span.id);
|
|
105
|
+
|
|
106
|
+
const bundle = await finalizeTrace(run);
|
|
107
|
+
|
|
108
|
+
expect(bundle.modelManifestHash).toBe(run.modelManifestHash);
|
|
109
|
+
expect(bundle.modelManifest?.framework).toBe("openai");
|
|
110
|
+
expect(bundle.publicView.modelManifestHash).toBe(run.modelManifestHash);
|
|
111
|
+
|
|
112
|
+
const result = await verifyBundle(bundle);
|
|
113
|
+
expect(result.valid).toBe(true);
|
|
114
|
+
expect(result.errors).toHaveLength(0);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("refuses to finalize a strict run with no manifest", async () => {
|
|
118
|
+
const run = await createTrace({ agentId: "agent-1" });
|
|
119
|
+
run.strict = true; // simulate a run that should have been pinned
|
|
120
|
+
await expect(finalizeTrace(run)).rejects.toThrow(/strict mode requires a model manifest/i);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("warns (warn-only path) when finalizing an unpinned non-strict run", async () => {
|
|
124
|
+
const run = await createTrace({ agentId: "agent-1" });
|
|
125
|
+
const span = addSpan(run, { name: "s", visibility: "public" });
|
|
126
|
+
await addEvent(run, span.id, { kind: "observation", observation: "ok", visibility: "public" });
|
|
127
|
+
await closeSpan(run, span.id);
|
|
128
|
+
|
|
129
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
|
130
|
+
const bundle = await finalizeTrace(run);
|
|
131
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
132
|
+
warnSpy.mockRestore();
|
|
133
|
+
|
|
134
|
+
expect(bundle.modelManifestHash).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
});
|