@to11ai/sdk 2.0.0-rc.1 → 2.0.0-rc.3
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/client.cjs +5 -1
- package/dist/client.d.ts +10 -3
- package/dist/client.js +5 -1
- package/dist/context.d.ts +1 -1
- package/dist/gateway.cjs +3 -0
- package/dist/gateway.d.ts +8 -0
- package/dist/gateway.js +3 -0
- package/dist/prompts/cache.d.ts +8 -9
- package/dist/prompts/resource.cjs +57 -78
- package/dist/prompts/resource.d.ts +14 -18
- package/dist/prompts/resource.js +58 -79
- package/dist/prompts/shape.d.ts +8 -6
- package/dist/prompts/types.d.ts +34 -39
- package/package.json +1 -1
- package/src/client.ts +21 -5
- package/src/context.ts +1 -1
- package/src/gateway.ts +13 -0
- package/src/generated/zod.gen.ts +3 -3
- package/src/prompts/cache.ts +10 -11
- package/src/prompts/resource.ts +111 -137
- package/src/prompts/shape.ts +11 -8
- package/src/prompts/types.ts +33 -43
package/src/generated/zod.gen.ts
CHANGED
|
@@ -236,7 +236,7 @@ export const zPrompt = z.object({
|
|
|
236
236
|
id: z.string(),
|
|
237
237
|
name: z.string().min(1).max(200),
|
|
238
238
|
projectId: z.string(),
|
|
239
|
-
slug: z.string().min(1).max(
|
|
239
|
+
slug: z.string().min(1).max(128).regex(/^[a-z0-9-]+$/),
|
|
240
240
|
status: z.enum(['active', 'archived']),
|
|
241
241
|
tags: z.array(z.string()),
|
|
242
242
|
updatedAt: z.iso.datetime()
|
|
@@ -7359,7 +7359,7 @@ export const zPromptsListPromptsResponse = z.object({
|
|
|
7359
7359
|
latestVersion: z.number().nullable(),
|
|
7360
7360
|
name: z.string().min(1).max(200),
|
|
7361
7361
|
projectId: z.string(),
|
|
7362
|
-
slug: z.string().min(1).max(
|
|
7362
|
+
slug: z.string().min(1).max(128).regex(/^[a-z0-9-]+$/),
|
|
7363
7363
|
status: z.enum(['active', 'archived']),
|
|
7364
7364
|
tags: z.array(z.string()),
|
|
7365
7365
|
updatedAt: z.iso.datetime()
|
|
@@ -7390,7 +7390,7 @@ export const zPromptsListPromptsResponse = z.object({
|
|
|
7390
7390
|
export const zPromptsCreatePromptBody = z.object({
|
|
7391
7391
|
description: z.string().nullish(),
|
|
7392
7392
|
name: z.string().min(1).max(200),
|
|
7393
|
-
slug: z.string().min(1).max(
|
|
7393
|
+
slug: z.string().min(1).max(128).regex(/^[a-z0-9-]+$/),
|
|
7394
7394
|
tags: z.array(z.string()).nullish()
|
|
7395
7395
|
});
|
|
7396
7396
|
|
package/src/prompts/cache.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RenderedPrompt } from "./types";
|
|
2
2
|
|
|
3
3
|
export interface CacheConfig {
|
|
4
4
|
/** Time-to-live in seconds. Default: 300 (5 minutes) */
|
|
@@ -8,18 +8,17 @@ export interface CacheConfig {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* The cache stores
|
|
12
|
-
* `render()` result. Both are JSON-cloneable; the cache is shape-agnostic.
|
|
11
|
+
* The cache stores enriched `render()` results, which are JSON-cloneable.
|
|
13
12
|
*/
|
|
14
|
-
export type
|
|
13
|
+
export type CacheableRendered = RenderedPrompt;
|
|
15
14
|
|
|
16
15
|
interface CacheEntry {
|
|
17
|
-
value:
|
|
16
|
+
value: CacheableRendered;
|
|
18
17
|
expiresAt: number;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
|
-
* Build a canonical cache key
|
|
21
|
+
* Build a canonical base cache key from project/slug/labels/variables.
|
|
23
22
|
*
|
|
24
23
|
* - projectId is included to prevent cross-project cache collisions: a single
|
|
25
24
|
* PromptResource can serve multiple projects, so the same slug+labels must
|
|
@@ -58,7 +57,7 @@ export function buildCacheKey(
|
|
|
58
57
|
* caches as the same deterministic variant on repeat (D8); the
|
|
59
58
|
* weighted-no-subject bypass is handled in the resource, not here.
|
|
60
59
|
*/
|
|
61
|
-
export function
|
|
60
|
+
export function buildRenderCacheKey(
|
|
62
61
|
projectId: string,
|
|
63
62
|
slug: string,
|
|
64
63
|
env: string,
|
|
@@ -66,7 +65,7 @@ export function buildFetchCacheKey(
|
|
|
66
65
|
variables?: Record<string, unknown>,
|
|
67
66
|
): string {
|
|
68
67
|
const base = buildCacheKey(projectId, slug, [env], variables);
|
|
69
|
-
return `
|
|
68
|
+
return `render|${base}|${JSON.stringify(env)}|${JSON.stringify(subject ?? null)}`;
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
function sortObjectKeys(obj: unknown): unknown {
|
|
@@ -80,7 +79,7 @@ function sortObjectKeys(obj: unknown): unknown {
|
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
/**
|
|
83
|
-
* In-memory LRU cache for
|
|
82
|
+
* In-memory LRU cache for RenderedPrompt objects.
|
|
84
83
|
*
|
|
85
84
|
* Uses a Map (insertion-ordered) as the backing store. On every access, the
|
|
86
85
|
* entry is deleted and re-inserted to maintain LRU order — oldest entries sit
|
|
@@ -97,7 +96,7 @@ export class PromptCache {
|
|
|
97
96
|
this.store = new Map();
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
get(key: string):
|
|
99
|
+
get(key: string): CacheableRendered | undefined {
|
|
101
100
|
const entry = this.store.get(key);
|
|
102
101
|
if (!entry) return undefined;
|
|
103
102
|
|
|
@@ -113,7 +112,7 @@ export class PromptCache {
|
|
|
113
112
|
return entry.value;
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
set(key: string, value:
|
|
115
|
+
set(key: string, value: CacheableRendered): void {
|
|
117
116
|
// maxEntries: 0 (or negative) means caching is disabled — drop the entry.
|
|
118
117
|
if (this.maxEntries <= 0) {
|
|
119
118
|
return;
|
package/src/prompts/resource.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { EnvNotFoundError, To11aiNetworkError } from "../errors";
|
|
|
3
3
|
import { zResolvedPrompt } from "../generated/zod.gen";
|
|
4
4
|
import type { ApiEnvelope, Transport } from "../transport";
|
|
5
5
|
import type { CacheConfig } from "./cache";
|
|
6
|
-
import {
|
|
6
|
+
import { buildRenderCacheKey, PromptCache } from "./cache";
|
|
7
7
|
import type {
|
|
8
8
|
AnthropicRenderedPrompt,
|
|
9
9
|
OpenAiRenderedPrompt,
|
|
@@ -21,8 +21,6 @@ import type {
|
|
|
21
21
|
CreatePromptVersionResult,
|
|
22
22
|
DeleteProjectPromptLabelInput,
|
|
23
23
|
DeleteProjectPromptLabelResult,
|
|
24
|
-
FetchedPrompt,
|
|
25
|
-
FetchPromptOptions,
|
|
26
24
|
GetProjectPromptLabelInput,
|
|
27
25
|
GetProjectPromptLabelResult,
|
|
28
26
|
GetPromptInput,
|
|
@@ -50,8 +48,6 @@ import type {
|
|
|
50
48
|
RenderOptions,
|
|
51
49
|
ResolvedPrompt,
|
|
52
50
|
ResolvedPromptEnvelope,
|
|
53
|
-
ResolvePromptInput,
|
|
54
|
-
ResolvePromptResult,
|
|
55
51
|
UnknownRoleMode,
|
|
56
52
|
UpdateProjectPromptLabelInput,
|
|
57
53
|
UpdateProjectPromptLabelResult,
|
|
@@ -75,8 +71,6 @@ export type {
|
|
|
75
71
|
CreatePromptVersionResult,
|
|
76
72
|
DeleteProjectPromptLabelInput,
|
|
77
73
|
DeleteProjectPromptLabelResult,
|
|
78
|
-
FetchedPrompt,
|
|
79
|
-
FetchPromptOptions,
|
|
80
74
|
GetProjectPromptLabelInput,
|
|
81
75
|
GetProjectPromptLabelResult,
|
|
82
76
|
GetPromptInput,
|
|
@@ -110,33 +104,22 @@ export type {
|
|
|
110
104
|
PromptVersion,
|
|
111
105
|
RenderedChatMessage,
|
|
112
106
|
RenderedPrompt,
|
|
107
|
+
RenderFormat,
|
|
108
|
+
RenderOptions,
|
|
113
109
|
ResolvedPrompt,
|
|
114
110
|
ResolvedPromptEnvelope,
|
|
115
111
|
ResolvedToolCall,
|
|
116
112
|
ResolvedToolDefinition,
|
|
117
|
-
ResolvePromptInput,
|
|
118
|
-
ResolvePromptResult,
|
|
119
113
|
RowError,
|
|
120
114
|
TextChatMessage,
|
|
121
115
|
TextChatRole,
|
|
116
|
+
UnknownRoleMode,
|
|
122
117
|
UpdateProjectPromptLabelInput,
|
|
123
118
|
UpdateProjectPromptLabelResult,
|
|
124
119
|
UpdatePromptInput,
|
|
125
120
|
UpdatePromptResult,
|
|
126
121
|
} from "./types";
|
|
127
122
|
|
|
128
|
-
const zResolvedPromptLegacy = zResolvedPrompt.pick({
|
|
129
|
-
promptId: true,
|
|
130
|
-
versionId: true,
|
|
131
|
-
version: true,
|
|
132
|
-
label: true,
|
|
133
|
-
contentHash: true,
|
|
134
|
-
rendered: true,
|
|
135
|
-
tools: true,
|
|
136
|
-
toolChoice: true,
|
|
137
|
-
modelConfig: true,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
123
|
const zResolvedPromptEnvelope = zResolvedPrompt
|
|
141
124
|
.partial({ releaseId: true })
|
|
142
125
|
.extend({ strategy: z.enum(["single", "weighted"]).optional() });
|
|
@@ -153,22 +136,28 @@ function getProjectPromptsPath(projectId: string): string {
|
|
|
153
136
|
}
|
|
154
137
|
|
|
155
138
|
/**
|
|
156
|
-
* Project a server `rendered` payload into the `messages
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
139
|
+
* Project a server `rendered` payload into the enriched result's `messages`.
|
|
140
|
+
*
|
|
141
|
+
* Prompts resolve as chat blocks. The deprecated `text` format is no longer a
|
|
142
|
+
* first-class case: a non-chat payload throws rather than being silently
|
|
143
|
+
* coerced (a text prompt is sugar for a single-block chat and is being removed
|
|
144
|
+
* from the API).
|
|
160
145
|
*/
|
|
161
|
-
function renderedToMessages(
|
|
162
|
-
|
|
163
|
-
|
|
146
|
+
function renderedToMessages(
|
|
147
|
+
rendered: ResolvedPrompt["rendered"],
|
|
148
|
+
): RenderedChatMessage[] {
|
|
149
|
+
if (rendered.format !== "chat") {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"[to11ai/sdk] Unsupported non-chat prompt format; author the prompt as chat blocks.",
|
|
152
|
+
);
|
|
164
153
|
}
|
|
165
|
-
return
|
|
154
|
+
return rendered.messages;
|
|
166
155
|
}
|
|
167
156
|
|
|
168
157
|
/**
|
|
169
|
-
* Normalize the server resolve envelope into the enriched
|
|
158
|
+
* Normalize the server resolve envelope into the enriched `RenderedPrompt`.
|
|
170
159
|
*
|
|
171
|
-
* - `messages` is
|
|
160
|
+
* - `messages` is derived from the `rendered` chat payload.
|
|
172
161
|
* - `releaseId` is ALWAYS a key: a string when the server sends one, else
|
|
173
162
|
* explicit `null` (R5 `string | null`).
|
|
174
163
|
* - `variantName` is present IFF the server sent one (i.e. weighted release);
|
|
@@ -177,9 +166,10 @@ function renderedToMessages(rendered: RenderedPrompt): RenderedChatMessage[] {
|
|
|
177
166
|
*/
|
|
178
167
|
function normalizeResolvedEnvelope(
|
|
179
168
|
raw: ResolvedPrompt | ResolvedPromptEnvelope,
|
|
180
|
-
|
|
169
|
+
slug: string,
|
|
170
|
+
): RenderedPrompt {
|
|
181
171
|
const provenance = raw as ResolvedPromptEnvelope;
|
|
182
|
-
const result:
|
|
172
|
+
const result: RenderedPrompt = {
|
|
183
173
|
messages: renderedToMessages(raw.rendered),
|
|
184
174
|
version: raw.version,
|
|
185
175
|
releaseId: provenance.releaseId ?? null,
|
|
@@ -188,7 +178,10 @@ function normalizeResolvedEnvelope(
|
|
|
188
178
|
versionId: raw.versionId,
|
|
189
179
|
contentHash: raw.contentHash,
|
|
190
180
|
label: raw.label,
|
|
191
|
-
|
|
181
|
+
// The requested slug — the API resolve envelope does not echo it, so it
|
|
182
|
+
// is threaded from the `render()` input (TO11-2727). Surfaced as
|
|
183
|
+
// `x-to11-prompt-slug` for trace readability.
|
|
184
|
+
slug,
|
|
192
185
|
modelConfig: provenance.modelConfig,
|
|
193
186
|
};
|
|
194
187
|
// `tools` is present IFF the resolved version declares tool definitions; the
|
|
@@ -219,20 +212,26 @@ function normalizeResolvedEnvelope(
|
|
|
219
212
|
}
|
|
220
213
|
|
|
221
214
|
/**
|
|
222
|
-
* Lift a caller-supplied offline `fallback` into the
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* own value or explicit `null`; `blockRenderRecord` → `{}`; `variantName`
|
|
227
|
-
* stays absent).
|
|
215
|
+
* Lift a caller-supplied offline `fallback` into the returned shape (D7), so
|
|
216
|
+
* `render()` returns an identical shape on the online and offline paths. The
|
|
217
|
+
* fallback's own slug is preferred, falling back to the `render()` input when it
|
|
218
|
+
* carries none, so `x-to11-prompt-slug` is still emitted.
|
|
228
219
|
*/
|
|
229
|
-
function
|
|
230
|
-
fallback:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
220
|
+
function liftFallbackToRendered(
|
|
221
|
+
fallback: RenderedPrompt,
|
|
222
|
+
slug: string,
|
|
223
|
+
): RenderedPrompt {
|
|
224
|
+
// Defensive: `fallback` is typed `RenderedPrompt`, but an untyped JS caller,
|
|
225
|
+
// an `as` cast, or a persisted pre-2.0 `resolve()` result could pass the old
|
|
226
|
+
// `ResolvedPrompt` shape (no top-level `messages`). Fail loudly rather than
|
|
227
|
+
// return a malformed result with `undefined` messages.
|
|
228
|
+
if (!Array.isArray((fallback as { messages?: unknown }).messages)) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
"[to11ai/sdk] `fallback` must be a RenderedPrompt (as returned by render()); the legacy resolve() shape is no longer supported.",
|
|
231
|
+
);
|
|
234
232
|
}
|
|
235
|
-
|
|
233
|
+
// `||` (not `??`) so an empty-string slug also falls through to the input.
|
|
234
|
+
return { ...structuredClone(fallback), slug: fallback.slug || slug };
|
|
236
235
|
}
|
|
237
236
|
|
|
238
237
|
function getProjectPromptPath(projectId: string, promptId: string): string {
|
|
@@ -316,10 +315,29 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
316
315
|
this.onUnknownRole = config?.onUnknownRole;
|
|
317
316
|
}
|
|
318
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Resolve the project for a control-plane call: the per-call `projectId`
|
|
320
|
+
* wins, else the client-bound one from `createClient({ projectId })`. Throws
|
|
321
|
+
* if neither is set. Mirrors how `render()` resolves its project, so every
|
|
322
|
+
* method honors the bound `projectId` and callers need not repeat it.
|
|
323
|
+
*/
|
|
324
|
+
private resolveProjectId(perCall?: string): string {
|
|
325
|
+
const projectId = perCall ?? this.projectId;
|
|
326
|
+
if (projectId === undefined || projectId.trim() === "") {
|
|
327
|
+
throw new Error(
|
|
328
|
+
"[to11ai/sdk] this call needs a `projectId` — pass it on the call or bind one via createClient({ projectId, ... }).",
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
// Trim so a whitespace-padded per-call id normalizes like the client-bound
|
|
332
|
+
// one (resolved via `pick()`), instead of reaching the wire untrimmed.
|
|
333
|
+
return projectId.trim();
|
|
334
|
+
}
|
|
335
|
+
|
|
319
336
|
// ─── Create ──────────────────────────────────────────────────────────────
|
|
320
337
|
|
|
321
338
|
async create(input: CreatePromptInput): Promise<CreatePromptResult> {
|
|
322
|
-
const { projectId, ...body } = input;
|
|
339
|
+
const { projectId: perCallProjectId, ...body } = input;
|
|
340
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
323
341
|
const envelope = await this.transport.request<CreatePromptResult>(
|
|
324
342
|
"POST",
|
|
325
343
|
getProjectPromptsPath(projectId),
|
|
@@ -331,9 +349,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
331
349
|
// ─── List ─────────────────────────────────────────────────────────────────
|
|
332
350
|
|
|
333
351
|
async list(
|
|
334
|
-
input: ListPromptsInput,
|
|
352
|
+
input: ListPromptsInput = {},
|
|
335
353
|
): Promise<PaginatedResult<ListPromptsResult>> {
|
|
336
|
-
const { projectId, ...queryRest } = input;
|
|
354
|
+
const { projectId: perCallProjectId, ...queryRest } = input;
|
|
355
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
337
356
|
const envelope = await this.transport.request<
|
|
338
357
|
| ListPromptsResult[]
|
|
339
358
|
| {
|
|
@@ -365,7 +384,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
365
384
|
// ─── Get ──────────────────────────────────────────────────────────────────
|
|
366
385
|
|
|
367
386
|
async get(input: GetPromptInput): Promise<GetPromptResult> {
|
|
368
|
-
const { projectId, promptId } = input;
|
|
387
|
+
const { projectId: perCallProjectId, promptId } = input;
|
|
388
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
369
389
|
const envelope = await this.transport.request<GetPromptResult>(
|
|
370
390
|
"GET",
|
|
371
391
|
getProjectPromptPath(projectId, promptId),
|
|
@@ -376,7 +396,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
376
396
|
// ─── Update ───────────────────────────────────────────────────────────────
|
|
377
397
|
|
|
378
398
|
async update(input: UpdatePromptInput): Promise<UpdatePromptResult> {
|
|
379
|
-
const { projectId, promptId, ...body } = input;
|
|
399
|
+
const { projectId: perCallProjectId, promptId, ...body } = input;
|
|
400
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
380
401
|
const envelope = await this.transport.request<UpdatePromptResult>(
|
|
381
402
|
"PATCH",
|
|
382
403
|
getProjectPromptPath(projectId, promptId),
|
|
@@ -388,7 +409,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
388
409
|
// ─── Archive ──────────────────────────────────────────────────────────────
|
|
389
410
|
|
|
390
411
|
async archive(input: ArchivePromptInput): Promise<ArchivePromptResult> {
|
|
391
|
-
const { projectId, promptId } = input;
|
|
412
|
+
const { projectId: perCallProjectId, promptId } = input;
|
|
413
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
392
414
|
const envelope = await this.transport.request<ArchivePromptResult>(
|
|
393
415
|
"DELETE",
|
|
394
416
|
getProjectPromptPath(projectId, promptId),
|
|
@@ -401,7 +423,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
401
423
|
async createVersion(
|
|
402
424
|
input: CreatePromptVersionInput,
|
|
403
425
|
): Promise<CreatePromptVersionResult> {
|
|
404
|
-
const { projectId, promptId, ...body } = input;
|
|
426
|
+
const { projectId: perCallProjectId, promptId, ...body } = input;
|
|
427
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
405
428
|
const envelope = await this.transport.request<CreatePromptVersionResult>(
|
|
406
429
|
"POST",
|
|
407
430
|
`${getProjectPromptPath(projectId, promptId)}/versions`,
|
|
@@ -415,7 +438,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
415
438
|
async listVersions(
|
|
416
439
|
input: ListPromptVersionsInput,
|
|
417
440
|
): Promise<ListPromptVersionsResult> {
|
|
418
|
-
const { projectId, promptId } = input;
|
|
441
|
+
const { projectId: perCallProjectId, promptId } = input;
|
|
442
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
419
443
|
const envelope = await this.transport.request<ListPromptVersionsResult>(
|
|
420
444
|
"GET",
|
|
421
445
|
`${getProjectPromptPath(projectId, promptId)}/versions`,
|
|
@@ -428,7 +452,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
428
452
|
async getVersion(
|
|
429
453
|
input: GetPromptVersionInput,
|
|
430
454
|
): Promise<GetPromptVersionResult> {
|
|
431
|
-
const { projectId, promptId, versionNumber } = input;
|
|
455
|
+
const { projectId: perCallProjectId, promptId, versionNumber } = input;
|
|
456
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
432
457
|
const envelope = await this.transport.request<GetPromptVersionResult>(
|
|
433
458
|
"GET",
|
|
434
459
|
`${getProjectPromptPath(projectId, promptId)}/versions/${encodeURIComponent(String(versionNumber))}`,
|
|
@@ -439,7 +464,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
439
464
|
// ─── Move Label ───────────────────────────────────────────────────────────
|
|
440
465
|
|
|
441
466
|
async moveLabel(input: MovePromptLabelInput): Promise<MovePromptLabelResult> {
|
|
442
|
-
const { projectId, promptId, label, ...body } = input;
|
|
467
|
+
const { projectId: perCallProjectId, promptId, label, ...body } = input;
|
|
468
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
443
469
|
const envelope = await this.transport.request<MovePromptLabelResult>(
|
|
444
470
|
"PUT",
|
|
445
471
|
`${getProjectPromptPath(projectId, promptId)}/labels/${encodeURIComponent(label)}`,
|
|
@@ -453,7 +479,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
453
479
|
async listLabels(
|
|
454
480
|
input: ListPromptLabelsInput,
|
|
455
481
|
): Promise<ListPromptLabelsResult> {
|
|
456
|
-
const { projectId, promptId } = input;
|
|
482
|
+
const { projectId: perCallProjectId, promptId } = input;
|
|
483
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
457
484
|
const envelope = await this.transport.request<ListPromptLabelsResult>(
|
|
458
485
|
"GET",
|
|
459
486
|
`${getProjectPromptPath(projectId, promptId)}/labels`,
|
|
@@ -468,8 +495,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
468
495
|
* `projectId` + `env`, in one control-plane request. With a `format` (per
|
|
469
496
|
* call or inherited from the client) the result is provider-shaped
|
|
470
497
|
* `{ messages, config, metadata }`; without one it is the neutral
|
|
471
|
-
* `
|
|
472
|
-
* resolution see {@link PromptResource.resolve | `resolve()`}.
|
|
498
|
+
* `RenderedPrompt`. Resolves by the client's configured `env`.
|
|
473
499
|
*
|
|
474
500
|
* @example Render an OpenAI-shaped prompt and send it through the gateway
|
|
475
501
|
* const prompt = await to11.prompts.render("weather-concierge", {
|
|
@@ -492,15 +518,15 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
492
518
|
async render(
|
|
493
519
|
slug: string,
|
|
494
520
|
options: RenderOptions = {},
|
|
495
|
-
): Promise<
|
|
496
|
-
const
|
|
497
|
-
return shapePrompt(
|
|
521
|
+
): Promise<RenderedPrompt | OpenAiRenderedPrompt | AnthropicRenderedPrompt> {
|
|
522
|
+
const rendered = await this.resolveToRendered(slug, options);
|
|
523
|
+
return shapePrompt(rendered, options.format ?? this.format, {
|
|
498
524
|
onUnknownRole: options.onUnknownRole ?? this.onUnknownRole,
|
|
499
525
|
});
|
|
500
526
|
}
|
|
501
527
|
|
|
502
528
|
/**
|
|
503
|
-
* Env-aware resolution → neutral `
|
|
529
|
+
* Env-aware resolution → neutral `RenderedPrompt`. Carries the resolve /
|
|
504
530
|
* cache / offline-fallback logic; `render()` is the public shaping surface.
|
|
505
531
|
*
|
|
506
532
|
* - **env→label wire mapping:** the resolve route is label-based
|
|
@@ -513,10 +539,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
513
539
|
* WITH a `subject`; BYPASSES the cache for weighted releases with NO
|
|
514
540
|
* `subject` (a uniform-random assignment must not be pinned for the TTL).
|
|
515
541
|
*/
|
|
516
|
-
private async
|
|
542
|
+
private async resolveToRendered(
|
|
517
543
|
slug: string,
|
|
518
|
-
options:
|
|
519
|
-
): Promise<
|
|
544
|
+
options: RenderOptions = {},
|
|
545
|
+
): Promise<RenderedPrompt> {
|
|
520
546
|
if (this.projectId === undefined || this.projectId.trim() === "") {
|
|
521
547
|
throw new Error(
|
|
522
548
|
"[to11ai/sdk] `prompts.render()` requires a `projectId` on the client — pass it to createClient({ projectId, ... }).",
|
|
@@ -543,7 +569,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
543
569
|
|
|
544
570
|
const { subject, variables, cache: useCache = true, fallback } = options;
|
|
545
571
|
|
|
546
|
-
const cacheKey =
|
|
572
|
+
const cacheKey = buildRenderCacheKey(
|
|
547
573
|
this.projectId,
|
|
548
574
|
slug,
|
|
549
575
|
env,
|
|
@@ -553,9 +579,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
553
579
|
|
|
554
580
|
if (useCache) {
|
|
555
581
|
const cached = this.cache.get(cacheKey);
|
|
556
|
-
|
|
557
|
-
// namespace ("fetch|…") never collides with the legacy resolve key.
|
|
558
|
-
if (cached && "messages" in cached) return structuredClone(cached);
|
|
582
|
+
if (cached) return structuredClone(cached);
|
|
559
583
|
}
|
|
560
584
|
|
|
561
585
|
// env→label mapping: the env slug is the single resolution label.
|
|
@@ -574,7 +598,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
574
598
|
},
|
|
575
599
|
);
|
|
576
600
|
const raw = unwrap(envelope);
|
|
577
|
-
const result = normalizeResolvedEnvelope(raw);
|
|
601
|
+
const result = normalizeResolvedEnvelope(raw, slug);
|
|
578
602
|
|
|
579
603
|
// D8: bypass the cache for a weighted release with NO subject —
|
|
580
604
|
// caching a uniform-random assignment would pin one variant for the
|
|
@@ -599,64 +623,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
599
623
|
// client-eager throw already happened above the try, so there is no
|
|
600
624
|
// network error to trigger it on the env-missing path.
|
|
601
625
|
if (err instanceof To11aiNetworkError && fallback !== undefined) {
|
|
602
|
-
return
|
|
603
|
-
}
|
|
604
|
-
throw err;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// ─── Resolve ──────────────────────────────────────────────────────────────
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* Resolve a prompt by explicit `labels` for a given `projectId`, in one
|
|
612
|
-
* control-plane request. This is the label-addressed counterpart to
|
|
613
|
-
* {@link PromptResource.render | `render()`}, which resolves by the client's
|
|
614
|
-
* configured `env`. Reach for `resolve()` when you address prompts by label
|
|
615
|
-
* rather than by environment; it is env-agnostic, so it carries no `env` /
|
|
616
|
-
* `subject`, no enriched release/variant provenance, and no typed-env errors,
|
|
617
|
-
* and is not subject to the env-based no-silent-fallback gate.
|
|
618
|
-
*/
|
|
619
|
-
async resolve(input: ResolvePromptInput): Promise<ResolvePromptResult> {
|
|
620
|
-
const {
|
|
621
|
-
projectId,
|
|
622
|
-
slug,
|
|
623
|
-
labels,
|
|
624
|
-
variables,
|
|
625
|
-
cache: useCache = true,
|
|
626
|
-
fallback,
|
|
627
|
-
} = input;
|
|
628
|
-
|
|
629
|
-
const cacheKey = buildCacheKey(projectId, slug, labels, variables);
|
|
630
|
-
|
|
631
|
-
if (useCache) {
|
|
632
|
-
const cached = this.cache.get(cacheKey);
|
|
633
|
-
// The legacy resolve key namespace only ever stores ResolvedPrompt
|
|
634
|
-
// entries (the fetch namespace is "fetch|…"); the `messages` guard
|
|
635
|
-
// keeps the static return type as ResolvedPrompt.
|
|
636
|
-
if (cached && !("messages" in cached)) return structuredClone(cached);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
try {
|
|
640
|
-
const envelope = await this.transport.request<ResolvePromptResult>(
|
|
641
|
-
"POST",
|
|
642
|
-
`${getProjectPromptsPath(projectId)}:resolve`,
|
|
643
|
-
{
|
|
644
|
-
body: { slug, labels, variables },
|
|
645
|
-
decode: (data) =>
|
|
646
|
-
zResolvedPromptLegacy.parse(data) as ResolvePromptResult,
|
|
647
|
-
},
|
|
648
|
-
);
|
|
649
|
-
const result = unwrap(envelope);
|
|
650
|
-
|
|
651
|
-
if (useCache) {
|
|
652
|
-
this.cache.set(cacheKey, structuredClone(result));
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
return result;
|
|
656
|
-
} catch (err) {
|
|
657
|
-
// If network error and we have a fallback, serve it
|
|
658
|
-
if (err instanceof To11aiNetworkError && fallback !== undefined) {
|
|
659
|
-
return fallback;
|
|
626
|
+
return liftFallbackToRendered(fallback, slug);
|
|
660
627
|
}
|
|
661
628
|
throw err;
|
|
662
629
|
}
|
|
@@ -672,9 +639,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
672
639
|
* affordances). Gated on `prompt:read` server-side.
|
|
673
640
|
*/
|
|
674
641
|
async listProjectLabels(
|
|
675
|
-
input: ListProjectPromptLabelsInput,
|
|
642
|
+
input: ListProjectPromptLabelsInput = {},
|
|
676
643
|
): Promise<ListProjectPromptLabelsResult> {
|
|
677
|
-
const { projectId, includeArchived } = input;
|
|
644
|
+
const { projectId: perCallProjectId, includeArchived } = input;
|
|
645
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
678
646
|
const envelope =
|
|
679
647
|
await this.transport.request<ListProjectPromptLabelsResult>(
|
|
680
648
|
"GET",
|
|
@@ -696,7 +664,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
696
664
|
async createProjectLabel(
|
|
697
665
|
input: CreateProjectPromptLabelInput,
|
|
698
666
|
): Promise<CreateProjectPromptLabelResult> {
|
|
699
|
-
const { projectId, ...body } = input;
|
|
667
|
+
const { projectId: perCallProjectId, ...body } = input;
|
|
668
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
700
669
|
const envelope =
|
|
701
670
|
await this.transport.request<CreateProjectPromptLabelResult>(
|
|
702
671
|
"POST",
|
|
@@ -709,9 +678,10 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
709
678
|
async getProjectLabel(
|
|
710
679
|
input: GetProjectPromptLabelInput,
|
|
711
680
|
): Promise<GetProjectPromptLabelResult> {
|
|
681
|
+
const projectId = this.resolveProjectId(input.projectId);
|
|
712
682
|
const envelope = await this.transport.request<GetProjectPromptLabelResult>(
|
|
713
683
|
"GET",
|
|
714
|
-
getProjectPromptLabelPath(
|
|
684
|
+
getProjectPromptLabelPath(projectId, input.label),
|
|
715
685
|
);
|
|
716
686
|
return unwrap(envelope);
|
|
717
687
|
}
|
|
@@ -727,7 +697,8 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
727
697
|
async updateProjectLabel(
|
|
728
698
|
input: UpdateProjectPromptLabelInput,
|
|
729
699
|
): Promise<UpdateProjectPromptLabelResult> {
|
|
730
|
-
const { projectId, label, ...rest } = input;
|
|
700
|
+
const { projectId: perCallProjectId, label, ...rest } = input;
|
|
701
|
+
const projectId = this.resolveProjectId(perCallProjectId);
|
|
731
702
|
const body: Record<string, unknown> = {};
|
|
732
703
|
if (rest.isDefault !== undefined) body.isDefault = rest.isDefault;
|
|
733
704
|
if (rest.description !== undefined) body.description = rest.description;
|
|
@@ -753,10 +724,11 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
753
724
|
async deleteProjectLabel(
|
|
754
725
|
input: DeleteProjectPromptLabelInput,
|
|
755
726
|
): Promise<DeleteProjectPromptLabelResult> {
|
|
727
|
+
const projectId = this.resolveProjectId(input.projectId);
|
|
756
728
|
const envelope =
|
|
757
729
|
await this.transport.request<DeleteProjectPromptLabelResult>(
|
|
758
730
|
"DELETE",
|
|
759
|
-
getProjectPromptLabelPath(
|
|
731
|
+
getProjectPromptLabelPath(projectId, input.label),
|
|
760
732
|
);
|
|
761
733
|
return unwrap(envelope);
|
|
762
734
|
}
|
|
@@ -775,7 +747,7 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
775
747
|
const envelope =
|
|
776
748
|
await this.transport.request<ListProjectPromptLabelUsagesResult>(
|
|
777
749
|
"GET",
|
|
778
|
-
`${getProjectPromptLabelPath(input.projectId, input.label)}/usages`,
|
|
750
|
+
`${getProjectPromptLabelPath(this.resolveProjectId(input.projectId), input.label)}/usages`,
|
|
779
751
|
);
|
|
780
752
|
return unwrap(envelope);
|
|
781
753
|
}
|
|
@@ -800,7 +772,9 @@ export class PromptResource<F extends RenderFormat | undefined = undefined> {
|
|
|
800
772
|
async listLabelEvents(
|
|
801
773
|
input: ListLabelEventsInput,
|
|
802
774
|
): Promise<ListLabelEventsResult> {
|
|
803
|
-
const projectSeg = encodeURIComponent(
|
|
775
|
+
const projectSeg = encodeURIComponent(
|
|
776
|
+
this.resolveProjectId(input.projectId),
|
|
777
|
+
);
|
|
804
778
|
let path: string;
|
|
805
779
|
switch (input.scope) {
|
|
806
780
|
case "label":
|