@velum-labs/routekit-eval-service 1.3.2 → 1.5.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/dist/adapters/eval-trial-accounting.d.ts +32 -0
- package/dist/adapters/eval-trial-accounting.js +135 -0
- package/dist/adapters/repository-agent-checkout.d.ts +23 -0
- package/dist/adapters/repository-agent-checkout.js +731 -0
- package/dist/adapters/repository-agent-model-tools.d.ts +51 -0
- package/dist/adapters/repository-agent-model-tools.js +215 -0
- package/dist/adapters/repository-agent-model-wire.d.ts +31 -0
- package/dist/adapters/repository-agent-model-wire.js +207 -0
- package/dist/adapters/repository-agent-model.d.ts +17 -0
- package/dist/adapters/repository-agent-model.js +614 -0
- package/dist/adapters/serving-request-http.d.ts +56 -0
- package/dist/adapters/serving-request-http.js +791 -0
- package/dist/candidate-trial-plan-protocol.d.ts +188 -0
- package/dist/candidate-trial-plan-protocol.js +6 -0
- package/dist/effect-api.d.ts +13 -1
- package/dist/effect-api.js +8 -1
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +5 -0
- package/dist/eval-trial-authority-protocol.d.ts +172 -0
- package/dist/eval-trial-authority-protocol.js +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +5 -1
- package/dist/layer-options.d.ts +20 -0
- package/dist/production-runner.js +44 -4
- package/dist/repository-agent-model-protocol.d.ts +51 -0
- package/dist/repository-agent-model-protocol.js +1 -0
- package/dist/repository-agent-protocol.d.ts +93 -0
- package/dist/repository-agent-protocol.js +1 -0
- package/dist/service.d.ts +9 -3
- package/dist/service.js +146 -154
- package/dist/services/candidate-trial-plan/service.d.ts +25 -0
- package/dist/services/candidate-trial-plan/service.js +815 -0
- package/dist/services/repository-agent/service.d.ts +40 -0
- package/dist/services/repository-agent/service.js +383 -0
- package/dist/services/serving-request/service.d.ts +27 -0
- package/dist/services/serving-request/service.js +215 -0
- package/dist/serving-request-protocol.d.ts +43 -0
- package/dist/serving-request-protocol.js +1 -0
- package/dist/test/candidate-trial-plan.test.d.ts +1 -0
- package/dist/test/candidate-trial-plan.test.js +628 -0
- package/dist/test/eval-trial-accounting.test.d.ts +1 -0
- package/dist/test/eval-trial-accounting.test.js +216 -0
- package/dist/test/production-runner.test.js +63 -6
- package/dist/test/repository-agent-model.test.d.ts +1 -0
- package/dist/test/repository-agent-model.test.js +840 -0
- package/dist/test/repository-agent.test.d.ts +1 -0
- package/dist/test/repository-agent.test.js +983 -0
- package/dist/test/service.test.js +5 -4
- package/dist/test/serving-request-http.test.d.ts +1 -0
- package/dist/test/serving-request-http.test.js +1207 -0
- package/dist/test/serving-request.test.d.ts +1 -0
- package/dist/test/serving-request.test.js +183 -0
- package/package.json +7 -5
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { EvalTrialBinding, EvalTrialClaim, EvalTrialGatewayObservation, EvalTrialRequestIntent, EvalTrialRequestSettlement } from "../eval-trial-authority-protocol.js";
|
|
2
|
+
declare const EvalTrialAccountingError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
3
|
+
readonly _tag: "EvalTrialAccountingError";
|
|
4
|
+
} & Readonly<A>;
|
|
5
|
+
export declare class EvalTrialAccountingError extends EvalTrialAccountingError_base<{
|
|
6
|
+
readonly detail: string;
|
|
7
|
+
}> {
|
|
8
|
+
}
|
|
9
|
+
/** One canonical observation projection shared by both G6 adapters/CLI owner. */
|
|
10
|
+
export declare const evalTrialGatewayObservationDigest: (observation: EvalTrialGatewayObservation) => string;
|
|
11
|
+
export type EvalTrialSettlementValidator = {
|
|
12
|
+
/**
|
|
13
|
+
* Returns cumulative actually reconciled upstream attempts. Throws a finite,
|
|
14
|
+
* sanitized typed error on unknown exposure or mismatched/stale proof.
|
|
15
|
+
*/
|
|
16
|
+
readonly accept: (input: {
|
|
17
|
+
readonly request: EvalTrialRequestIntent;
|
|
18
|
+
readonly observation: EvalTrialGatewayObservation;
|
|
19
|
+
readonly settlement: EvalTrialRequestSettlement;
|
|
20
|
+
}) => number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Shared transient validation, NOT a durable store or provider receipt issuer.
|
|
24
|
+
* Exactly one instance belongs to one already-claimed trial. There is no
|
|
25
|
+
* unknown->closed healing, retry admission, or authority fallback here.
|
|
26
|
+
*/
|
|
27
|
+
export declare const makeEvalTrialSettlementValidator: (input: {
|
|
28
|
+
readonly binding: EvalTrialBinding;
|
|
29
|
+
readonly bindingDigest: string;
|
|
30
|
+
readonly claim: EvalTrialClaim;
|
|
31
|
+
}) => EvalTrialSettlementValidator;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Data } from "effect";
|
|
3
|
+
export class EvalTrialAccountingError extends Data.TaggedError("EvalTrialAccountingError") {
|
|
4
|
+
}
|
|
5
|
+
const failure = (detail) => new EvalTrialAccountingError({ detail });
|
|
6
|
+
const isDigest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/u.test(value);
|
|
7
|
+
const isId = (value) => typeof value === "string" && /^[a-z0-9][a-z0-9._:/-]{0,255}$/iu.test(value);
|
|
8
|
+
/** One canonical observation projection shared by both G6 adapters/CLI owner. */
|
|
9
|
+
export const evalTrialGatewayObservationDigest = (observation) => createHash("sha256")
|
|
10
|
+
.update(JSON.stringify({
|
|
11
|
+
fetchInvocations: observation.fetchInvocations,
|
|
12
|
+
headersObserved: observation.headersObserved,
|
|
13
|
+
...(observation.httpStatus === undefined ? {} : { httpStatus: observation.httpStatus }),
|
|
14
|
+
...(observation.modelCallId === undefined ? {} : { modelCallId: observation.modelCallId }),
|
|
15
|
+
responseBytes: observation.responseBytes,
|
|
16
|
+
responseDigest: observation.responseDigest,
|
|
17
|
+
rawEof: observation.rawEof,
|
|
18
|
+
terminal: observation.terminal,
|
|
19
|
+
...(observation.responseId === undefined ? {} : { responseId: observation.responseId }),
|
|
20
|
+
failure: observation.failure
|
|
21
|
+
}))
|
|
22
|
+
.digest("hex");
|
|
23
|
+
/**
|
|
24
|
+
* Shared transient validation, NOT a durable store or provider receipt issuer.
|
|
25
|
+
* Exactly one instance belongs to one already-claimed trial. There is no
|
|
26
|
+
* unknown->closed healing, retry admission, or authority fallback here.
|
|
27
|
+
*/
|
|
28
|
+
export const makeEvalTrialSettlementValidator = (input) => {
|
|
29
|
+
const { binding, bindingDigest, claim } = input;
|
|
30
|
+
const providerAttemptIds = new Set();
|
|
31
|
+
const requestIds = new Set();
|
|
32
|
+
const ingressIds = new Set();
|
|
33
|
+
const modelCallIds = new Set();
|
|
34
|
+
const evidenceDigests = new Set([claim.initialEvidenceDigest]);
|
|
35
|
+
let lastRequestSequence = 0;
|
|
36
|
+
let lastEvidenceDigest = claim.initialEvidenceDigest;
|
|
37
|
+
let poisoned = false;
|
|
38
|
+
return {
|
|
39
|
+
accept: ({ request, observation, settlement }) => {
|
|
40
|
+
if (poisoned)
|
|
41
|
+
throw failure("trial settlement validation is poisoned; continuation is forbidden");
|
|
42
|
+
// Any thrown validation exception keeps this transient guard poisoned.
|
|
43
|
+
// The authoritative owner must independently persist the same fail-closed
|
|
44
|
+
// disposition, including when the process dies before this guard runs.
|
|
45
|
+
poisoned = true;
|
|
46
|
+
if (settlement === undefined || settlement === null || settlement.state !== "closed") {
|
|
47
|
+
throw failure("upstream request exposure is unknown; continuation is forbidden");
|
|
48
|
+
}
|
|
49
|
+
const proof = settlement;
|
|
50
|
+
if (!isDigest(bindingDigest) ||
|
|
51
|
+
!isId(claim.claimId) ||
|
|
52
|
+
!isDigest(claim.initialEvidenceDigest) ||
|
|
53
|
+
claim.bindingDigest !== bindingDigest ||
|
|
54
|
+
claim.trialId !== binding.trialId ||
|
|
55
|
+
claim.sessionId !== binding.session.sessionId ||
|
|
56
|
+
request.bindingDigest !== bindingDigest ||
|
|
57
|
+
request.trialId !== binding.trialId ||
|
|
58
|
+
request.sessionId !== binding.session.sessionId ||
|
|
59
|
+
request.model !== binding.candidateModel ||
|
|
60
|
+
request.role !== "candidate" ||
|
|
61
|
+
request.destination !== binding.session.responsesUrl ||
|
|
62
|
+
!isId(request.requestId) ||
|
|
63
|
+
requestIds.has(request.requestId) ||
|
|
64
|
+
!isDigest(request.requestDigest) ||
|
|
65
|
+
!isDigest(request.ingressRequestDigest) ||
|
|
66
|
+
!Number.isSafeInteger(request.sequence) ||
|
|
67
|
+
request.sequence !== lastRequestSequence + 1 ||
|
|
68
|
+
!Number.isSafeInteger(request.requestBytes) ||
|
|
69
|
+
request.requestBytes < 1 ||
|
|
70
|
+
request.requestBytes > 16 * 1024 * 1024 ||
|
|
71
|
+
(request.requestedOutputTokens !== undefined &&
|
|
72
|
+
(!Number.isSafeInteger(request.requestedOutputTokens) ||
|
|
73
|
+
request.requestedOutputTokens < 1)) ||
|
|
74
|
+
observation.fetchInvocations !== 1 ||
|
|
75
|
+
observation.headersObserved !== true ||
|
|
76
|
+
observation.rawEof !== true ||
|
|
77
|
+
!Number.isSafeInteger(observation.responseBytes) ||
|
|
78
|
+
observation.responseBytes < 0 ||
|
|
79
|
+
observation.responseBytes > 64 * 1024 * 1024 ||
|
|
80
|
+
!isDigest(observation.responseDigest) ||
|
|
81
|
+
!Number.isSafeInteger(observation.httpStatus) ||
|
|
82
|
+
observation.httpStatus < 100 ||
|
|
83
|
+
observation.httpStatus > 599 ||
|
|
84
|
+
!isId(observation.modelCallId) ||
|
|
85
|
+
modelCallIds.has(observation.modelCallId) ||
|
|
86
|
+
!["unobserved", "completed", "failed", "incomplete", "invalid"].includes(observation.terminal) ||
|
|
87
|
+
![
|
|
88
|
+
"none",
|
|
89
|
+
"transport",
|
|
90
|
+
"wire",
|
|
91
|
+
"budget",
|
|
92
|
+
"deadline",
|
|
93
|
+
"interrupted",
|
|
94
|
+
"local-lifecycle"
|
|
95
|
+
].includes(observation.failure) ||
|
|
96
|
+
(observation.responseId !== undefined && !isId(observation.responseId)) ||
|
|
97
|
+
proof.claimId !== claim.claimId ||
|
|
98
|
+
proof.bindingDigest !== bindingDigest ||
|
|
99
|
+
proof.trialId !== binding.trialId ||
|
|
100
|
+
proof.sessionId !== binding.session.sessionId ||
|
|
101
|
+
proof.requestId !== request.requestId ||
|
|
102
|
+
proof.requestDigest !== request.requestDigest ||
|
|
103
|
+
proof.ingressRequestDigest !== request.ingressRequestDigest ||
|
|
104
|
+
proof.gatewayObservationDigest !== evalTrialGatewayObservationDigest(observation) ||
|
|
105
|
+
proof.grantId !== binding.session.grantId ||
|
|
106
|
+
proof.executionDigest !== binding.session.executionDigest ||
|
|
107
|
+
proof.issuerIdentity !== binding.session.issuerIdentity ||
|
|
108
|
+
proof.maximumProviderAttempts !== binding.session.maximumProviderAttempts ||
|
|
109
|
+
proof.beforeEvidenceDigest !== lastEvidenceDigest ||
|
|
110
|
+
!isDigest(proof.evidenceDigest) ||
|
|
111
|
+
evidenceDigests.has(proof.evidenceDigest) ||
|
|
112
|
+
!isId(proof.ingressId) ||
|
|
113
|
+
ingressIds.has(proof.ingressId) ||
|
|
114
|
+
!Array.isArray(proof.newProviderAttemptIds) ||
|
|
115
|
+
proof.newProviderAttemptIds.length === 0 ||
|
|
116
|
+
proof.newProviderAttemptIds.length > binding.session.maximumProviderAttempts ||
|
|
117
|
+
proof.newProviderAttemptIds.some((id) => !isId(id) || providerAttemptIds.has(id)) ||
|
|
118
|
+
new Set(proof.newProviderAttemptIds).size !== proof.newProviderAttemptIds.length ||
|
|
119
|
+
providerAttemptIds.size + proof.newProviderAttemptIds.length >
|
|
120
|
+
binding.session.maximumProviderAttempts) {
|
|
121
|
+
throw failure("authoritative reconciliation did not match the exact request and reviewed grant");
|
|
122
|
+
}
|
|
123
|
+
for (const attemptId of proof.newProviderAttemptIds)
|
|
124
|
+
providerAttemptIds.add(attemptId);
|
|
125
|
+
requestIds.add(request.requestId);
|
|
126
|
+
ingressIds.add(proof.ingressId);
|
|
127
|
+
modelCallIds.add(observation.modelCallId);
|
|
128
|
+
evidenceDigests.add(proof.evidenceDigest);
|
|
129
|
+
lastRequestSequence = request.sequence;
|
|
130
|
+
lastEvidenceDigest = proof.evidenceDigest;
|
|
131
|
+
poisoned = false;
|
|
132
|
+
return providerAttemptIds.size;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RepositoryCommandRecipeV1 } from "@velum-labs/routekit-eval-setup";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { EvalServiceRepositoryAgentError } from "../errors.js";
|
|
4
|
+
import type { RepositoryAgentCommandResult, RepositoryAgentIsolationEvidence, RepositoryAgentTools, RepositoryAgentToolTraceEvent } from "../repository-agent-protocol.js";
|
|
5
|
+
export type RepositoryAgentCheckoutSession = {
|
|
6
|
+
readonly checkout: string;
|
|
7
|
+
readonly isolation: RepositoryAgentIsolationEvidence;
|
|
8
|
+
readonly tools: RepositoryAgentTools;
|
|
9
|
+
readonly preparationResults: readonly RepositoryAgentCommandResult[];
|
|
10
|
+
readonly trace: readonly RepositoryAgentToolTraceEvent[];
|
|
11
|
+
readonly capturePatch: Effect.Effect<string, EvalServiceRepositoryAgentError>;
|
|
12
|
+
readonly captureChangedPaths: Effect.Effect<readonly string[], EvalServiceRepositoryAgentError>;
|
|
13
|
+
};
|
|
14
|
+
export declare const acquireRepositoryAgentCheckout: (input: {
|
|
15
|
+
readonly repositoryRoot: string;
|
|
16
|
+
readonly commit: string;
|
|
17
|
+
readonly allowedTools: readonly string[];
|
|
18
|
+
readonly allowedRecipes: readonly RepositoryCommandRecipeV1[];
|
|
19
|
+
readonly trustedPreparationRecipes?: readonly RepositoryCommandRecipeV1[];
|
|
20
|
+
readonly candidateValidationRecipes?: readonly RepositoryCommandRecipeV1[];
|
|
21
|
+
readonly gradeRecipeIds?: readonly string[];
|
|
22
|
+
readonly protectedControlPaths?: readonly string[];
|
|
23
|
+
}) => Effect.Effect<RepositoryAgentCheckoutSession, EvalServiceRepositoryAgentError, import("effect/Scope").Scope>;
|