@to11ai/sdk 2.0.0-rc.3 → 2.0.0-rc.4
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 +18 -12
- package/dist/gateway.d.ts +19 -20
- package/dist/gateway.js +18 -12
- 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 +3 -11
- package/src/client.ts +31 -118
- package/src/context.ts +3 -5
- package/src/errors.ts +34 -78
- package/src/gateway.ts +44 -39
- 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/prompts/resource.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
MissingLabelError,
|
|
3
|
+
To11aiApiError,
|
|
4
|
+
To11aiNetworkError,
|
|
5
|
+
} from "../errors";
|
|
3
6
|
import { zResolvedPrompt } from "../generated/zod.gen";
|
|
4
7
|
import type { ApiEnvelope, Transport } from "../transport";
|
|
5
|
-
import type { CacheConfig } from "./cache";
|
|
6
|
-
import { buildRenderCacheKey, PromptCache } from "./cache";
|
|
7
8
|
import type {
|
|
8
9
|
AnthropicRenderedPrompt,
|
|
9
10
|
OpenAiRenderedPrompt,
|
|
@@ -56,13 +57,12 @@ import type {
|
|
|
56
57
|
} from "./types";
|
|
57
58
|
import { ENV_SLUG_REGEX } from "./types";
|
|
58
59
|
|
|
59
|
-
// Re-export CacheConfig so callers can import from this module
|
|
60
|
-
export type { CacheConfig } from "./cache";
|
|
61
60
|
export type {
|
|
62
61
|
ArchivePromptInput,
|
|
63
62
|
ArchivePromptResult,
|
|
64
63
|
BlockRenderRecord,
|
|
65
64
|
ChatMessage,
|
|
65
|
+
ChatRole,
|
|
66
66
|
CreateProjectPromptLabelInput,
|
|
67
67
|
CreateProjectPromptLabelResult,
|
|
68
68
|
CreatePromptInput,
|
|
@@ -111,8 +111,6 @@ export type {
|
|
|
111
111
|
ResolvedToolCall,
|
|
112
112
|
ResolvedToolDefinition,
|
|
113
113
|
RowError,
|
|
114
|
-
TextChatMessage,
|
|
115
|
-
TextChatRole,
|
|
116
114
|
UnknownRoleMode,
|
|
117
115
|
UpdateProjectPromptLabelInput,
|
|
118
116
|
UpdateProjectPromptLabelResult,
|
|
@@ -120,9 +118,7 @@ export type {
|
|
|
120
118
|
UpdatePromptResult,
|
|
121
119
|
} from "./types";
|
|
122
120
|
|
|
123
|
-
const zResolvedPromptEnvelope = zResolvedPrompt
|
|
124
|
-
.partial({ releaseId: true })
|
|
125
|
-
.extend({ strategy: z.enum(["single", "weighted"]).optional() });
|
|
121
|
+
const zResolvedPromptEnvelope = zResolvedPrompt.partial({ releaseId: true });
|
|
126
122
|
|
|
127
123
|
function unwrap<T>(envelope: ApiEnvelope<T>): T {
|
|
128
124
|
if (envelope.data === null || envelope.data === undefined) {
|
|
@@ -293,7 +289,6 @@ export interface PromptResourceConfig {
|
|
|
293
289
|
|
|
294
290
|
export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
295
291
|
private readonly transport: Transport;
|
|
296
|
-
private readonly cache: PromptCache;
|
|
297
292
|
private readonly projectId: string | undefined;
|
|
298
293
|
private readonly env: string | undefined;
|
|
299
294
|
private readonly format: RenderFormat | undefined;
|
|
@@ -301,14 +296,12 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
301
296
|
|
|
302
297
|
constructor(
|
|
303
298
|
transport: Transport,
|
|
304
|
-
cacheConfig?: CacheConfig,
|
|
305
299
|
// `format` is bound to the class generic `F` (not the wider `RenderFormat`)
|
|
306
300
|
// so the constructed default shape cannot disagree with what `render()`'s
|
|
307
301
|
// return type (`RenderResult<F>`) promises.
|
|
308
302
|
config?: Omit<PromptResourceConfig, "format"> & { format?: F },
|
|
309
303
|
) {
|
|
310
304
|
this.transport = transport;
|
|
311
|
-
this.cache = new PromptCache(cacheConfig);
|
|
312
305
|
this.projectId = config?.projectId;
|
|
313
306
|
this.env = config?.env;
|
|
314
307
|
this.format = config?.format;
|
|
@@ -527,17 +520,14 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
527
520
|
|
|
528
521
|
/**
|
|
529
522
|
* Env-aware resolution → neutral `RenderedPrompt`. Carries the resolve /
|
|
530
|
-
*
|
|
523
|
+
* offline-fallback logic; `render()` is the public shaping surface.
|
|
531
524
|
*
|
|
532
|
-
* - **
|
|
525
|
+
* - **label wire mapping:** the resolve route is label-based
|
|
533
526
|
* (`/v1/projects/:projectId/prompts:resolve`, body `labels[]`); the
|
|
534
|
-
* resolved
|
|
535
|
-
* - **No silent fallback (R4):** if no
|
|
536
|
-
*
|
|
537
|
-
* before any transport call.
|
|
538
|
-
* - **Cache (D8):** caches `single`-strategy releases and weighted releases
|
|
539
|
-
* WITH a `subject`; BYPASSES the cache for weighted releases with NO
|
|
540
|
-
* `subject` (a uniform-random assignment must not be pinned for the TTL).
|
|
527
|
+
* resolved label is sent as the single resolution label.
|
|
528
|
+
* - **No silent fallback (R4):** if no label is resolvable (none per call
|
|
529
|
+
* AND no client `env` fallback) this throws `MissingLabelError`
|
|
530
|
+
* CLIENT-side, eagerly, before any transport call.
|
|
541
531
|
*/
|
|
542
532
|
private async resolveToRendered(
|
|
543
533
|
slug: string,
|
|
@@ -549,41 +539,28 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
549
539
|
);
|
|
550
540
|
}
|
|
551
541
|
|
|
552
|
-
// R2 precedence: per-call
|
|
553
|
-
const
|
|
542
|
+
// R2 precedence: per-call label > client `env` fallback. No third source.
|
|
543
|
+
const label = options.label ?? this.env;
|
|
554
544
|
|
|
555
|
-
// R4 / AC4: no
|
|
556
|
-
if (
|
|
557
|
-
throw
|
|
545
|
+
// R4 / AC4: no label resolvable → throw eagerly, ZERO transport calls.
|
|
546
|
+
if (label === undefined) {
|
|
547
|
+
throw new MissingLabelError();
|
|
558
548
|
}
|
|
559
549
|
|
|
560
|
-
// Validate the RESOLVED
|
|
561
|
-
// init-time env, but PromptResource has a
|
|
562
|
-
// bypass createClient, so re-validate here so
|
|
563
|
-
// "Prod") never reaches the wire
|
|
564
|
-
if (!ENV_SLUG_REGEX.test(
|
|
550
|
+
// Validate the RESOLVED label (per-call OR the client `env` fallback).
|
|
551
|
+
// createClient validates the init-time env, but PromptResource has a
|
|
552
|
+
// public constructor that can bypass createClient, so re-validate here so
|
|
553
|
+
// a malformed label (e.g. "Prod") never reaches the wire (CodeRabbit).
|
|
554
|
+
if (!ENV_SLUG_REGEX.test(label)) {
|
|
565
555
|
throw new Error(
|
|
566
|
-
`[to11ai/sdk] \`
|
|
556
|
+
`[to11ai/sdk] \`label\` must match ${ENV_SLUG_REGEX} (lowercase-kebab, 1-63 chars). Received: ${JSON.stringify(label)}`,
|
|
567
557
|
);
|
|
568
558
|
}
|
|
569
559
|
|
|
570
|
-
const { subject, variables,
|
|
560
|
+
const { subject, variables, fallback } = options;
|
|
571
561
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
slug,
|
|
575
|
-
env,
|
|
576
|
-
subject,
|
|
577
|
-
variables,
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
if (useCache) {
|
|
581
|
-
const cached = this.cache.get(cacheKey);
|
|
582
|
-
if (cached) return structuredClone(cached);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// env→label mapping: the env slug is the single resolution label.
|
|
586
|
-
const body: Record<string, unknown> = { slug, labels: [env] };
|
|
562
|
+
// The resolved label is sent as the single resolution label.
|
|
563
|
+
const body: Record<string, unknown> = { slug, labels: [label] };
|
|
587
564
|
if (subject !== undefined) body.subject = subject;
|
|
588
565
|
if (variables !== undefined) body.variables = variables;
|
|
589
566
|
|
|
@@ -598,31 +575,16 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
598
575
|
},
|
|
599
576
|
);
|
|
600
577
|
const raw = unwrap(envelope);
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
// D8: bypass the cache for a weighted release with NO subject —
|
|
604
|
-
// caching a uniform-random assignment would pin one variant for the
|
|
605
|
-
// whole TTL. Cache `single` releases and `subject`-keyed weighted
|
|
606
|
-
// releases (deterministic, safe to cache). Weighted-ness is inferred
|
|
607
|
-
// from `strategy === "weighted"` OR the presence of `variantName`
|
|
608
|
-
// (which the server stamps iff weighted) — so a result that carries a
|
|
609
|
-
// variant but omits `strategy` is still correctly bypassed rather than
|
|
610
|
-
// pinning one random variant for the TTL (Cursor / Greptile).
|
|
611
|
-
const isWeighted =
|
|
612
|
-
raw.strategy === "weighted" || result.variantName !== undefined;
|
|
613
|
-
const cacheable = !(isWeighted && subject === undefined);
|
|
614
|
-
if (useCache && cacheable) {
|
|
615
|
-
this.cache.set(cacheKey, structuredClone(result));
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return result;
|
|
578
|
+
return normalizeResolvedEnvelope(raw, slug);
|
|
619
579
|
} catch (err) {
|
|
620
|
-
//
|
|
621
|
-
//
|
|
622
|
-
//
|
|
623
|
-
// client-eager
|
|
624
|
-
|
|
625
|
-
|
|
580
|
+
// Serve the offline fallback on a TRANSIENT outage — a network error
|
|
581
|
+
// or a 5xx from the control plane. A typed 4xx (not-found / policy /
|
|
582
|
+
// validation) is a real application error and is NOT masked. The
|
|
583
|
+
// client-eager MissingLabelError already threw above the try.
|
|
584
|
+
const transient =
|
|
585
|
+
err instanceof To11aiNetworkError ||
|
|
586
|
+
(err instanceof To11aiApiError && err.statusCode >= 500);
|
|
587
|
+
if (transient && fallback !== undefined) {
|
|
626
588
|
return liftFallbackToRendered(fallback, slug);
|
|
627
589
|
}
|
|
628
590
|
throw err;
|
package/src/prompts/shape.ts
CHANGED
|
@@ -135,7 +135,7 @@ export function shapeAnthropic(
|
|
|
135
135
|
return result;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
/** Shape a
|
|
138
|
+
/** Shape a rendered prompt for `format`, or return it neutral when unset. */
|
|
139
139
|
export function shapePrompt(
|
|
140
140
|
prompt: RenderedPrompt,
|
|
141
141
|
format: RenderFormat | undefined,
|
package/src/prompts/types.ts
CHANGED
|
@@ -23,16 +23,14 @@ export type PromptVersion = GenPromptVersion;
|
|
|
23
23
|
export type PromptFormat = PromptVersion["format"];
|
|
24
24
|
export type PromptLabelPointer = GenPromptLabelPointer;
|
|
25
25
|
|
|
26
|
-
export type
|
|
26
|
+
export type ChatRole = "system" | "user" | "assistant" | "developer";
|
|
27
27
|
|
|
28
|
-
export interface
|
|
29
|
-
role:
|
|
28
|
+
export interface ChatMessage {
|
|
29
|
+
role: ChatRole;
|
|
30
30
|
content: string;
|
|
31
31
|
disabled?: boolean;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export type ChatMessage = TextChatMessage;
|
|
35
|
-
|
|
36
34
|
export type RenderedChatMessage = GenRenderedChatPrompt["messages"][number];
|
|
37
35
|
|
|
38
36
|
/** Provider shape for `prompts.render()` output. Omit for the neutral result. */
|
|
@@ -174,6 +172,13 @@ export interface ListPromptLabelsInput {
|
|
|
174
172
|
|
|
175
173
|
export type ListPromptLabelsResult = PromptLabelPointer[];
|
|
176
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Slug rule for a `render()` label — lowercase-kebab, 1-63 chars. Env slugs are
|
|
177
|
+
* a special case of a label value, so labels are held to the same strict rule
|
|
178
|
+
* (no uppercase, no underscores): a label is a routing key, and case-insensitive
|
|
179
|
+
* routing keys invite silent "production" vs "Production" mismatches. Intentionally
|
|
180
|
+
* strict — do not loosen.
|
|
181
|
+
*/
|
|
177
182
|
export const ENV_SLUG_REGEX = /^[a-z0-9-]{1,63}$/;
|
|
178
183
|
|
|
179
184
|
export type BlockRenderRecord = Record<string, "rendered" | "dimmed">;
|
|
@@ -223,20 +228,18 @@ export interface RenderedPrompt {
|
|
|
223
228
|
toolChoice?: NonNullable<ResolvedPrompt["toolChoice"]>;
|
|
224
229
|
}
|
|
225
230
|
|
|
226
|
-
export type ResolvedPromptEnvelope = GenResolvedPrompt
|
|
227
|
-
/** Active release strategy. Used to decide weighted-no-subject cache bypass (D8). */
|
|
228
|
-
strategy?: "single" | "weighted";
|
|
229
|
-
};
|
|
231
|
+
export type ResolvedPromptEnvelope = GenResolvedPrompt;
|
|
230
232
|
|
|
231
233
|
export interface RenderOptions {
|
|
232
|
-
/**
|
|
233
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Label to resolve this call's prompt version; overrides the client default.
|
|
236
|
+
* When omitted, falls back to the client's `env` (env is a label value).
|
|
237
|
+
*/
|
|
238
|
+
label?: string;
|
|
234
239
|
/** Stable id so a weighted release sends the same caller to the same variant. */
|
|
235
240
|
subject?: string;
|
|
236
241
|
variables?: Record<string, unknown>;
|
|
237
|
-
/**
|
|
238
|
-
cache?: boolean;
|
|
239
|
-
/** Prompt served if the API is unreachable on a network error and cache is empty. */
|
|
242
|
+
/** Prompt served if the API is unreachable on a network error. */
|
|
240
243
|
fallback?: RenderedPrompt;
|
|
241
244
|
/** Provider shape for this call's output; overrides the client default. */
|
|
242
245
|
format?: RenderFormat;
|
package/src/transport.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { BehaviorStore } from "./behaviors/store";
|
|
2
2
|
import {
|
|
3
|
-
AmbiguousEnvLabelsError,
|
|
4
3
|
EnvNotEnabledForProviderError,
|
|
5
|
-
|
|
4
|
+
LabelNotFoundError,
|
|
6
5
|
NoActiveReleaseError,
|
|
7
6
|
NoApplicableRouteError,
|
|
8
7
|
PromptLabelNotRegisteredError,
|
|
@@ -108,14 +107,11 @@ function mapPromptResolveError(
|
|
|
108
107
|
);
|
|
109
108
|
}
|
|
110
109
|
if (detailsCode === "ENV_NOT_FOUND") {
|
|
111
|
-
// Server-side unknown-
|
|
112
|
-
// raised in resource.ts before any request
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
details,
|
|
117
|
-
status,
|
|
118
|
-
);
|
|
110
|
+
// Server-side unknown-label. (The client-side MissingLabelError — no
|
|
111
|
+
// label supplied at all — is raised in resource.ts before any request.)
|
|
112
|
+
// Prefer `details.label`, falling back to `details.env`.
|
|
113
|
+
const label = typeof d.label === "string" ? d.label : env;
|
|
114
|
+
return new LabelNotFoundError(message, label, details, status);
|
|
119
115
|
}
|
|
120
116
|
if (detailsCode === "NO_ACTIVE_RELEASE") {
|
|
121
117
|
return new NoActiveReleaseError(
|
|
@@ -125,9 +121,6 @@ function mapPromptResolveError(
|
|
|
125
121
|
status,
|
|
126
122
|
);
|
|
127
123
|
}
|
|
128
|
-
if (detailsCode === "AMBIGUOUS_ENV_LABELS") {
|
|
129
|
-
return new AmbiguousEnvLabelsError(message, { env, slug }, details, status);
|
|
130
|
-
}
|
|
131
124
|
if (PROMPT_POLICY_VIOLATION_DETAILS_CODES.has(detailsCode)) {
|
|
132
125
|
const policy = typeof d.policy === "string" ? d.policy : undefined;
|
|
133
126
|
return new PromptPolicyViolationError(
|
|
@@ -140,9 +133,9 @@ function mapPromptResolveError(
|
|
|
140
133
|
return undefined;
|
|
141
134
|
}
|
|
142
135
|
|
|
143
|
-
export
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
export interface AuthConfig {
|
|
137
|
+
readonly apiKey: string;
|
|
138
|
+
}
|
|
146
139
|
|
|
147
140
|
export interface TransportConfig {
|
|
148
141
|
readonly baseUrl: string;
|
|
@@ -248,12 +241,25 @@ function mapHttpError(
|
|
|
248
241
|
}
|
|
249
242
|
if (status === 429) {
|
|
250
243
|
const retryAfterHeader = headers?.get("retry-after");
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
244
|
+
let retryAfterSeconds: number | undefined;
|
|
245
|
+
if (retryAfterHeader) {
|
|
246
|
+
const numeric = Number(retryAfterHeader);
|
|
247
|
+
if (Number.isFinite(numeric)) {
|
|
248
|
+
retryAfterSeconds = Math.max(0, numeric);
|
|
249
|
+
} else {
|
|
250
|
+
// RFC 9110 HTTP-date form → seconds until that instant.
|
|
251
|
+
const when = Date.parse(retryAfterHeader);
|
|
252
|
+
if (!Number.isNaN(when)) {
|
|
253
|
+
retryAfterSeconds = Math.max(
|
|
254
|
+
0,
|
|
255
|
+
Math.round((when - Date.now()) / 1000),
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
254
260
|
return new To11aiRateLimitError(
|
|
255
261
|
message,
|
|
256
|
-
|
|
262
|
+
retryAfterSeconds,
|
|
257
263
|
errorCode,
|
|
258
264
|
details,
|
|
259
265
|
);
|
|
@@ -395,25 +401,20 @@ export class Transport {
|
|
|
395
401
|
private readonly behaviorStore: BehaviorStore | undefined;
|
|
396
402
|
|
|
397
403
|
constructor(config: TransportConfig) {
|
|
398
|
-
this.baseUrl = config.baseUrl.replace(
|
|
404
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
399
405
|
this.auth = config.auth;
|
|
400
406
|
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
401
407
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
402
408
|
this.behaviorStore = config.behaviorStore;
|
|
403
409
|
}
|
|
404
410
|
|
|
405
|
-
private
|
|
411
|
+
private buildHeaders(): Record<string, string> {
|
|
406
412
|
const headers: Record<string, string> = {
|
|
407
413
|
"content-type": "application/json",
|
|
408
414
|
accept: "application/json",
|
|
409
415
|
};
|
|
410
416
|
|
|
411
|
-
|
|
412
|
-
headers.authorization = `Bearer ${this.auth.apiKey}`;
|
|
413
|
-
} else if (this.auth.getAccessToken) {
|
|
414
|
-
const token = await this.auth.getAccessToken();
|
|
415
|
-
headers.authorization = `Bearer ${token}`;
|
|
416
|
-
}
|
|
417
|
+
headers.authorization = `Bearer ${this.auth.apiKey}`;
|
|
417
418
|
|
|
418
419
|
const tp = activeTraceContext();
|
|
419
420
|
if (tp) {
|
|
@@ -452,7 +453,7 @@ export class Transport {
|
|
|
452
453
|
let attempt = 0;
|
|
453
454
|
|
|
454
455
|
while (true) {
|
|
455
|
-
const headers =
|
|
456
|
+
const headers = this.buildHeaders();
|
|
456
457
|
const controller = new AbortController();
|
|
457
458
|
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
458
459
|
|
|
@@ -495,16 +496,28 @@ export class Transport {
|
|
|
495
496
|
let json: ApiEnvelope<T> | undefined;
|
|
496
497
|
const text = await response.text();
|
|
497
498
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
response.status,
|
|
504
|
-
null,
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
499
|
+
if (text === "" && response.ok) {
|
|
500
|
+
// A successful empty body (e.g. 204): synthesize an empty-data
|
|
501
|
+
// envelope rather than mis-mapping it to an API error.
|
|
502
|
+
json = {
|
|
503
|
+
success: true,
|
|
504
|
+
code: response.status,
|
|
505
|
+
data: null,
|
|
506
|
+
error: null,
|
|
507
|
+
pagination: null,
|
|
508
|
+
} as ApiEnvelope<T>;
|
|
509
|
+
} else {
|
|
510
|
+
try {
|
|
511
|
+
json = JSON.parse(text) as ApiEnvelope<T>;
|
|
512
|
+
} catch {
|
|
513
|
+
// Not a JSON body — treat as generic API error
|
|
514
|
+
throw mapHttpError(
|
|
515
|
+
response.status,
|
|
516
|
+
null,
|
|
517
|
+
text || response.statusText,
|
|
518
|
+
response.headers,
|
|
519
|
+
);
|
|
520
|
+
}
|
|
508
521
|
}
|
|
509
522
|
|
|
510
523
|
if (!response.ok) {
|
|
@@ -554,7 +567,7 @@ export class Transport {
|
|
|
554
567
|
const isTimeout =
|
|
555
568
|
err instanceof DOMException && err.name === "AbortError";
|
|
556
569
|
// Only retry on genuine network failures (fetch throws TypeError) or timeouts.
|
|
557
|
-
//
|
|
570
|
+
// Other unexpected errors propagate immediately.
|
|
558
571
|
const isNetworkError = err instanceof TypeError;
|
|
559
572
|
|
|
560
573
|
if (
|
package/dist/prompts/cache.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { RenderedPrompt } from "./types";
|
|
2
|
-
export interface CacheConfig {
|
|
3
|
-
/** Time-to-live in seconds. Default: 300 (5 minutes) */
|
|
4
|
-
ttlSeconds?: number;
|
|
5
|
-
/** Maximum number of entries. Default: 100 */
|
|
6
|
-
maxEntries?: number;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* The cache stores enriched `render()` results, which are JSON-cloneable.
|
|
10
|
-
*/
|
|
11
|
-
export type CacheableRendered = RenderedPrompt;
|
|
12
|
-
/**
|
|
13
|
-
* Build a canonical base cache key from project/slug/labels/variables.
|
|
14
|
-
*
|
|
15
|
-
* - projectId is included to prevent cross-project cache collisions: a single
|
|
16
|
-
* PromptResource can serve multiple projects, so the same slug+labels must
|
|
17
|
-
* not share a cache entry across different projects.
|
|
18
|
-
* - Labels are sorted alphabetically so ["production", "en"] and ["en", "production"]
|
|
19
|
-
* map to the same key.
|
|
20
|
-
* - Variables are canonicalized with deterministic sorted-keys JSON before hashing.
|
|
21
|
-
* - Each segment is JSON-encoded so that separator characters in slug or label
|
|
22
|
-
* values cannot cause key collisions (e.g. a slug containing "|" or ",").
|
|
23
|
-
*/
|
|
24
|
-
export declare function buildCacheKey(projectId: string, slug: string, labels: string[], variables?: Record<string, unknown>): string;
|
|
25
|
-
/**
|
|
26
|
-
* Build a cache key for an env-aware `render()` call.
|
|
27
|
-
*
|
|
28
|
-
* Folds in `env` and `subject` ON TOP of the base
|
|
29
|
-
* project/slug/labels/variables key so two calls that differ only by
|
|
30
|
-
* env/subject do not return a stale cross-env / cross-cohort hit
|
|
31
|
-
* (spec §3 "Caching key changes"). The env slug is also forwarded as the
|
|
32
|
-
* single resolution label (env→label mapping, correction #3), so it appears
|
|
33
|
-
* in the `labels` segment too; including it explicitly here keeps the key
|
|
34
|
-
* unambiguous and self-documenting.
|
|
35
|
-
*
|
|
36
|
-
* `subject` participates in the key so a `subject`-keyed weighted assignment
|
|
37
|
-
* caches as the same deterministic variant on repeat (D8); the
|
|
38
|
-
* weighted-no-subject bypass is handled in the resource, not here.
|
|
39
|
-
*/
|
|
40
|
-
export declare function buildRenderCacheKey(projectId: string, slug: string, env: string, subject?: string, variables?: Record<string, unknown>): string;
|
|
41
|
-
/**
|
|
42
|
-
* In-memory LRU cache for RenderedPrompt objects.
|
|
43
|
-
*
|
|
44
|
-
* Uses a Map (insertion-ordered) as the backing store. On every access, the
|
|
45
|
-
* entry is deleted and re-inserted to maintain LRU order — oldest entries sit
|
|
46
|
-
* at the front of the Map.
|
|
47
|
-
*/
|
|
48
|
-
export declare class PromptCache {
|
|
49
|
-
private readonly ttlMs;
|
|
50
|
-
private readonly maxEntries;
|
|
51
|
-
private readonly store;
|
|
52
|
-
constructor(config?: CacheConfig);
|
|
53
|
-
get(key: string): CacheableRendered | undefined;
|
|
54
|
-
set(key: string, value: CacheableRendered): void;
|
|
55
|
-
delete(key: string): void;
|
|
56
|
-
clear(): void;
|
|
57
|
-
get size(): number;
|
|
58
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
function __accessProp(key) {
|
|
6
|
-
return this[key];
|
|
7
|
-
}
|
|
8
|
-
var __toCommonJS = (from) => {
|
|
9
|
-
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
-
if (entry)
|
|
11
|
-
return entry;
|
|
12
|
-
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (var key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(entry, key))
|
|
16
|
-
__defProp(entry, key, {
|
|
17
|
-
get: __accessProp.bind(from, key),
|
|
18
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
__moduleCache.set(from, entry);
|
|
22
|
-
return entry;
|
|
23
|
-
};
|
|
24
|
-
var __moduleCache;
|
|
25
|
-
var __returnValue = (v) => v;
|
|
26
|
-
function __exportSetter(name, newValue) {
|
|
27
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
-
}
|
|
29
|
-
var __export = (target, all) => {
|
|
30
|
-
for (var name in all)
|
|
31
|
-
__defProp(target, name, {
|
|
32
|
-
get: all[name],
|
|
33
|
-
enumerable: true,
|
|
34
|
-
configurable: true,
|
|
35
|
-
set: __exportSetter.bind(all, name)
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// src/routingRules/resource.ts
|
|
40
|
-
var exports_resource = {};
|
|
41
|
-
__export(exports_resource, {
|
|
42
|
-
RoutingRulesResource: () => RoutingRulesResource,
|
|
43
|
-
RoutingRuleVersionResource: () => RoutingRuleVersionResource,
|
|
44
|
-
RoutingRuleReleaseResource: () => RoutingRuleReleaseResource
|
|
45
|
-
});
|
|
46
|
-
module.exports = __toCommonJS(exports_resource);
|
|
47
|
-
var import_zod = require("zod");
|
|
48
|
-
var import_zod2 = require("../generated/zod.gen");
|
|
49
|
-
function unwrap(envelope) {
|
|
50
|
-
if (envelope.data === null || envelope.data === undefined) {
|
|
51
|
-
throw new Error("API returned success but data is null");
|
|
52
|
-
}
|
|
53
|
-
return envelope.data;
|
|
54
|
-
}
|
|
55
|
-
function rulePath(projectId, ruleId) {
|
|
56
|
-
return `/v1/projects/${encodeURIComponent(projectId)}/routing/rules/${encodeURIComponent(ruleId)}`;
|
|
57
|
-
}
|
|
58
|
-
function versionsPath(projectId, ruleId) {
|
|
59
|
-
return `${rulePath(projectId, ruleId)}/versions`;
|
|
60
|
-
}
|
|
61
|
-
function versionPath(projectId, ruleId, versionId) {
|
|
62
|
-
return `${versionsPath(projectId, ruleId)}/${encodeURIComponent(versionId)}`;
|
|
63
|
-
}
|
|
64
|
-
function releasesPath(projectId, ruleId) {
|
|
65
|
-
return `${rulePath(projectId, ruleId)}/releases`;
|
|
66
|
-
}
|
|
67
|
-
function releasePath(projectId, ruleId, envSlug) {
|
|
68
|
-
return `${releasesPath(projectId, ruleId)}/${encodeURIComponent(envSlug)}`;
|
|
69
|
-
}
|
|
70
|
-
function releaseRollbackPath(projectId, ruleId, envSlug) {
|
|
71
|
-
return `${releasePath(projectId, ruleId, envSlug)}/rollback`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
class RoutingRuleVersionResource {
|
|
75
|
-
transport;
|
|
76
|
-
constructor(transport) {
|
|
77
|
-
this.transport = transport;
|
|
78
|
-
}
|
|
79
|
-
async create(input) {
|
|
80
|
-
const { projectId, ruleId, ...body } = input;
|
|
81
|
-
const envelope = await this.transport.request("POST", versionsPath(projectId, ruleId), { body });
|
|
82
|
-
return unwrap(envelope);
|
|
83
|
-
}
|
|
84
|
-
async list(input) {
|
|
85
|
-
const query = {};
|
|
86
|
-
if (input.cursor !== undefined)
|
|
87
|
-
query.cursor = input.cursor;
|
|
88
|
-
if (input.limit !== undefined)
|
|
89
|
-
query.limit = input.limit;
|
|
90
|
-
const envelope = await this.transport.request("GET", versionsPath(input.projectId, input.ruleId), { query });
|
|
91
|
-
return unwrap(envelope);
|
|
92
|
-
}
|
|
93
|
-
async get(input) {
|
|
94
|
-
const envelope = await this.transport.request("GET", versionPath(input.projectId, input.ruleId, input.versionId), {
|
|
95
|
-
decode: (data) => import_zod2.zRuleVersion.parse(data)
|
|
96
|
-
});
|
|
97
|
-
return unwrap(envelope);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
class RoutingRuleReleaseResource {
|
|
102
|
-
transport;
|
|
103
|
-
constructor(transport) {
|
|
104
|
-
this.transport = transport;
|
|
105
|
-
}
|
|
106
|
-
async set(input) {
|
|
107
|
-
const { projectId, ruleId, envSlug, ...body } = input;
|
|
108
|
-
const envelope = await this.transport.request("PUT", releasePath(projectId, ruleId, envSlug), { body });
|
|
109
|
-
return unwrap(envelope);
|
|
110
|
-
}
|
|
111
|
-
async rollback(input) {
|
|
112
|
-
const { projectId, ruleId, envSlug, ...body } = input;
|
|
113
|
-
const envelope = await this.transport.request("POST", releaseRollbackPath(projectId, ruleId, envSlug), { body });
|
|
114
|
-
return unwrap(envelope);
|
|
115
|
-
}
|
|
116
|
-
async list(input) {
|
|
117
|
-
const query = {};
|
|
118
|
-
if (input.cursor !== undefined)
|
|
119
|
-
query.cursor = input.cursor;
|
|
120
|
-
if (input.limit !== undefined)
|
|
121
|
-
query.limit = input.limit;
|
|
122
|
-
const path = input.envSlug !== undefined ? releasePath(input.projectId, input.ruleId, input.envSlug) : releasesPath(input.projectId, input.ruleId);
|
|
123
|
-
const envelope = await this.transport.request("GET", path, {
|
|
124
|
-
query,
|
|
125
|
-
decode: (data) => import_zod.z.array(import_zod2.zRuleRelease).parse(data)
|
|
126
|
-
});
|
|
127
|
-
return unwrap(envelope);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
class RoutingRulesResource {
|
|
132
|
-
versions;
|
|
133
|
-
releases;
|
|
134
|
-
constructor(transport) {
|
|
135
|
-
this.versions = new RoutingRuleVersionResource(transport);
|
|
136
|
-
this.releases = new RoutingRuleReleaseResource(transport);
|
|
137
|
-
}
|
|
138
|
-
}
|