@independo/inderun-contracts 0.3.0-dev.9 → 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/README.md +3 -3
- package/dist/generated/schemas.d.ts +4 -4
- package/dist/generated/schemas.js +4 -4
- package/dist/generated/stream-event.d.ts +9 -3
- package/dist/generated/stream-event.d.ts.map +1 -1
- package/dist/generated/task-result.d.ts +8 -5
- package/dist/generated/task-result.d.ts.map +1 -1
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/package.json +12 -6
- package/src/generated/http-request.ts +34 -0
- package/src/generated/http-response.ts +24 -0
- package/src/generated/inderun-error.ts +55 -0
- package/src/generated/index.ts +15 -0
- package/src/generated/model-package.ts +191 -0
- package/src/generated/route-plan.ts +78 -0
- package/src/generated/route-planner-input.ts +161 -0
- package/src/generated/schemas.ts +1691 -0
- package/src/generated/stream-event.ts +154 -0
- package/src/generated/stream-run.ts +32 -0
- package/src/generated/stream-terminal-outcome.ts +186 -0
- package/src/generated/task-request.ts +183 -0
- package/src/generated/task-result.ts +114 -0
- package/src/generated/telemetry-event.ts +29 -0
- package/src/host.ts +205 -0
- package/src/index.ts +100 -0
- package/src/types.ts +32 -0
- package/src/validators.ts +288 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* This file was generated from JSON Schema using quicktype. Do not edit by hand. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalized telemetry event emitted by the orchestrator and providers.
|
|
5
|
+
*/
|
|
6
|
+
export type TelemetryEvent = {
|
|
7
|
+
/**
|
|
8
|
+
* Event-specific metadata. It must not contain prompt payloads or raw secrets.
|
|
9
|
+
*/
|
|
10
|
+
payload: { [key: string]: unknown };
|
|
11
|
+
/**
|
|
12
|
+
* Opaque run identifier associated with this execution event.
|
|
13
|
+
*/
|
|
14
|
+
runId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Wall-clock event timestamp in Unix epoch milliseconds.
|
|
17
|
+
*/
|
|
18
|
+
timestamp: number;
|
|
19
|
+
/**
|
|
20
|
+
* Telemetry event kind emitted by the orchestrator or provider integration.
|
|
21
|
+
*/
|
|
22
|
+
type: Type;
|
|
23
|
+
[property: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Telemetry event kind emitted by the orchestrator or provider integration.
|
|
28
|
+
*/
|
|
29
|
+
export type Type = "route_decided" | "attempt_succeeded" | "attempt_failed" | "stream_attempt_started" | "stream_attempt_succeeded" | "stream_attempt_failed" | "stream_completed" | "stream_failed" | "stream_cancelled";
|
package/src/host.ts
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { HttpRequest, HttpResponse, TelemetryEvent } from "./generated/index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Service to inspect and monitor network connectivity status.
|
|
5
|
+
*/
|
|
6
|
+
export interface ConnectivityService {
|
|
7
|
+
/**
|
|
8
|
+
* Resolves to true if the host is connected to the network, false otherwise.
|
|
9
|
+
*/
|
|
10
|
+
isOnline(): Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Operating system thermal states denoting system heat and throttling risk.
|
|
15
|
+
*/
|
|
16
|
+
export type ThermalState = "nominal" | "fair" | "serious" | "critical";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Service to inspect host device battery, power, and thermal constraints.
|
|
20
|
+
*/
|
|
21
|
+
export interface DeviceConstraintsService {
|
|
22
|
+
/**
|
|
23
|
+
* Retrieves the current thermal state of the device, if supported.
|
|
24
|
+
*/
|
|
25
|
+
getThermalState?(): Promise<ThermalState>;
|
|
26
|
+
/**
|
|
27
|
+
* Checks if the host device has low power mode enabled, if supported.
|
|
28
|
+
*/
|
|
29
|
+
isLowPowerModeEnabled?(): Promise<boolean>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Secure storage interface to store and retrieve sensitive credentials/tokens
|
|
34
|
+
* without exposing them to request payloads.
|
|
35
|
+
*/
|
|
36
|
+
export interface SecureStorageService {
|
|
37
|
+
/**
|
|
38
|
+
* Gets a secret by its unique slot identifier.
|
|
39
|
+
*/
|
|
40
|
+
getSecret(slotId: string): Promise<string | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Securely saves a secret under a unique slot identifier.
|
|
43
|
+
*/
|
|
44
|
+
setSecret(slotId: string, secret: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Deletes a secret by its slot identifier.
|
|
47
|
+
*/
|
|
48
|
+
deleteSecret(slotId: string): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Service providing clocks for request timeouts and execution timing telemetry.
|
|
53
|
+
*/
|
|
54
|
+
export interface ClockService {
|
|
55
|
+
/**
|
|
56
|
+
* Returns current UNIX timestamp in milliseconds.
|
|
57
|
+
*/
|
|
58
|
+
now(): number;
|
|
59
|
+
/**
|
|
60
|
+
* Returns monotonic high-resolution time in milliseconds.
|
|
61
|
+
*/
|
|
62
|
+
monotonicNow?(): number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type { HttpRequest, HttpResponse, TelemetryEvent } from "./generated/index.js";
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* HttpClient service for cloud provider communication.
|
|
69
|
+
*/
|
|
70
|
+
export interface HttpClientService {
|
|
71
|
+
/**
|
|
72
|
+
* Dispatches a normalized HTTP request and returns the normalized response.
|
|
73
|
+
* @param request - Configuration options for the HTTP request.
|
|
74
|
+
*/
|
|
75
|
+
send(request: HttpRequest): Promise<HttpResponse>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The structural subset of the standard `AbortSignal` that IndeRun relies on.
|
|
80
|
+
*
|
|
81
|
+
* The contracts package targets a platform-neutral library surface and so has
|
|
82
|
+
* no DOM lib; a real `AbortSignal` satisfies this shape, so host implementations
|
|
83
|
+
* and callers can pass one directly.
|
|
84
|
+
*/
|
|
85
|
+
export interface AbortSignalLike {
|
|
86
|
+
/**
|
|
87
|
+
* Whether cancellation has already been requested.
|
|
88
|
+
*/
|
|
89
|
+
readonly aborted: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Registers a cancellation listener.
|
|
92
|
+
*/
|
|
93
|
+
addEventListener(type: "abort", listener: () => void, options?: { once?: boolean }): void;
|
|
94
|
+
/**
|
|
95
|
+
* Removes a previously registered cancellation listener.
|
|
96
|
+
*/
|
|
97
|
+
removeEventListener(type: "abort", listener: () => void): void;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A streamed HTTP response: the status line and headers are resolved first, and
|
|
102
|
+
* the body is delivered incrementally as raw byte chunks.
|
|
103
|
+
*
|
|
104
|
+
* The head is deliberately separated from the body so a caller can classify a
|
|
105
|
+
* non-2xx response (mapping it through the normal HTTP error taxonomy) before
|
|
106
|
+
* deciding to interpret the body as a protocol stream. Chunk boundaries are
|
|
107
|
+
* transport artifacts and carry no meaning: a chunk may split a protocol frame
|
|
108
|
+
* or even a multi-byte UTF-8 sequence, so consumers must buffer and decode
|
|
109
|
+
* incrementally rather than treating a chunk as a unit.
|
|
110
|
+
*/
|
|
111
|
+
export interface HttpStreamResponse {
|
|
112
|
+
/**
|
|
113
|
+
* HTTP status code.
|
|
114
|
+
*/
|
|
115
|
+
status: number;
|
|
116
|
+
/**
|
|
117
|
+
* HTTP status text.
|
|
118
|
+
*/
|
|
119
|
+
statusText: string;
|
|
120
|
+
/**
|
|
121
|
+
* Response headers, with lower-cased names.
|
|
122
|
+
*/
|
|
123
|
+
headers: Record<string, string>;
|
|
124
|
+
/**
|
|
125
|
+
* The response body as an incremental sequence of raw byte chunks. Iterating
|
|
126
|
+
* it to completion, or abandoning it, releases the underlying connection.
|
|
127
|
+
*/
|
|
128
|
+
body: AsyncIterable<Uint8Array>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Optional host capability for HTTP responses that must be consumed while they
|
|
133
|
+
* are still arriving — the transport requirement behind Mode 2 streaming over
|
|
134
|
+
* the network (for example server-sent events).
|
|
135
|
+
*
|
|
136
|
+
* It is a separate service rather than a method on {@link HttpClientService}
|
|
137
|
+
* because it is genuinely optional: a host that cannot stream simply omits it,
|
|
138
|
+
* and providers report that as `streamingAvailable: false` in their dynamic
|
|
139
|
+
* capabilities, which the route planner turns into an inspectable
|
|
140
|
+
* `streaming_unavailable` rejection.
|
|
141
|
+
*/
|
|
142
|
+
export interface HttpStreamingClientService {
|
|
143
|
+
/**
|
|
144
|
+
* Dispatches a normalized HTTP request and resolves once the response head is
|
|
145
|
+
* available, leaving the body to be consumed incrementally.
|
|
146
|
+
*
|
|
147
|
+
* `HttpRequest.timeoutMs` bounds the wait for the response head only — the
|
|
148
|
+
* time to first byte. It cannot bound total duration, since a stream is
|
|
149
|
+
* open-ended by nature; use `signal` to stop a stream that runs too long.
|
|
150
|
+
*
|
|
151
|
+
* @param request - Normalized HTTP request payload.
|
|
152
|
+
* @param signal - Optional caller-driven cancellation. Aborting it must tear
|
|
153
|
+
* down the underlying connection and cause the body iterator to stop.
|
|
154
|
+
*/
|
|
155
|
+
stream(request: HttpRequest, signal?: AbortSignalLike): Promise<HttpStreamResponse>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export type TelemetryEventType = TelemetryEvent["type"];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Telemetry service listener hook interface.
|
|
162
|
+
*/
|
|
163
|
+
export interface TelemetryService {
|
|
164
|
+
/**
|
|
165
|
+
* Emits a telemetry event.
|
|
166
|
+
* @param event - The generated telemetry event.
|
|
167
|
+
*/
|
|
168
|
+
emit(event: TelemetryEvent): void;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Registry of platform-specific host services utilized by the Engine Core.
|
|
173
|
+
* Evaluates execution capabilities and manages OS-level boundaries.
|
|
174
|
+
*/
|
|
175
|
+
export interface HostServices {
|
|
176
|
+
/**
|
|
177
|
+
* Network connectivity service.
|
|
178
|
+
*/
|
|
179
|
+
connectivity: ConnectivityService;
|
|
180
|
+
/**
|
|
181
|
+
* Device constraints service.
|
|
182
|
+
*/
|
|
183
|
+
deviceConstraints?: DeviceConstraintsService;
|
|
184
|
+
/**
|
|
185
|
+
* Secure storage service.
|
|
186
|
+
*/
|
|
187
|
+
secureStorage?: SecureStorageService;
|
|
188
|
+
/**
|
|
189
|
+
* Clock service.
|
|
190
|
+
*/
|
|
191
|
+
clock?: ClockService;
|
|
192
|
+
/**
|
|
193
|
+
* HTTP Client service for performing network requests.
|
|
194
|
+
*/
|
|
195
|
+
httpClient?: HttpClientService;
|
|
196
|
+
/**
|
|
197
|
+
* HTTP client service for responses consumed while still arriving. Absent on
|
|
198
|
+
* hosts that cannot stream a response body.
|
|
199
|
+
*/
|
|
200
|
+
streamingHttpClient?: HttpStreamingClientService;
|
|
201
|
+
/**
|
|
202
|
+
* Telemetry service to monitor execution steps, latency, and success rates.
|
|
203
|
+
*/
|
|
204
|
+
telemetry?: TelemetryService;
|
|
205
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical IndeRun contracts for TypeScript (`@independo/inderun-contracts`).
|
|
3
|
+
*
|
|
4
|
+
* Everything here derives from the JSON Schemas in `contracts/schemas/`, which are
|
|
5
|
+
* the single source of truth across all four languages — the TypeScript types
|
|
6
|
+
* below, the Kotlin and Swift contract models, and the Rust route-core model are
|
|
7
|
+
* all generated from them by `pnpm generate`. Do not hand-edit anything under
|
|
8
|
+
* `src/generated/`; change the schema and regenerate.
|
|
9
|
+
*
|
|
10
|
+
* Apps normally do not depend on this package directly: the SDKs re-export the
|
|
11
|
+
* contract types their own signatures use. Reach for it when you need the schemas
|
|
12
|
+
* themselves or the runtime validators.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** The generated request/response, streaming, routing, and telemetry models. */
|
|
16
|
+
export type * from "./generated/index.js";
|
|
17
|
+
|
|
18
|
+
/** The JSON Schemas those models were generated from, for runtime use. */
|
|
19
|
+
export {
|
|
20
|
+
httpRequestSchema,
|
|
21
|
+
httpResponseSchema,
|
|
22
|
+
inderunErrorSchema,
|
|
23
|
+
modelPackageSchema,
|
|
24
|
+
routePlanSchema,
|
|
25
|
+
routePlannerInputSchema,
|
|
26
|
+
streamEventSchema,
|
|
27
|
+
streamRunSchema,
|
|
28
|
+
streamTerminalOutcomeSchema,
|
|
29
|
+
taskRequestSchema,
|
|
30
|
+
taskResultSchema,
|
|
31
|
+
telemetryEventSchema
|
|
32
|
+
} from "./generated/index.js";
|
|
33
|
+
/**
|
|
34
|
+
* Ajv-backed validators for each contract. The `get*ValidationIssues` form returns
|
|
35
|
+
* a list of human-readable issues (empty when valid); the `validate*` form is the
|
|
36
|
+
* boolean type guard.
|
|
37
|
+
*/
|
|
38
|
+
export {
|
|
39
|
+
getHttpRequestValidationIssues,
|
|
40
|
+
getHttpResponseValidationIssues,
|
|
41
|
+
getIndeRunErrorValidationIssues,
|
|
42
|
+
getModelPackageValidationIssues,
|
|
43
|
+
getRoutePlanValidationIssues,
|
|
44
|
+
getRoutePlannerInputValidationIssues,
|
|
45
|
+
getStreamEventValidationIssues,
|
|
46
|
+
getStreamRunHandleValidationIssues,
|
|
47
|
+
getStreamTerminalOutcomeValidationIssues,
|
|
48
|
+
getTaskRequestValidationIssues,
|
|
49
|
+
getTaskResultValidationIssues,
|
|
50
|
+
getTelemetryEventValidationIssues,
|
|
51
|
+
validateHttpRequest,
|
|
52
|
+
validateHttpResponse,
|
|
53
|
+
validateIndeRunError,
|
|
54
|
+
validateModelPackage,
|
|
55
|
+
validateRoutePlan,
|
|
56
|
+
validateRoutePlannerInput,
|
|
57
|
+
validateStreamEvent,
|
|
58
|
+
validateStreamRunHandle,
|
|
59
|
+
validateStreamTerminalOutcome,
|
|
60
|
+
validateTaskRequest,
|
|
61
|
+
validateTaskResult,
|
|
62
|
+
validateTelemetryEvent,
|
|
63
|
+
type ValidationIssue
|
|
64
|
+
} from "./validators.js";
|
|
65
|
+
/**
|
|
66
|
+
* Narrowed aliases over the generated models: the closed enums the SDKs switch on,
|
|
67
|
+
* and the routing input/output shapes under the names the engines use.
|
|
68
|
+
*/
|
|
69
|
+
export type {
|
|
70
|
+
FinishReason,
|
|
71
|
+
IndeRunErrorClass,
|
|
72
|
+
ModelPackageFormat,
|
|
73
|
+
ModelSourceType,
|
|
74
|
+
SchemaVersion,
|
|
75
|
+
SharedRoutePlan,
|
|
76
|
+
SharedRoutePlannerInput,
|
|
77
|
+
TaskKind,
|
|
78
|
+
RoutingConstraints,
|
|
79
|
+
RoutingPreferences
|
|
80
|
+
} from "./types.js";
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The host-services contract: the platform capabilities an SDK reads and calls
|
|
84
|
+
* through. Defined here rather than per-SDK so the three platforms expose the
|
|
85
|
+
* same seams.
|
|
86
|
+
*/
|
|
87
|
+
export type {
|
|
88
|
+
AbortSignalLike,
|
|
89
|
+
ConnectivityService,
|
|
90
|
+
ThermalState,
|
|
91
|
+
DeviceConstraintsService,
|
|
92
|
+
SecureStorageService,
|
|
93
|
+
ClockService,
|
|
94
|
+
HttpClientService,
|
|
95
|
+
HttpStreamResponse,
|
|
96
|
+
HttpStreamingClientService,
|
|
97
|
+
TelemetryEventType,
|
|
98
|
+
TelemetryService,
|
|
99
|
+
HostServices
|
|
100
|
+
} from "./host.js";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IndeRunError,
|
|
3
|
+
ModelPackage,
|
|
4
|
+
RoutePlan,
|
|
5
|
+
RoutePlannerInput,
|
|
6
|
+
TaskRequest,
|
|
7
|
+
TaskResult
|
|
8
|
+
} from "./generated/index.js";
|
|
9
|
+
|
|
10
|
+
// Convenience aliases derived from the generated schema types, so consumers can
|
|
11
|
+
// name individual fields without reaching into the generated index signatures.
|
|
12
|
+
|
|
13
|
+
/** Contract schema version (e.g. `"1.0"`) carried by every task request. */
|
|
14
|
+
export type SchemaVersion = TaskRequest["schemaVersion"];
|
|
15
|
+
/** Supported task kinds (currently `"text_to_text"`). */
|
|
16
|
+
export type TaskKind = TaskRequest["task"]["kind"];
|
|
17
|
+
/** Normalized reason a task finished (e.g. `stop`, `length`, `error`). */
|
|
18
|
+
export type FinishReason = TaskResult["finishReason"];
|
|
19
|
+
/** The shared error taxonomy value carried on `IndeRunError`. */
|
|
20
|
+
export type IndeRunErrorClass = IndeRunError["errorClass"];
|
|
21
|
+
/** Input schema consumed by the shared (Rust/WASM) route planner. */
|
|
22
|
+
export type SharedRoutePlannerInput = RoutePlannerInput;
|
|
23
|
+
/** Route plan produced by the shared route planner. */
|
|
24
|
+
export type SharedRoutePlan = RoutePlan;
|
|
25
|
+
/** Routing constraints (cloud/privacy/timeout) from a task request. */
|
|
26
|
+
export type RoutingConstraints = NonNullable<TaskRequest["constraints"]>;
|
|
27
|
+
/** Routing preferences (e.g. `optimizeFor`) from a task request. */
|
|
28
|
+
export type RoutingPreferences = NonNullable<TaskRequest["preferences"]>;
|
|
29
|
+
/** Serialization format of a model package (`onnx`, `ort`, `genai`). */
|
|
30
|
+
export type ModelPackageFormat = ModelPackage["format"];
|
|
31
|
+
/** How a model package's files are supplied to a provider (`registry`, `bundled`, ...). */
|
|
32
|
+
export type ModelSourceType = NonNullable<ModelPackage["source"]>["sourceType"];
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
2
|
+
import type { ErrorObject, ValidateFunction } from "ajv";
|
|
3
|
+
|
|
4
|
+
import type { IndeRunError } from "./generated/inderun-error.js";
|
|
5
|
+
import type { HttpRequest } from "./generated/http-request.js";
|
|
6
|
+
import type { HttpResponse } from "./generated/http-response.js";
|
|
7
|
+
import type { ModelPackage } from "./generated/model-package.js";
|
|
8
|
+
import type { RoutePlan } from "./generated/route-plan.js";
|
|
9
|
+
import type { RoutePlannerInput } from "./generated/route-planner-input.js";
|
|
10
|
+
import {
|
|
11
|
+
httpRequestSchema,
|
|
12
|
+
httpResponseSchema,
|
|
13
|
+
inderunErrorSchema,
|
|
14
|
+
modelPackageSchema,
|
|
15
|
+
routePlanSchema,
|
|
16
|
+
routePlannerInputSchema,
|
|
17
|
+
streamEventSchema,
|
|
18
|
+
streamRunSchema,
|
|
19
|
+
streamTerminalOutcomeSchema,
|
|
20
|
+
taskRequestSchema,
|
|
21
|
+
taskResultSchema,
|
|
22
|
+
telemetryEventSchema
|
|
23
|
+
} from "./generated/index.js";
|
|
24
|
+
import type { StreamEvent } from "./generated/stream-event.js";
|
|
25
|
+
import type { StreamRunHandle } from "./generated/stream-run.js";
|
|
26
|
+
import type { StreamTerminalOutcome } from "./generated/stream-terminal-outcome.js";
|
|
27
|
+
import type { TaskRequest } from "./generated/task-request.js";
|
|
28
|
+
import type { TaskResult } from "./generated/task-result.js";
|
|
29
|
+
import type { TelemetryEvent } from "./generated/telemetry-event.js";
|
|
30
|
+
|
|
31
|
+
type Ajv2020Constructor = typeof import("ajv/dist/2020.js").Ajv2020;
|
|
32
|
+
|
|
33
|
+
export type ValidationIssue = {
|
|
34
|
+
path: string;
|
|
35
|
+
message: string;
|
|
36
|
+
keyword?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const Ajv2020Constructor = Ajv2020 as unknown as Ajv2020Constructor;
|
|
40
|
+
|
|
41
|
+
const ajv = new Ajv2020Constructor({
|
|
42
|
+
allErrors: true,
|
|
43
|
+
strict: true,
|
|
44
|
+
strictRequired: false,
|
|
45
|
+
validateSchema: true
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const validateTaskRequestSchema = ajv.compile(taskRequestSchema);
|
|
49
|
+
const validateTaskResultSchema = ajv.compile(taskResultSchema);
|
|
50
|
+
const validateIndeRunErrorSchema = ajv.compile(inderunErrorSchema);
|
|
51
|
+
const validateHttpRequestSchema = ajv.compile(httpRequestSchema);
|
|
52
|
+
const validateHttpResponseSchema = ajv.compile(httpResponseSchema);
|
|
53
|
+
const validateTelemetryEventSchema = ajv.compile(telemetryEventSchema);
|
|
54
|
+
const validateRoutePlannerInputSchema = ajv.compile(routePlannerInputSchema);
|
|
55
|
+
const validateRoutePlanSchema = ajv.compile(routePlanSchema);
|
|
56
|
+
const validateModelPackageSchema = ajv.compile(modelPackageSchema);
|
|
57
|
+
const validateStreamRunHandleSchema = ajv.compile(streamRunSchema);
|
|
58
|
+
const validateStreamEventSchema = ajv.compile(streamEventSchema);
|
|
59
|
+
const validateStreamTerminalOutcomeSchema = ajv.compile(streamTerminalOutcomeSchema);
|
|
60
|
+
|
|
61
|
+
export function validateTaskRequest(value: unknown): value is TaskRequest {
|
|
62
|
+
return getTaskRequestValidationIssues(value).length === 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function validateTaskResult(value: unknown): value is TaskResult {
|
|
66
|
+
return getTaskResultValidationIssues(value).length === 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function validateIndeRunError(value: unknown): value is IndeRunError {
|
|
70
|
+
return getIndeRunErrorValidationIssues(value).length === 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function validateHttpRequest(value: unknown): value is HttpRequest {
|
|
74
|
+
return getHttpRequestValidationIssues(value).length === 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function validateHttpResponse(value: unknown): value is HttpResponse {
|
|
78
|
+
return getHttpResponseValidationIssues(value).length === 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function validateTelemetryEvent(value: unknown): value is TelemetryEvent {
|
|
82
|
+
return getTelemetryEventValidationIssues(value).length === 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function validateRoutePlannerInput(value: unknown): value is RoutePlannerInput {
|
|
86
|
+
return getRoutePlannerInputValidationIssues(value).length === 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validateRoutePlan(value: unknown): value is RoutePlan {
|
|
90
|
+
return getRoutePlanValidationIssues(value).length === 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function validateModelPackage(value: unknown): value is ModelPackage {
|
|
94
|
+
return getModelPackageValidationIssues(value).length === 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function validateStreamRunHandle(value: unknown): value is StreamRunHandle {
|
|
98
|
+
return getStreamRunHandleValidationIssues(value).length === 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function validateStreamEvent(value: unknown): value is StreamEvent {
|
|
102
|
+
return getStreamEventValidationIssues(value).length === 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function validateStreamTerminalOutcome(value: unknown): value is StreamTerminalOutcome {
|
|
106
|
+
return getStreamTerminalOutcomeValidationIssues(value).length === 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function getTaskRequestValidationIssues(value: unknown): ValidationIssue[] {
|
|
110
|
+
const issues = getValidationIssues(validateTaskRequestSchema, value, {
|
|
111
|
+
forbidInlineSecrets: true
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
issues.push(...findRoutingConstraintIssues(value));
|
|
115
|
+
return issues;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getTaskResultValidationIssues(value: unknown): ValidationIssue[] {
|
|
119
|
+
return getValidationIssues(validateTaskResultSchema, value, {
|
|
120
|
+
forbidInlineSecrets: true
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function getIndeRunErrorValidationIssues(value: unknown): ValidationIssue[] {
|
|
125
|
+
return getValidationIssues(validateIndeRunErrorSchema, value, {
|
|
126
|
+
forbidInlineSecrets: true
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getHttpRequestValidationIssues(value: unknown): ValidationIssue[] {
|
|
131
|
+
return getValidationIssues(validateHttpRequestSchema, value);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function getHttpResponseValidationIssues(value: unknown): ValidationIssue[] {
|
|
135
|
+
return getValidationIssues(validateHttpResponseSchema, value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function getTelemetryEventValidationIssues(value: unknown): ValidationIssue[] {
|
|
139
|
+
return getValidationIssues(validateTelemetryEventSchema, value, {
|
|
140
|
+
forbidInlineSecrets: true
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function getRoutePlannerInputValidationIssues(value: unknown): ValidationIssue[] {
|
|
145
|
+
return getValidationIssues(validateRoutePlannerInputSchema, value);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function getRoutePlanValidationIssues(value: unknown): ValidationIssue[] {
|
|
149
|
+
return getValidationIssues(validateRoutePlanSchema, value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function getModelPackageValidationIssues(value: unknown): ValidationIssue[] {
|
|
153
|
+
return getValidationIssues(validateModelPackageSchema, value, {
|
|
154
|
+
forbidInlineSecrets: true
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function getStreamRunHandleValidationIssues(value: unknown): ValidationIssue[] {
|
|
159
|
+
return getValidationIssues(validateStreamRunHandleSchema, value);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function getStreamEventValidationIssues(value: unknown): ValidationIssue[] {
|
|
163
|
+
return getValidationIssues(validateStreamEventSchema, value, {
|
|
164
|
+
forbidInlineSecrets: true
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function getStreamTerminalOutcomeValidationIssues(value: unknown): ValidationIssue[] {
|
|
169
|
+
return getValidationIssues(validateStreamTerminalOutcomeSchema, value, {
|
|
170
|
+
forbidInlineSecrets: true
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getValidationIssues(
|
|
175
|
+
validateSchema: ValidateFunction,
|
|
176
|
+
value: unknown,
|
|
177
|
+
options: { forbidInlineSecrets?: boolean } = {}
|
|
178
|
+
): ValidationIssue[] {
|
|
179
|
+
const issues: ValidationIssue[] = [];
|
|
180
|
+
|
|
181
|
+
if (!validateSchema(value)) {
|
|
182
|
+
issues.push(...formatAjvIssues(validateSchema.errors ?? []));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (options.forbidInlineSecrets) {
|
|
186
|
+
issues.push(...findInlineSecretKeys(value));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return issues;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function formatAjvIssues(errors: ErrorObject[]): ValidationIssue[] {
|
|
193
|
+
return errors.map((error) => ({
|
|
194
|
+
path: error.instancePath || "/",
|
|
195
|
+
message: error.message ?? "Invalid value",
|
|
196
|
+
keyword: error.keyword
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function findInlineSecretKeys(value: unknown, path = ""): ValidationIssue[] {
|
|
201
|
+
if (Array.isArray(value)) {
|
|
202
|
+
return value.flatMap((item, index) => findInlineSecretKeys(item, `${path}/${index}`));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!value || typeof value !== "object") {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const issues: ValidationIssue[] = [];
|
|
210
|
+
for (const [key, child] of Object.entries(value)) {
|
|
211
|
+
const childPath = `${path}/${escapeJsonPointerSegment(key)}`;
|
|
212
|
+
if (isForbiddenSecretKey(key)) {
|
|
213
|
+
issues.push({
|
|
214
|
+
path: childPath,
|
|
215
|
+
message: "Inline secrets are not allowed; use authContextRef instead.",
|
|
216
|
+
keyword: "forbiddenSecretKey"
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
issues.push(...findInlineSecretKeys(child, childPath));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return issues;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function findRoutingConstraintIssues(value: unknown): ValidationIssue[] {
|
|
226
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const request = value as {
|
|
231
|
+
constraints?: {
|
|
232
|
+
cloud?: string;
|
|
233
|
+
privacy?: string;
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const constraints = request.constraints;
|
|
238
|
+
if (!constraints) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const issues: ValidationIssue[] = [];
|
|
243
|
+
|
|
244
|
+
if (constraints.privacy === "local_required" && constraints.cloud === "required") {
|
|
245
|
+
issues.push({
|
|
246
|
+
path: "/constraints",
|
|
247
|
+
message: "privacy=local_required conflicts with cloud=required.",
|
|
248
|
+
keyword: "routingConstraintConflict"
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (constraints.privacy === "cloud_required" && constraints.cloud === "forbidden") {
|
|
253
|
+
issues.push({
|
|
254
|
+
path: "/constraints",
|
|
255
|
+
message: "privacy=cloud_required conflicts with cloud=forbidden.",
|
|
256
|
+
keyword: "routingConstraintConflict"
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return issues;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function normalizeKey(key: string): string {
|
|
264
|
+
return key.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function isForbiddenSecretKey(key: string): boolean {
|
|
268
|
+
const normalizedKey = normalizeKey(key);
|
|
269
|
+
|
|
270
|
+
if (
|
|
271
|
+
normalizedKey.includes("authorization") ||
|
|
272
|
+
normalizedKey.includes("password") ||
|
|
273
|
+
normalizedKey.includes("secret") ||
|
|
274
|
+
normalizedKey.includes("apikey")
|
|
275
|
+
) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (normalizedKey.includes("api") && normalizedKey.includes("key")) {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return normalizedKey === "token" || normalizedKey.endsWith("token");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function escapeJsonPointerSegment(segment: string): string {
|
|
287
|
+
return segment.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
288
|
+
}
|