@to11ai/sdk 2.0.0-rc.3 → 2.0.0-rc.5
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 +99 -258
- package/dist/client.cjs +16 -41
- package/dist/client.d.ts +17 -37
- package/dist/client.js +14 -50
- package/dist/errors.cjs +19 -24
- package/dist/errors.d.ts +18 -46
- package/dist/errors.js +19 -24
- package/dist/gateway.cjs +32 -21
- package/dist/gateway.d.ts +44 -25
- package/dist/gateway.js +32 -21
- package/dist/generated/index.d.ts +1 -1
- package/dist/generated/types.gen.d.ts +103 -0
- package/dist/generated/zod.gen.d.ts +55 -12
- package/dist/prompts/resource.cjs +13 -28
- package/dist/prompts/resource.d.ts +8 -14
- package/dist/prompts/resource.js +17 -28
- package/dist/prompts/shape.d.ts +1 -1
- package/dist/prompts/types.d.ts +17 -13
- package/dist/transport.d.ts +2 -6
- package/package.json +5 -11
- package/src/client.ts +31 -118
- package/src/context.ts +3 -5
- package/src/errors.ts +34 -78
- package/src/gateway.ts +92 -62
- package/src/generated/index.ts +1 -1
- package/src/generated/types.gen.ts +108 -0
- package/src/generated/zod.gen.ts +46 -0
- package/src/prompts/resource.ts +36 -74
- package/src/prompts/shape.ts +1 -1
- package/src/prompts/types.ts +17 -14
- package/src/transport.ts +53 -40
- package/dist/prompts/cache.d.ts +0 -58
- package/dist/routingRules/resource.cjs +0 -138
- package/dist/routingRules/resource.js +0 -98
- package/src/prompts/cache.ts +0 -153
package/src/client.ts
CHANGED
|
@@ -8,46 +8,30 @@ import {
|
|
|
8
8
|
} from "./context";
|
|
9
9
|
import { gatewayAuthHeaders } from "./gateway";
|
|
10
10
|
import { ProjectEnvironmentResource } from "./projectEnvironments/resource";
|
|
11
|
-
import type { CacheConfig } from "./prompts/cache";
|
|
12
11
|
import { PromptResource } from "./prompts/resource";
|
|
13
12
|
import {
|
|
14
13
|
ENV_SLUG_REGEX,
|
|
15
14
|
type RenderFormat,
|
|
16
15
|
type UnknownRoleMode,
|
|
17
16
|
} from "./prompts/types";
|
|
18
|
-
import { RoutingRulesResource } from "./routingRules/resource";
|
|
19
17
|
import { Transport } from "./transport";
|
|
20
18
|
|
|
21
|
-
// SDK-behavior delivery (TO11-2100): the resolution store + helpers.
|
|
22
|
-
export {
|
|
23
|
-
BehaviorStore,
|
|
24
|
-
type BehaviorStoreOptions,
|
|
25
|
-
envVarNameForKey,
|
|
26
|
-
type SdkBehaviorEnvelope,
|
|
27
|
-
} from "./behaviors/store";
|
|
28
19
|
export type { TurnAuthContext } from "./context";
|
|
29
20
|
export { Conversation, Session, Turn } from "./context";
|
|
30
21
|
// Re-export all error types for convenience
|
|
31
22
|
export type {
|
|
32
|
-
AmbiguousEnvLabelsReason,
|
|
33
|
-
EnvNotFoundReason,
|
|
34
23
|
NoActiveReleaseReason,
|
|
35
|
-
NoApplicableRouteReason,
|
|
36
24
|
PromptNotFoundReason,
|
|
37
25
|
PromptPolicyViolationReason,
|
|
38
26
|
} from "./errors";
|
|
39
27
|
export {
|
|
40
28
|
// TO11-980 (S9) prompt-resolution typed errors.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
EnvNotFoundError,
|
|
29
|
+
LabelNotFoundError,
|
|
30
|
+
MissingLabelError,
|
|
44
31
|
NoActiveReleaseError,
|
|
45
|
-
NoApplicableRouteError,
|
|
46
32
|
PromptLabelNotRegisteredError,
|
|
47
33
|
PromptNotFoundError,
|
|
48
34
|
PromptPolicyViolationError,
|
|
49
|
-
RuleNotFoundError,
|
|
50
|
-
RuleVersionInvalidSnapshotError,
|
|
51
35
|
To11aiApiError,
|
|
52
36
|
To11aiAuthError,
|
|
53
37
|
To11aiError,
|
|
@@ -75,7 +59,6 @@ export type {
|
|
|
75
59
|
UpdateProjectEnvironmentResult,
|
|
76
60
|
} from "./projectEnvironments/resource";
|
|
77
61
|
export { ProjectEnvironmentResource } from "./projectEnvironments/resource";
|
|
78
|
-
export type { CacheConfig } from "./prompts/cache";
|
|
79
62
|
export type { PromptResourceConfig } from "./prompts/resource";
|
|
80
63
|
export { PromptResource } from "./prompts/resource";
|
|
81
64
|
export type {
|
|
@@ -92,42 +75,9 @@ export type {
|
|
|
92
75
|
RenderedPrompt,
|
|
93
76
|
RenderFormat,
|
|
94
77
|
RenderOptions,
|
|
95
|
-
ResolvedPrompt,
|
|
96
|
-
ResolvedPromptEnvelope,
|
|
97
78
|
UnknownRoleMode,
|
|
98
79
|
} from "./prompts/types";
|
|
99
80
|
export { ENV_SLUG_REGEX } from "./prompts/types";
|
|
100
|
-
export type {
|
|
101
|
-
CreateRuleVersionInput,
|
|
102
|
-
CreateRuleVersionResult,
|
|
103
|
-
GetRuleVersionInput,
|
|
104
|
-
GetRuleVersionResult,
|
|
105
|
-
ListRuleReleasesInput,
|
|
106
|
-
ListRuleReleasesResult,
|
|
107
|
-
ListRuleVersionsInput,
|
|
108
|
-
ListRuleVersionsResult,
|
|
109
|
-
RollbackRuleReleaseInput,
|
|
110
|
-
RollbackRuleReleaseResult,
|
|
111
|
-
RuleModelBinding,
|
|
112
|
-
RuleProviderRef,
|
|
113
|
-
RuleRelease,
|
|
114
|
-
RuleReleaseActorType,
|
|
115
|
-
RuleReleaseApprovalStatus,
|
|
116
|
-
RuleReleaseChangeKind,
|
|
117
|
-
RuleReleaseStrategy,
|
|
118
|
-
RuleReleaseWeight,
|
|
119
|
-
RuleSnapshot,
|
|
120
|
-
RuleStrategy,
|
|
121
|
-
RuleVersion,
|
|
122
|
-
SetRuleReleaseInput,
|
|
123
|
-
SetRuleReleaseResult,
|
|
124
|
-
} from "./routingRules/resource";
|
|
125
|
-
export {
|
|
126
|
-
RoutingRuleReleaseResource,
|
|
127
|
-
RoutingRulesResource,
|
|
128
|
-
RoutingRuleVersionResource,
|
|
129
|
-
} from "./routingRules/resource";
|
|
130
|
-
export type { Transport } from "./transport";
|
|
131
81
|
|
|
132
82
|
// ─── Client options ────────────────────────────────────────────────────────
|
|
133
83
|
|
|
@@ -143,12 +93,11 @@ export interface ClientOptions {
|
|
|
143
93
|
*/
|
|
144
94
|
gatewayUrl?: string;
|
|
145
95
|
/**
|
|
146
|
-
* API key for server-side use. Precedence: this
|
|
147
|
-
* Rejected in browser contexts —
|
|
96
|
+
* API key for server-side use (server, CLI, backend job). Precedence: this
|
|
97
|
+
* option → `TO11_API_KEY` env. Required. Rejected in browser contexts — a
|
|
98
|
+
* to11 API key carries gateway access and must never ship in client code.
|
|
148
99
|
*/
|
|
149
100
|
apiKey?: string;
|
|
150
|
-
/** Token callback for browser / edge contexts — avoids static secrets. */
|
|
151
|
-
getAccessToken?: () => Promise<string>;
|
|
152
101
|
/**
|
|
153
102
|
* Provider API key (BYOK) carried by `openaiOptions()` / `anthropicOptions()`
|
|
154
103
|
* as the provider client's `apiKey`, so a BYOK call is a single
|
|
@@ -183,17 +132,10 @@ export interface ClientOptions {
|
|
|
183
132
|
* via `render(slug, { onUnknownRole })`.
|
|
184
133
|
*/
|
|
185
134
|
onUnknownRole?: UnknownRoleMode;
|
|
186
|
-
/** LRU cache configuration for render(). Defaults: ttlSeconds=300, maxEntries=100 */
|
|
187
|
-
cache?: CacheConfig;
|
|
188
135
|
/** Request timeout in milliseconds. Default: 30_000 */
|
|
189
136
|
timeoutMs?: number;
|
|
190
137
|
/** Max retry attempts for network errors / 5xx. Default: 3 */
|
|
191
138
|
maxRetries?: number;
|
|
192
|
-
/**
|
|
193
|
-
* Purely local overrides for server-evaluated SDK-behavior flags (TO11-2100).
|
|
194
|
-
* An init option here wins over a `TO11_SDK_BEHAVIOR_*` env var. No LaunchDarkly.
|
|
195
|
-
*/
|
|
196
|
-
behaviorOverrides?: Record<string, unknown>;
|
|
197
139
|
}
|
|
198
140
|
|
|
199
141
|
// ─── Provider-client options (openaiOptions / anthropicOptions) ─────────────
|
|
@@ -230,19 +172,10 @@ export type ProviderClientOptions<
|
|
|
230
172
|
export interface To11aiClient<F extends RenderFormat | undefined = undefined> {
|
|
231
173
|
readonly prompts: PromptResource<F>;
|
|
232
174
|
readonly projectEnvironments: ProjectEnvironmentResource;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
* var) → server-delivered value (captured from responses the client has
|
|
238
|
-
* already made) → the `codedDefault` passed here. A value whose runtime type
|
|
239
|
-
* disagrees with `codedDefault` falls through to the next tier. The SDK
|
|
240
|
-
* carries no LaunchDarkly dependency; this only reads what the server
|
|
241
|
-
* delivered on the envelope.
|
|
242
|
-
*/
|
|
243
|
-
readonly resolveBehavior: <T>(key: string, codedDefault: T) => T;
|
|
244
|
-
/** Resolved API key (server mode); `undefined` in browser (`getAccessToken`) mode. */
|
|
245
|
-
readonly apiKey: string | undefined;
|
|
175
|
+
/** Resolved API key (option → `TO11_API_KEY`). */
|
|
176
|
+
readonly apiKey: string;
|
|
177
|
+
/** Resolved control-plane base URL (option → `TO11_API_URL` → default). */
|
|
178
|
+
readonly baseUrl: string;
|
|
246
179
|
/** Resolved data-plane gateway URL (option → `TO11_GATEWAY_URL` → default). */
|
|
247
180
|
readonly gatewayUrl: string;
|
|
248
181
|
/**
|
|
@@ -251,6 +184,11 @@ export interface To11aiClient<F extends RenderFormat | undefined = undefined> {
|
|
|
251
184
|
* already default to it, so you don't need to pass it back into calls.
|
|
252
185
|
*/
|
|
253
186
|
readonly projectId: string | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* Resolved default serving environment (option → `TO11_ENV`); `undefined`
|
|
189
|
+
* when none is configured. `prompts.render()` already defaults to it.
|
|
190
|
+
*/
|
|
191
|
+
readonly env: string | undefined;
|
|
254
192
|
/** A new session (unit of work). Pass an id to group telemetry by your own id. */
|
|
255
193
|
session(id?: string): Session;
|
|
256
194
|
/**
|
|
@@ -299,9 +237,8 @@ export interface To11aiClient<F extends RenderFormat | undefined = undefined> {
|
|
|
299
237
|
* - URLs (`baseUrl` / `gatewayUrl`) — option → env var → built-in default.
|
|
300
238
|
* - `apiKey` / `projectId` — option → env var (no default).
|
|
301
239
|
* - `env` / `format` — option only.
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* - `getAccessToken` → browser-safe; token fetched per request.
|
|
240
|
+
* Authenticate with `apiKey` (or `TO11_API_KEY`) — required, and server-side
|
|
241
|
+
* only (server, CLI, backend job); constructing with `apiKey` in a browser throws.
|
|
305
242
|
*
|
|
306
243
|
* @example Minimal setup (single call)
|
|
307
244
|
* import { createClient } from "@to11ai/sdk";
|
|
@@ -371,15 +308,7 @@ export function createClient<
|
|
|
371
308
|
)
|
|
372
309
|
.replace(/\/+$/, "")
|
|
373
310
|
.replace(/\/v1$/, "");
|
|
374
|
-
const
|
|
375
|
-
// Explicit token auth suppresses an ambient `TO11_API_KEY` so a shared env
|
|
376
|
-
// (SSR / CI / edge) can't defeat an explicit `getAccessToken`. Only an
|
|
377
|
-
// *explicit* `options.apiKey` alongside `getAccessToken` stays ambiguous and
|
|
378
|
-
// is rejected by the exactly-one check below.
|
|
379
|
-
const apiKey =
|
|
380
|
-
getAccessToken !== undefined
|
|
381
|
-
? options.apiKey
|
|
382
|
-
: pick(options.apiKey, "TO11_API_KEY");
|
|
311
|
+
const apiKey = pick(options.apiKey, "TO11_API_KEY");
|
|
383
312
|
const projectId = pick(options.projectId, "TO11_PROJECT_ID");
|
|
384
313
|
// Default serving env: explicit option → `TO11_ENV` env var — the same
|
|
385
314
|
// `pick(option, "TO11_*")` resolution `apiKey`/`projectId` use (empty option
|
|
@@ -395,7 +324,7 @@ export function createClient<
|
|
|
395
324
|
// undefined in the browser, so only the explicit option can reach here.
|
|
396
325
|
if (isBrowser && options.apiKey !== undefined) {
|
|
397
326
|
throw new Error(
|
|
398
|
-
"[to11ai/sdk] Do not use `apiKey` in browser
|
|
327
|
+
"[to11ai/sdk] Do not use `apiKey` in a browser — a to11 API key carries gateway access and must stay server-side (server, CLI, or backend job).",
|
|
399
328
|
);
|
|
400
329
|
}
|
|
401
330
|
|
|
@@ -422,23 +351,19 @@ export function createClient<
|
|
|
422
351
|
);
|
|
423
352
|
}
|
|
424
353
|
|
|
425
|
-
//
|
|
426
|
-
|
|
427
|
-
const hasGetAccessToken = getAccessToken !== undefined;
|
|
428
|
-
if (hasApiKey === hasGetAccessToken) {
|
|
354
|
+
// `apiKey` is the only auth source and is required.
|
|
355
|
+
if (apiKey === undefined) {
|
|
429
356
|
throw new Error(
|
|
430
|
-
"[to11ai/sdk]
|
|
357
|
+
"[to11ai/sdk] An `apiKey` (or `TO11_API_KEY`) is required.",
|
|
431
358
|
);
|
|
432
359
|
}
|
|
433
360
|
|
|
434
|
-
const auth =
|
|
435
|
-
? ({ apiKey: apiKey as string } as const)
|
|
436
|
-
: ({ getAccessToken: getAccessToken as () => Promise<string> } as const);
|
|
361
|
+
const auth = { apiKey } as const;
|
|
437
362
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
});
|
|
363
|
+
// The SDK-behavior store (TO11-2100) still captures any server-delivered
|
|
364
|
+
// flags off responses, but the public `resolveBehavior` accessor is hidden
|
|
365
|
+
// for the 2.0 surface — kept internal so it can be re-exposed step by step.
|
|
366
|
+
const behaviorStore = new BehaviorStore({ env: processEnv });
|
|
442
367
|
|
|
443
368
|
const transport = new Transport({
|
|
444
369
|
baseUrl,
|
|
@@ -448,24 +373,18 @@ export function createClient<
|
|
|
448
373
|
behaviorStore,
|
|
449
374
|
});
|
|
450
375
|
|
|
451
|
-
const prompts = new PromptResource<F>(transport,
|
|
376
|
+
const prompts = new PromptResource<F>(transport, {
|
|
452
377
|
projectId,
|
|
453
378
|
env,
|
|
454
379
|
format: options.format,
|
|
455
380
|
onUnknownRole: options.onUnknownRole,
|
|
456
381
|
});
|
|
457
382
|
const projectEnvironments = new ProjectEnvironmentResource(transport);
|
|
458
|
-
const routingRules = new RoutingRulesResource(transport);
|
|
459
383
|
|
|
460
384
|
// ── Gateway observability context (session → conversation → turn) ──
|
|
461
|
-
// These build gateway auth headers, so they require a
|
|
462
|
-
//
|
|
385
|
+
// These build gateway auth headers, so they require a projectId; fail loudly
|
|
386
|
+
// rather than emit a broken header bag.
|
|
463
387
|
const gatewayAuthContext = (): TurnAuthContext => {
|
|
464
|
-
if (apiKey === undefined) {
|
|
465
|
-
throw new Error(
|
|
466
|
-
"[to11ai/sdk] session()/turn()/openaiOptions() need an `apiKey` (or `TO11_API_KEY`) — gateway calls require a server-side key. In the browser use `getAccessToken` for control-plane calls only.",
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
388
|
if (projectId === undefined) {
|
|
470
389
|
throw new Error(
|
|
471
390
|
"[to11ai/sdk] session()/turn() need a `projectId` (or `TO11_PROJECT_ID`).",
|
|
@@ -496,11 +415,6 @@ export function createClient<
|
|
|
496
415
|
defaultBaseURL: string,
|
|
497
416
|
overrides?: T,
|
|
498
417
|
): ProviderClientOptions<T> => {
|
|
499
|
-
if (apiKey === undefined) {
|
|
500
|
-
throw new Error(
|
|
501
|
-
"[to11ai/sdk] openaiOptions()/anthropicOptions() need an `apiKey` (or `TO11_API_KEY`) — gateway calls require a server-side key. In the browser use `getAccessToken` for control-plane calls only.",
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
418
|
if (projectId === undefined) {
|
|
505
419
|
throw new Error(
|
|
506
420
|
"[to11ai/sdk] openaiOptions()/anthropicOptions() need a `projectId` (or `TO11_PROJECT_ID`) — it authenticates the gateway call via `x-to11-project-id`.",
|
|
@@ -530,12 +444,11 @@ export function createClient<
|
|
|
530
444
|
return {
|
|
531
445
|
prompts,
|
|
532
446
|
projectEnvironments,
|
|
533
|
-
routingRules,
|
|
534
|
-
resolveBehavior: (key, codedDefault) =>
|
|
535
|
-
behaviorStore.resolve(key, codedDefault),
|
|
536
447
|
apiKey,
|
|
448
|
+
baseUrl,
|
|
537
449
|
gatewayUrl,
|
|
538
450
|
projectId,
|
|
451
|
+
env,
|
|
539
452
|
session: (id) => new Session(gatewayAuthContext(), id),
|
|
540
453
|
conversation: (id) => clientDefaultSession().conversation(id),
|
|
541
454
|
turn: () => clientDefaultSession().turn(),
|
package/src/context.ts
CHANGED
|
@@ -49,10 +49,6 @@ function newId(): string {
|
|
|
49
49
|
return crypto.randomUUID();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function provenanceOf(prompt: PromptForHeaders): GatewayPromptProvenance {
|
|
53
|
-
return "metadata" in prompt ? prompt.metadata : prompt;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
52
|
/**
|
|
57
53
|
* One agent turn — a single trace. A turn's `traceparent` is fixed for its
|
|
58
54
|
* lifetime, so every LLM-call made under it groups into one trace.
|
|
@@ -105,7 +101,9 @@ export class Turn {
|
|
|
105
101
|
traceparent: this.traceparent,
|
|
106
102
|
};
|
|
107
103
|
if (prompt !== undefined) {
|
|
108
|
-
|
|
104
|
+
// gatewayPromptHeaders accepts a RenderedPrompt or a format-shaped
|
|
105
|
+
// result and unwraps `metadata` itself.
|
|
106
|
+
Object.assign(headers, gatewayPromptHeaders(prompt));
|
|
109
107
|
}
|
|
110
108
|
return headers;
|
|
111
109
|
}
|
package/src/errors.ts
CHANGED
|
@@ -342,60 +342,54 @@ export class PromptNotFoundError extends To11aiApiError {
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
/**
|
|
345
|
-
*
|
|
345
|
+
* Thrown when the label supplied to `prompts.render()` does not resolve to an
|
|
346
|
+
* existing (active) label on the server. Surfaced from the server's
|
|
347
|
+
* `ENV_NOT_FOUND` code; `label` carries the rejected slug when the server
|
|
348
|
+
* echoes it.
|
|
346
349
|
*
|
|
347
|
-
* `
|
|
348
|
-
*
|
|
349
|
-
* CLIENT-side, eagerly, before any request (R4 / AC4); `env` is absent.
|
|
350
|
-
* - `"unknown"` — an env was supplied but the server's registry has no
|
|
351
|
-
* such (active) env. Surfaced from the `ENV_NOT_FOUND` server code; `env`
|
|
352
|
-
* carries the rejected slug when the server echoes it.
|
|
350
|
+
* This is distinct from `MissingLabelError`, which is raised client-side when
|
|
351
|
+
* no label was supplied at all.
|
|
353
352
|
*/
|
|
354
|
-
export
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Thrown when no serving environment can be resolved for a `prompts.render()`
|
|
361
|
-
* call. One class, two `reason.source` discriminants (see
|
|
362
|
-
* `EnvNotFoundReason`): client-eager `"missing"` (R4) and server-forwarded
|
|
363
|
-
* `"unknown"` (`ENV_NOT_FOUND`).
|
|
364
|
-
*
|
|
365
|
-
* The client-eager variant carries no `statusCode`/`errorCode` from the wire
|
|
366
|
-
* (no request was made); it reports status `0` and errorCode `ENV_NOT_FOUND`
|
|
367
|
-
* for a uniform catch surface.
|
|
368
|
-
*/
|
|
369
|
-
export class EnvNotFoundError extends To11aiApiError {
|
|
370
|
-
readonly reason: EnvNotFoundReason;
|
|
353
|
+
export class LabelNotFoundError extends To11aiApiError {
|
|
354
|
+
/** The label slug that could not be found, when the server echoes it. */
|
|
355
|
+
readonly label?: string;
|
|
371
356
|
|
|
372
357
|
constructor(
|
|
373
358
|
message: string,
|
|
374
|
-
|
|
359
|
+
label: string | undefined,
|
|
375
360
|
details: unknown,
|
|
376
|
-
// Required (no misleading default): the client-eager `missing()` variant
|
|
377
|
-
// passes 0 (no HTTP call) and the transport mapper passes the real server
|
|
378
|
-
// status, so a hardcoded default would never reflect reality.
|
|
379
361
|
statusCode: number,
|
|
380
362
|
) {
|
|
363
|
+
// The wire code is unchanged (`ENV_NOT_FOUND`); only the SDK class name
|
|
364
|
+
// is label-oriented, decoupled from the server error code.
|
|
381
365
|
super(message, statusCode, "ENV_NOT_FOUND", details);
|
|
382
|
-
this.name = "
|
|
383
|
-
this.
|
|
366
|
+
this.name = "LabelNotFoundError";
|
|
367
|
+
this.label = label;
|
|
384
368
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
385
369
|
}
|
|
386
370
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
371
|
+
static is(err: unknown): err is LabelNotFoundError {
|
|
372
|
+
return err instanceof LabelNotFoundError;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Thrown CLIENT-side, eagerly (before any request), when `prompts.render()` is
|
|
378
|
+
* called but no label is resolvable at all: none supplied per call, and no
|
|
379
|
+
* client `env` (or `TO11_ENV`) to fall back to. A usage error, not a lookup
|
|
380
|
+
* failure — distinct from `LabelNotFoundError`.
|
|
381
|
+
*/
|
|
382
|
+
export class MissingLabelError extends To11aiError {
|
|
383
|
+
constructor(
|
|
384
|
+
message = "[to11ai/sdk] No label resolvable for prompts.render(): none supplied per call, and no client env (or TO11_ENV) to fall back to. Pass `label` to render(slug, { label }) or set `env` on createClient.",
|
|
385
|
+
) {
|
|
386
|
+
super(message);
|
|
387
|
+
this.name = "MissingLabelError";
|
|
388
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
395
389
|
}
|
|
396
390
|
|
|
397
|
-
static is(err: unknown): err is
|
|
398
|
-
return err instanceof
|
|
391
|
+
static is(err: unknown): err is MissingLabelError {
|
|
392
|
+
return err instanceof MissingLabelError;
|
|
399
393
|
}
|
|
400
394
|
}
|
|
401
395
|
|
|
@@ -480,44 +474,6 @@ export class PromptPolicyViolationError extends To11aiApiError {
|
|
|
480
474
|
}
|
|
481
475
|
}
|
|
482
476
|
|
|
483
|
-
/**
|
|
484
|
-
* Reason payload for `AmbiguousEnvLabelsError`.
|
|
485
|
-
*
|
|
486
|
-
* Carried when the supplied env-label matched more than one candidate
|
|
487
|
-
* version and the server cannot pick deterministically
|
|
488
|
-
* (`AMBIGUOUS_ENV_LABELS`).
|
|
489
|
-
*/
|
|
490
|
-
export interface AmbiguousEnvLabelsReason {
|
|
491
|
-
env?: string;
|
|
492
|
-
slug?: string;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Thrown when the env→label resolution is ambiguous on the server
|
|
497
|
-
* (`AMBIGUOUS_ENV_LABELS`) — the label matched multiple candidates and the
|
|
498
|
-
* server declines to guess. Surfaces (rather than 500ing) so callers get a
|
|
499
|
-
* typed, actionable error.
|
|
500
|
-
*/
|
|
501
|
-
export class AmbiguousEnvLabelsError extends To11aiApiError {
|
|
502
|
-
readonly reason: AmbiguousEnvLabelsReason;
|
|
503
|
-
|
|
504
|
-
constructor(
|
|
505
|
-
message: string,
|
|
506
|
-
reason: AmbiguousEnvLabelsReason,
|
|
507
|
-
details?: unknown,
|
|
508
|
-
statusCode = 409,
|
|
509
|
-
) {
|
|
510
|
-
super(message, statusCode, "AMBIGUOUS_ENV_LABELS", details);
|
|
511
|
-
this.name = "AmbiguousEnvLabelsError";
|
|
512
|
-
this.reason = reason;
|
|
513
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
static is(err: unknown): err is AmbiguousEnvLabelsError {
|
|
517
|
-
return err instanceof AmbiguousEnvLabelsError;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
477
|
/**
|
|
522
478
|
* Thrown when version create rejects the snapshot for a structural
|
|
523
479
|
* reason — invalid variant params, malformed `when` clause, illegal
|
package/src/gateway.ts
CHANGED
|
@@ -73,29 +73,26 @@ export function gatewayAuthHeaders(
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Build prompt provenance headers from a
|
|
76
|
+
* Build prompt provenance headers from a `render()` result (or a format-shaped
|
|
77
|
+
* result's `metadata`, unwrapped automatically).
|
|
77
78
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* - `x-to11-
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* - `x-to11-
|
|
85
|
-
*
|
|
86
|
-
* - `x-to11-
|
|
87
|
-
*
|
|
88
|
-
* - `x-to11-prompt-slug` — the prompt's human-readable slug
|
|
89
|
-
* readable name alongside the opaque `x-to11-prompt-id`; omitted when empty
|
|
90
|
-
* or absent (a legacy `resolve()` result carries no slug).
|
|
79
|
+
* No provenance header is required, and the gateway rejects empty header
|
|
80
|
+
* values — so each header is emitted only when its value is present and valid,
|
|
81
|
+
* and the function never throws:
|
|
82
|
+
* - `x-to11-prompt-id` — a non-empty string.
|
|
83
|
+
* - `x-to11-prompt-version` — a finite number (a non-number/NaN is dropped
|
|
84
|
+
* rather than stringified to junk like `"[object Object]"`).
|
|
85
|
+
* - `x-to11-release-id` — a non-empty `releaseId` (the offline-fallback path
|
|
86
|
+
* can carry an explicit `null`, which is omitted).
|
|
87
|
+
* - `x-to11-variant-name` — present IFF the resolved release was weighted.
|
|
88
|
+
* - `x-to11-prompt-label` — the single label the prompt resolved through.
|
|
89
|
+
* - `x-to11-prompt-slug` — the prompt's human-readable slug.
|
|
91
90
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* and omitted otherwise, so an old `resolve()` result still produces exactly
|
|
95
|
-
* the original three headers (non-breaking; D1/D7 shape coexistence).
|
|
91
|
+
* An input with no usable provenance yields an empty header bag — the same as
|
|
92
|
+
* a prompt-less `turn.headers()` call.
|
|
96
93
|
*
|
|
97
94
|
* @example
|
|
98
|
-
* const result = await client.prompts.render(slug, {
|
|
95
|
+
* const result = await client.prompts.render(slug, { label: "prod" });
|
|
99
96
|
* const headers = gatewayPromptHeaders(result);
|
|
100
97
|
*/
|
|
101
98
|
export interface GatewayPromptProvenance {
|
|
@@ -115,30 +112,38 @@ export interface GatewayPromptProvenance {
|
|
|
115
112
|
}
|
|
116
113
|
|
|
117
114
|
export function gatewayPromptHeaders(
|
|
118
|
-
prompt: GatewayPromptProvenance,
|
|
115
|
+
prompt: GatewayPromptProvenance | { metadata: GatewayPromptProvenance },
|
|
119
116
|
): Record<string, string> {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
// A format-shaped result carries provenance under `metadata`; unwrap it so
|
|
118
|
+
// the whole shaped result works, not just the bare provenance (mirrors the
|
|
119
|
+
// Python SDK).
|
|
120
|
+
const p = prompt != null && "metadata" in prompt ? prompt.metadata : prompt;
|
|
121
|
+
// No prompt provenance header is required, and the gateway rejects empty
|
|
122
|
+
// header values — so emit each one only when its value is present and valid,
|
|
123
|
+
// and never throw. An all-empty input just yields an empty header bag (the
|
|
124
|
+
// same as a prompt-less `turn.headers()` call).
|
|
125
|
+
const headers: Record<string, string> = {};
|
|
126
|
+
if (p == null) return headers;
|
|
127
|
+
if (typeof p.promptId === "string" && p.promptId.length > 0) {
|
|
128
|
+
headers["x-to11-prompt-id"] = p.promptId;
|
|
129
|
+
}
|
|
130
|
+
// A non-finite/non-number version would stringify to junk (e.g. NaN or
|
|
131
|
+
// "[object Object]"); only emit a real finite number.
|
|
132
|
+
if (typeof p.version === "number" && Number.isFinite(p.version)) {
|
|
133
|
+
headers["x-to11-prompt-version"] = String(p.version);
|
|
134
|
+
}
|
|
135
|
+
if (typeof p.releaseId === "string" && p.releaseId.length > 0) {
|
|
136
|
+
headers["x-to11-release-id"] = p.releaseId;
|
|
129
137
|
}
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
// variantName is present IFF the resolved release was weighted.
|
|
139
|
+
if (typeof p.variantName === "string" && p.variantName.length > 0) {
|
|
140
|
+
headers["x-to11-variant-name"] = p.variantName;
|
|
132
141
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (prompt.label.length > 0) {
|
|
136
|
-
headers["x-to11-prompt-label"] = prompt.label;
|
|
142
|
+
if (typeof p.label === "string" && p.label.length > 0) {
|
|
143
|
+
headers["x-to11-prompt-label"] = p.label;
|
|
137
144
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (typeof prompt.slug === "string" && prompt.slug.length > 0) {
|
|
141
|
-
headers["x-to11-prompt-slug"] = prompt.slug;
|
|
145
|
+
if (typeof p.slug === "string" && p.slug.length > 0) {
|
|
146
|
+
headers["x-to11-prompt-slug"] = p.slug;
|
|
142
147
|
}
|
|
143
148
|
return headers;
|
|
144
149
|
}
|
|
@@ -175,13 +180,25 @@ export interface OpenAIToolCall {
|
|
|
175
180
|
function: { name: string; arguments: string };
|
|
176
181
|
}
|
|
177
182
|
|
|
178
|
-
/**
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
/**
|
|
184
|
+
* OpenAI Chat Completions `messages[]` entry — a per-role discriminated union
|
|
185
|
+
* matching what Chat Completions requires for each role, so an
|
|
186
|
+
* `OpenAIChatMessage[]` is structurally assignable to the `openai` package's
|
|
187
|
+
* `ChatCompletionMessageParam[]` (a `render({ format: "openai" })` result spreads
|
|
188
|
+
* straight into `openai.chat.completions.create({ messages })` with no cast).
|
|
189
|
+
* The SDK carries no dependency on the `openai` package — this only mirrors its
|
|
190
|
+
* shape. (Assignability is regression-guarded in `gateway.provider-types.test.ts`.)
|
|
191
|
+
*/
|
|
192
|
+
export type OpenAIChatMessage =
|
|
193
|
+
| { role: "system"; content: string }
|
|
194
|
+
| { role: "developer"; content: string }
|
|
195
|
+
| { role: "user"; content: string }
|
|
196
|
+
| {
|
|
197
|
+
role: "assistant";
|
|
198
|
+
content?: string | null;
|
|
199
|
+
tool_calls?: OpenAIToolCall[];
|
|
200
|
+
}
|
|
201
|
+
| { role: "tool"; tool_call_id: string; content: string };
|
|
185
202
|
|
|
186
203
|
/** Anthropic Messages `tools[]` entry. */
|
|
187
204
|
export interface AnthropicTool {
|
|
@@ -405,24 +422,37 @@ export function toOpenAIMessages(
|
|
|
405
422
|
return;
|
|
406
423
|
}
|
|
407
424
|
if (message.role === "assistant") {
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
425
|
+
const content = message.content ?? null;
|
|
426
|
+
out.push(
|
|
427
|
+
message.toolCalls != null && message.toolCalls.length > 0
|
|
428
|
+
? {
|
|
429
|
+
role: "assistant",
|
|
430
|
+
content,
|
|
431
|
+
tool_calls: message.toolCalls.map((call) => ({
|
|
432
|
+
id: requireToolCallId(call.id),
|
|
433
|
+
type: "function",
|
|
434
|
+
function: {
|
|
435
|
+
name: call.name,
|
|
436
|
+
arguments: JSON.stringify(call.arguments),
|
|
437
|
+
},
|
|
438
|
+
})),
|
|
439
|
+
}
|
|
440
|
+
: { role: "assistant", content },
|
|
441
|
+
);
|
|
423
442
|
return;
|
|
424
443
|
}
|
|
425
|
-
|
|
444
|
+
// Remaining plain-text roles: `system` | `developer` | `user`. Push a
|
|
445
|
+
// literal-role object per branch so each element lands on a single union
|
|
446
|
+
// member (a union-typed `role` wouldn't be assignable to the discriminated
|
|
447
|
+
// ChatCompletionMessageParam).
|
|
448
|
+
const content = message.content ?? "";
|
|
449
|
+
if (message.role === "user") {
|
|
450
|
+
out.push({ role: "user", content });
|
|
451
|
+
} else if (message.role === "developer") {
|
|
452
|
+
out.push({ role: "developer", content });
|
|
453
|
+
} else {
|
|
454
|
+
out.push({ role: "system", content });
|
|
455
|
+
}
|
|
426
456
|
});
|
|
427
457
|
return out;
|
|
428
458
|
}
|