@to11ai/sdk 1.1.0 → 2.0.0-rc.2
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 +88 -18
- package/dist/client.d.ts +141 -39
- package/dist/client.js +94 -19
- package/dist/config.d.ts +9 -0
- package/dist/context.d.ts +124 -0
- package/dist/errors.cjs +18 -1
- package/dist/errors.d.ts +18 -1
- package/dist/errors.js +18 -1
- package/dist/gateway.cjs +65 -22
- package/dist/gateway.d.ts +65 -9
- package/dist/gateway.js +65 -22
- package/dist/generated/index.d.ts +1 -1
- package/dist/generated/types.gen.d.ts +1299 -167
- package/dist/generated/zod.gen.d.ts +393 -232
- package/dist/prompts/cache.d.ts +9 -10
- package/dist/prompts/resource.cjs +29 -67
- package/dist/prompts/resource.d.ts +52 -32
- package/dist/prompts/resource.js +30 -68
- package/dist/prompts/shape.d.ts +56 -0
- package/dist/prompts/types.d.ts +44 -31
- package/package.json +1 -1
- package/src/client.ts +337 -82
- package/src/config.ts +13 -0
- package/src/context.ts +198 -0
- package/src/errors.ts +33 -3
- package/src/gateway.ts +148 -33
- package/src/generated/index.ts +1 -1
- package/src/generated/types.gen.ts +1310 -167
- package/src/generated/zod.gen.ts +361 -271
- package/src/prompts/cache.ts +11 -15
- package/src/prompts/resource.ts +129 -142
- package/src/prompts/shape.ts +152 -0
- package/src/prompts/types.ts +45 -34
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability context — the `session → conversation → turn` hierarchy that
|
|
3
|
+
* maps onto to11's telemetry grouping (Session → Conversation → Turn →
|
|
4
|
+
* LLM-Call). Each level provides a lazy default for the level below; explicit
|
|
5
|
+
* control is opt-in per use case. See
|
|
6
|
+
* docs/product-specs/2026-07-14-to11-2691-sdk-simpler-setup.md.
|
|
7
|
+
*
|
|
8
|
+
* Header-only: `turn.headers(prompt?)` returns a plain object the caller applies
|
|
9
|
+
* to their own provider client. It never sends a request.
|
|
10
|
+
*/
|
|
11
|
+
import { type GatewayPromptProvenance } from "./gateway";
|
|
12
|
+
/** Auth/config the client hands to the context for gateway header building. */
|
|
13
|
+
export interface TurnAuthContext {
|
|
14
|
+
readonly apiKey: string;
|
|
15
|
+
readonly projectId: string;
|
|
16
|
+
readonly env?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A prompt argument accepted by `turn.headers()`. Either the flat provenance
|
|
20
|
+
* (a neutral `RenderedPrompt`) or a `format`-shaped result carrying it under
|
|
21
|
+
* `metadata` — both are accepted so the format and neutral render paths carry
|
|
22
|
+
* provenance identically.
|
|
23
|
+
*/
|
|
24
|
+
export type PromptForHeaders = GatewayPromptProvenance | {
|
|
25
|
+
metadata: GatewayPromptProvenance;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* One agent turn — a single trace. A turn's `traceparent` is fixed for its
|
|
29
|
+
* lifetime, so every LLM-call made under it groups into one trace.
|
|
30
|
+
*/
|
|
31
|
+
export declare class Turn {
|
|
32
|
+
private readonly auth;
|
|
33
|
+
readonly sessionId: string;
|
|
34
|
+
readonly conversationId: string;
|
|
35
|
+
/** The fixed W3C traceparent for this turn (one trace). */
|
|
36
|
+
readonly traceparent: string;
|
|
37
|
+
constructor(auth: TurnAuthContext, sessionId: string, conversationId: string, traceparent?: string);
|
|
38
|
+
/**
|
|
39
|
+
* The complete gateway header bag: auth + session + conversation +
|
|
40
|
+
* traceparent, plus prompt provenance when a `prompt` is supplied. A pure
|
|
41
|
+
* read of the turn's fixed ids — reuse it across a tool-loop or call per
|
|
42
|
+
* request; the result is identical.
|
|
43
|
+
*
|
|
44
|
+
* Only valid for requests targeting the gateway (`GATEWAY_URL`); it carries
|
|
45
|
+
* the to11 API key and must never be attached to a direct provider endpoint.
|
|
46
|
+
*
|
|
47
|
+
* @example One call (inline turn)
|
|
48
|
+
* const res = await openai.chat.completions.create(
|
|
49
|
+
* { ...prompt.config, messages: prompt.messages },
|
|
50
|
+
* { headers: to11.turn().headers(prompt) },
|
|
51
|
+
* );
|
|
52
|
+
*
|
|
53
|
+
* @example Tool loop — hoist the turn so every call shares one trace
|
|
54
|
+
* const turn = to11.turn();
|
|
55
|
+
* const headers = turn.headers(prompt);
|
|
56
|
+
* while (true) {
|
|
57
|
+
* const res = await openai.chat.completions.create({ ...prompt.config, messages, tools }, { headers });
|
|
58
|
+
* // …reuse `headers` for every call in the loop…
|
|
59
|
+
* }
|
|
60
|
+
*/
|
|
61
|
+
headers(prompt?: PromptForHeaders): Record<string, string>;
|
|
62
|
+
}
|
|
63
|
+
/** One agent thread — a stable conversation id shared across all its turns. */
|
|
64
|
+
export declare class Conversation {
|
|
65
|
+
private readonly auth;
|
|
66
|
+
readonly sessionId: string;
|
|
67
|
+
/** Stable conversation id, reused by every turn created from this object. */
|
|
68
|
+
readonly id: string;
|
|
69
|
+
constructor(auth: TurnAuthContext, sessionId: string, id?: string);
|
|
70
|
+
/** Start a new turn (fresh trace) in this conversation. */
|
|
71
|
+
turn(): Turn;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* One unit of work. Groups conversations under a single session id. A session
|
|
75
|
+
* lazily owns one default conversation, used by `session.turn()`; call
|
|
76
|
+
* `session.conversation()` for additional (side/parallel) agent threads.
|
|
77
|
+
*/
|
|
78
|
+
export declare class Session {
|
|
79
|
+
private readonly auth;
|
|
80
|
+
/** Stable session id, carried by every conversation and turn beneath it. */
|
|
81
|
+
readonly id: string;
|
|
82
|
+
private defaultConversation;
|
|
83
|
+
constructor(auth: TurnAuthContext, id?: string);
|
|
84
|
+
/**
|
|
85
|
+
* Create a distinct conversation (a separate agent thread) in this session.
|
|
86
|
+
* Use this when one unit of work runs multiple agents (side / parallel
|
|
87
|
+
* agents): each conversation gets its own `x-to11-conversation-id`, all under
|
|
88
|
+
* the one session.
|
|
89
|
+
*
|
|
90
|
+
* @example Multiple agents in one session
|
|
91
|
+
* const session = to11.session();
|
|
92
|
+
* const main = session.conversation(); // main agent thread
|
|
93
|
+
* const research = session.conversation(); // separate research thread, same session
|
|
94
|
+
*
|
|
95
|
+
* await openai.chat.completions.create(
|
|
96
|
+
* { ...mainPrompt.config, messages: mainMessages },
|
|
97
|
+
* { headers: main.turn().headers(mainPrompt) },
|
|
98
|
+
* );
|
|
99
|
+
* await openai.chat.completions.create(
|
|
100
|
+
* { ...researchPrompt.config, messages: researchMessages },
|
|
101
|
+
* { headers: research.turn().headers(researchPrompt) },
|
|
102
|
+
* );
|
|
103
|
+
*/
|
|
104
|
+
conversation(id?: string): Conversation;
|
|
105
|
+
/**
|
|
106
|
+
* Start a turn in the session's single implicit conversation. The default
|
|
107
|
+
* conversation is created lazily on first call and reused, so consecutive
|
|
108
|
+
* `session.turn()` calls share one `x-to11-conversation-id` (each with its
|
|
109
|
+
* own `traceparent`).
|
|
110
|
+
*
|
|
111
|
+
* @example Multi-turn chat — every turn shares the session's conversation
|
|
112
|
+
* const session = to11.session();
|
|
113
|
+
* for await (const input of inputs) {
|
|
114
|
+
* messages.push({ role: "user", content: input });
|
|
115
|
+
* const turn = session.turn(); // fresh trace, same conversation id
|
|
116
|
+
* const res = await openai.chat.completions.create(
|
|
117
|
+
* { ...prompt.config, messages },
|
|
118
|
+
* { headers: turn.headers(prompt) },
|
|
119
|
+
* );
|
|
120
|
+
* messages.push(res.choices[0].message);
|
|
121
|
+
* }
|
|
122
|
+
*/
|
|
123
|
+
turn(): Turn;
|
|
124
|
+
}
|
package/dist/errors.cjs
CHANGED
|
@@ -39,6 +39,7 @@ var __export = (target, all) => {
|
|
|
39
39
|
// src/errors.ts
|
|
40
40
|
var exports_errors = {};
|
|
41
41
|
__export(exports_errors, {
|
|
42
|
+
UnknownRoleError: () => UnknownRoleError,
|
|
42
43
|
To11aiValidationError: () => To11aiValidationError,
|
|
43
44
|
To11aiResponseValidationError: () => To11aiResponseValidationError,
|
|
44
45
|
To11aiRateLimitError: () => To11aiRateLimitError,
|
|
@@ -130,6 +131,22 @@ class To11aiResponseValidationError extends To11aiError {
|
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
class UnknownRoleError extends To11aiError {
|
|
135
|
+
code = "unknown_role";
|
|
136
|
+
role;
|
|
137
|
+
index;
|
|
138
|
+
constructor(role, index) {
|
|
139
|
+
super(`[to11ai/sdk] message[${index}] has unknown role ${JSON.stringify(role)}; expected system|user|assistant|developer|tool`);
|
|
140
|
+
this.name = "UnknownRoleError";
|
|
141
|
+
this.role = role;
|
|
142
|
+
this.index = index;
|
|
143
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
144
|
+
}
|
|
145
|
+
static is(err) {
|
|
146
|
+
return err instanceof UnknownRoleError;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
133
150
|
class PromptLabelNotRegisteredError extends To11aiApiError {
|
|
134
151
|
projectId;
|
|
135
152
|
label;
|
|
@@ -218,7 +235,7 @@ class EnvNotFoundError extends To11aiApiError {
|
|
|
218
235
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
219
236
|
}
|
|
220
237
|
static missing(env) {
|
|
221
|
-
return new EnvNotFoundError("[to11ai/sdk] No environment resolvable for prompts.
|
|
238
|
+
return new EnvNotFoundError("[to11ai/sdk] No environment resolvable for prompts.render(): none supplied at client init and none supplied per call. Pass `env` to createClient or render(slug, { env }).", { env, source: "missing" }, undefined, 0);
|
|
222
239
|
}
|
|
223
240
|
static is(err) {
|
|
224
241
|
return err instanceof EnvNotFoundError;
|
package/dist/errors.d.ts
CHANGED
|
@@ -51,6 +51,23 @@ export declare class To11aiResponseValidationError extends To11aiError {
|
|
|
51
51
|
}[];
|
|
52
52
|
constructor(path: string, zodError: unknown, cause?: unknown);
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Thrown by the provider-shape converters (`toOpenAIMessages` /
|
|
56
|
+
* `toAnthropicMessages`, and `render({ format })`) in `onUnknownRole: "error"`
|
|
57
|
+
* mode when a message carries a role outside the known set
|
|
58
|
+
* (`system | user | assistant | developer | tool`). `code` is the stable
|
|
59
|
+
* `"unknown_role"` slug; `role` / `index` locate the offending turn.
|
|
60
|
+
*
|
|
61
|
+
* A client-side conversion error — it extends `To11aiError` (not
|
|
62
|
+
* `To11aiApiError`), so it never carries an HTTP status.
|
|
63
|
+
*/
|
|
64
|
+
export declare class UnknownRoleError extends To11aiError {
|
|
65
|
+
readonly code = "unknown_role";
|
|
66
|
+
readonly role: string;
|
|
67
|
+
readonly index: number;
|
|
68
|
+
constructor(role: string, index: number);
|
|
69
|
+
static is(err: unknown): err is UnknownRoleError;
|
|
70
|
+
}
|
|
54
71
|
/**
|
|
55
72
|
* Thrown when strict-mode rejects a label move for an unregistered label.
|
|
56
73
|
*
|
|
@@ -168,7 +185,7 @@ export interface EnvNotFoundReason {
|
|
|
168
185
|
source: "missing" | "unknown";
|
|
169
186
|
}
|
|
170
187
|
/**
|
|
171
|
-
* Thrown when no serving environment can be resolved for a `prompts.
|
|
188
|
+
* Thrown when no serving environment can be resolved for a `prompts.render()`
|
|
172
189
|
* call. One class, two `reason.source` discriminants (see
|
|
173
190
|
* `EnvNotFoundReason`): client-eager `"missing"` (R4) and server-forwarded
|
|
174
191
|
* `"unknown"` (`ENV_NOT_FOUND`).
|
package/dist/errors.js
CHANGED
|
@@ -70,6 +70,22 @@ class To11aiResponseValidationError extends To11aiError {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
class UnknownRoleError extends To11aiError {
|
|
74
|
+
code = "unknown_role";
|
|
75
|
+
role;
|
|
76
|
+
index;
|
|
77
|
+
constructor(role, index) {
|
|
78
|
+
super(`[to11ai/sdk] message[${index}] has unknown role ${JSON.stringify(role)}; expected system|user|assistant|developer|tool`);
|
|
79
|
+
this.name = "UnknownRoleError";
|
|
80
|
+
this.role = role;
|
|
81
|
+
this.index = index;
|
|
82
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
83
|
+
}
|
|
84
|
+
static is(err) {
|
|
85
|
+
return err instanceof UnknownRoleError;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
73
89
|
class PromptLabelNotRegisteredError extends To11aiApiError {
|
|
74
90
|
projectId;
|
|
75
91
|
label;
|
|
@@ -158,7 +174,7 @@ class EnvNotFoundError extends To11aiApiError {
|
|
|
158
174
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
159
175
|
}
|
|
160
176
|
static missing(env) {
|
|
161
|
-
return new EnvNotFoundError("[to11ai/sdk] No environment resolvable for prompts.
|
|
177
|
+
return new EnvNotFoundError("[to11ai/sdk] No environment resolvable for prompts.render(): none supplied at client init and none supplied per call. Pass `env` to createClient or render(slug, { env }).", { env, source: "missing" }, undefined, 0);
|
|
162
178
|
}
|
|
163
179
|
static is(err) {
|
|
164
180
|
return err instanceof EnvNotFoundError;
|
|
@@ -221,6 +237,7 @@ class RuleVersionInvalidSnapshotError extends To11aiApiError {
|
|
|
221
237
|
}
|
|
222
238
|
}
|
|
223
239
|
export {
|
|
240
|
+
UnknownRoleError,
|
|
224
241
|
To11aiValidationError,
|
|
225
242
|
To11aiResponseValidationError,
|
|
226
243
|
To11aiRateLimitError,
|
package/dist/gateway.cjs
CHANGED
|
@@ -44,11 +44,13 @@ __export(exports_gateway, {
|
|
|
44
44
|
toOpenAIMessages: () => toOpenAIMessages,
|
|
45
45
|
toAnthropicTools: () => toAnthropicTools,
|
|
46
46
|
toAnthropicToolChoice: () => toAnthropicToolChoice,
|
|
47
|
+
toAnthropicSystem: () => toAnthropicSystem,
|
|
47
48
|
toAnthropicMessages: () => toAnthropicMessages,
|
|
48
49
|
gatewayPromptHeaders: () => gatewayPromptHeaders,
|
|
49
50
|
gatewayAuthHeaders: () => gatewayAuthHeaders
|
|
50
51
|
});
|
|
51
52
|
module.exports = __toCommonJS(exports_gateway);
|
|
53
|
+
var import_errors = require("./errors");
|
|
52
54
|
var import_types = require("./prompts/types");
|
|
53
55
|
function gatewayAuthHeaders(options) {
|
|
54
56
|
if (options.apiKey.trim().length === 0) {
|
|
@@ -77,16 +79,17 @@ function gatewayPromptHeaders(prompt) {
|
|
|
77
79
|
"x-to11-prompt-id": prompt.promptId,
|
|
78
80
|
"x-to11-prompt-version": String(prompt.version)
|
|
79
81
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
headers["x-to11-release-id"] = releaseId;
|
|
82
|
+
if (typeof prompt.releaseId === "string" && prompt.releaseId.length > 0) {
|
|
83
|
+
headers["x-to11-release-id"] = prompt.releaseId;
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
headers["x-to11-variant-name"] = variantName;
|
|
85
|
+
if (typeof prompt.variantName === "string" && prompt.variantName.length > 0) {
|
|
86
|
+
headers["x-to11-variant-name"] = prompt.variantName;
|
|
87
87
|
}
|
|
88
|
-
if (prompt.
|
|
89
|
-
headers["x-to11-prompt-
|
|
88
|
+
if (prompt.label.length > 0) {
|
|
89
|
+
headers["x-to11-prompt-label"] = prompt.label;
|
|
90
|
+
}
|
|
91
|
+
if (typeof prompt.slug === "string" && prompt.slug.length > 0) {
|
|
92
|
+
headers["x-to11-prompt-slug"] = prompt.slug;
|
|
90
93
|
}
|
|
91
94
|
return headers;
|
|
92
95
|
}
|
|
@@ -142,22 +145,44 @@ function requireToolCallId(id) {
|
|
|
142
145
|
}
|
|
143
146
|
return id;
|
|
144
147
|
}
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
var KNOWN_RENDERED_ROLES = new Set([
|
|
149
|
+
"system",
|
|
150
|
+
"user",
|
|
151
|
+
"assistant",
|
|
152
|
+
"developer",
|
|
153
|
+
"tool"
|
|
154
|
+
]);
|
|
155
|
+
function skipUnknownRole(role, index, mode) {
|
|
156
|
+
if (KNOWN_RENDERED_ROLES.has(role))
|
|
157
|
+
return false;
|
|
158
|
+
if (mode === "error")
|
|
159
|
+
throw new import_errors.UnknownRoleError(role, index);
|
|
160
|
+
if (mode === "warn") {
|
|
161
|
+
console.warn(`[to11ai/sdk] dropped message[${index}] with unknown role ${JSON.stringify(role)} (expected system|user|assistant|developer|tool)`);
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
function toOpenAIMessages(messages, options = {}) {
|
|
166
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
167
|
+
const out = [];
|
|
168
|
+
messages.forEach((message, index) => {
|
|
169
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
170
|
+
return;
|
|
147
171
|
if (message.role === "tool") {
|
|
148
|
-
|
|
172
|
+
out.push({
|
|
149
173
|
role: "tool",
|
|
150
174
|
tool_call_id: requireToolCallId(message.toolCallId),
|
|
151
175
|
content: message.content ?? ""
|
|
152
|
-
};
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
153
178
|
}
|
|
154
179
|
if (message.role === "assistant") {
|
|
155
|
-
const
|
|
180
|
+
const msg = {
|
|
156
181
|
role: "assistant",
|
|
157
182
|
content: message.content ?? null
|
|
158
183
|
};
|
|
159
184
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
160
|
-
|
|
185
|
+
msg.tool_calls = message.toolCalls.map((call) => ({
|
|
161
186
|
id: requireToolCallId(call.id),
|
|
162
187
|
type: "function",
|
|
163
188
|
function: {
|
|
@@ -166,17 +191,22 @@ function toOpenAIMessages(messages) {
|
|
|
166
191
|
}
|
|
167
192
|
}));
|
|
168
193
|
}
|
|
169
|
-
|
|
194
|
+
out.push(msg);
|
|
195
|
+
return;
|
|
170
196
|
}
|
|
171
|
-
|
|
197
|
+
out.push({ role: message.role, content: message.content ?? "" });
|
|
172
198
|
});
|
|
199
|
+
return out;
|
|
173
200
|
}
|
|
174
|
-
function toAnthropicMessages(messages) {
|
|
201
|
+
function toAnthropicMessages(messages, options = {}) {
|
|
202
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
175
203
|
const out = [];
|
|
176
|
-
|
|
204
|
+
messages.forEach((message, index) => {
|
|
177
205
|
if (message.role === "system" || message.role === "developer") {
|
|
178
|
-
|
|
206
|
+
return;
|
|
179
207
|
}
|
|
208
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
209
|
+
return;
|
|
180
210
|
if (message.role === "tool") {
|
|
181
211
|
out.push({
|
|
182
212
|
role: "user",
|
|
@@ -188,7 +218,7 @@ function toAnthropicMessages(messages) {
|
|
|
188
218
|
}
|
|
189
219
|
]
|
|
190
220
|
});
|
|
191
|
-
|
|
221
|
+
return;
|
|
192
222
|
}
|
|
193
223
|
if (message.role === "assistant") {
|
|
194
224
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
@@ -208,9 +238,22 @@ function toAnthropicMessages(messages) {
|
|
|
208
238
|
} else {
|
|
209
239
|
out.push({ role: "assistant", content: message.content ?? "" });
|
|
210
240
|
}
|
|
211
|
-
|
|
241
|
+
return;
|
|
212
242
|
}
|
|
213
243
|
out.push({ role: "user", content: message.content ?? "" });
|
|
214
|
-
}
|
|
244
|
+
});
|
|
215
245
|
return out;
|
|
216
246
|
}
|
|
247
|
+
function toAnthropicSystem(messages) {
|
|
248
|
+
const parts = [];
|
|
249
|
+
for (const message of messages) {
|
|
250
|
+
if (message.role !== "system" && message.role !== "developer")
|
|
251
|
+
continue;
|
|
252
|
+
if (typeof message.content === "string" && message.content.length > 0) {
|
|
253
|
+
parts.push(message.content);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return parts.length > 0 ? parts.join(`
|
|
257
|
+
|
|
258
|
+
`) : undefined;
|
|
259
|
+
}
|
package/dist/gateway.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type RenderedChatMessage, type ResolvedPrompt, type ResolvedToolDefinition, type UnknownRoleMode } from "./prompts/types";
|
|
2
2
|
export interface GatewayAuthOptions {
|
|
3
3
|
apiKey: string;
|
|
4
4
|
projectId: string;
|
|
@@ -36,23 +36,41 @@ export declare function gatewayAuthHeaders(options: GatewayAuthOptions): Record<
|
|
|
36
36
|
* follows from a resolved prompt:
|
|
37
37
|
* - `x-to11-prompt-id`, `x-to11-prompt-version` — always.
|
|
38
38
|
* - `x-to11-release-id` — when the result carries a non-null `releaseId`
|
|
39
|
-
* (the enriched `
|
|
39
|
+
* (the enriched `render()` result; a legacy `resolve()` result has no
|
|
40
40
|
* `releaseId`, so it is omitted — non-breaking).
|
|
41
41
|
* - `x-to11-variant-name` — IFF `variantName` is present (i.e. the resolved
|
|
42
42
|
* release was weighted); omitted for `single`-strategy releases.
|
|
43
|
-
* - `x-to11-prompt-
|
|
44
|
-
*
|
|
43
|
+
* - `x-to11-prompt-label` — the single label the prompt resolved through (the
|
|
44
|
+
* env slug today); omitted when empty (the gateway rejects empty values).
|
|
45
|
+
* - `x-to11-prompt-slug` — the prompt's human-readable slug, so traces show a
|
|
46
|
+
* readable name alongside the opaque `x-to11-prompt-id`; omitted when empty
|
|
47
|
+
* or absent (a legacy `resolve()` result carries no slug).
|
|
45
48
|
*
|
|
46
|
-
* The parameter type is WIDENED to accept either the enriched `
|
|
49
|
+
* The parameter type is WIDENED to accept either the enriched `render()`
|
|
47
50
|
* result or the legacy `ResolvedPrompt`. The new fields are read when present
|
|
48
51
|
* and omitted otherwise, so an old `resolve()` result still produces exactly
|
|
49
52
|
* the original three headers (non-breaking; D1/D7 shape coexistence).
|
|
50
53
|
*
|
|
51
54
|
* @example
|
|
52
|
-
* const result = await client.prompts.
|
|
55
|
+
* const result = await client.prompts.render(slug, { env: "prod" });
|
|
53
56
|
* const headers = gatewayPromptHeaders(result);
|
|
54
57
|
*/
|
|
55
|
-
export
|
|
58
|
+
export interface GatewayPromptProvenance {
|
|
59
|
+
promptId: string;
|
|
60
|
+
version: number;
|
|
61
|
+
/** The single label the prompt resolved through (env slug today). */
|
|
62
|
+
label: string;
|
|
63
|
+
/**
|
|
64
|
+
* The prompt's human-readable slug (the `render()`/`resolve()` input). Present
|
|
65
|
+
* on an enriched `render()` result; a legacy `ResolvedPrompt` omits it.
|
|
66
|
+
*/
|
|
67
|
+
slug?: string;
|
|
68
|
+
/** Present on an enriched result; a legacy `resolve()` result omits it. */
|
|
69
|
+
releaseId?: string | null;
|
|
70
|
+
/** Present IFF the resolved release was weighted. */
|
|
71
|
+
variantName?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare function gatewayPromptHeaders(prompt: GatewayPromptProvenance): Record<string, string>;
|
|
56
74
|
/** OpenAI Chat Completions `tools[]` entry (`function` tool). */
|
|
57
75
|
export interface OpenAITool {
|
|
58
76
|
type: "function";
|
|
@@ -175,6 +193,16 @@ export declare function toAnthropicTools(tools: ReadonlyArray<ResolvedToolDefini
|
|
|
175
193
|
* await anthropic.messages.create({ model, system, messages, tools, tool_choice });
|
|
176
194
|
*/
|
|
177
195
|
export declare function toAnthropicToolChoice(toolChoice: ResolvedPrompt["toolChoice"]): AnthropicToolChoice | undefined;
|
|
196
|
+
/** Options shared by the message converters. */
|
|
197
|
+
export interface ConvertMessagesOptions {
|
|
198
|
+
/**
|
|
199
|
+
* Policy for a message whose role is outside the known set
|
|
200
|
+
* (`system | user | assistant | developer | tool`). `"drop"` (default) omits
|
|
201
|
+
* it silently; `"warn"` omits it and emits a `console.warn`; `"error"` throws
|
|
202
|
+
* {@link UnknownRoleError}. Both converters apply this identically.
|
|
203
|
+
*/
|
|
204
|
+
onUnknownRole?: UnknownRoleMode;
|
|
205
|
+
}
|
|
178
206
|
/**
|
|
179
207
|
* Shape rendered turns for the OpenAI Chat Completions `messages` field.
|
|
180
208
|
*
|
|
@@ -184,11 +212,15 @@ export declare function toAnthropicToolChoice(toolChoice: ResolvedPrompt["toolCh
|
|
|
184
212
|
* become `tool_calls[]` with JSON-stringified `arguments` (the OpenAI wire
|
|
185
213
|
* shape). A pure tool-call turn keeps `content: null`.
|
|
186
214
|
* - `tool` (result) → `{ role: "tool", tool_call_id, content }`.
|
|
215
|
+
* - an unrecognized role is handled per `options.onUnknownRole` (default
|
|
216
|
+
* `"drop"`) — dropped, dropped-with-warning, or throws `UnknownRoleError`.
|
|
187
217
|
*
|
|
188
218
|
* @example
|
|
189
219
|
* const messages = toOpenAIMessages(prompt.messages);
|
|
220
|
+
* @example Fail loudly on a malformed message array
|
|
221
|
+
* const messages = toOpenAIMessages(history, { onUnknownRole: "error" });
|
|
190
222
|
*/
|
|
191
|
-
export declare function toOpenAIMessages(messages: ReadonlyArray<RenderedChatMessage
|
|
223
|
+
export declare function toOpenAIMessages(messages: ReadonlyArray<RenderedChatMessage>, options?: ConvertMessagesOptions): OpenAIChatMessage[];
|
|
192
224
|
/**
|
|
193
225
|
* Shape rendered turns for the Anthropic Messages `messages` field.
|
|
194
226
|
*
|
|
@@ -201,8 +233,32 @@ export declare function toOpenAIMessages(messages: ReadonlyArray<RenderedChatMes
|
|
|
201
233
|
* ...tool_use] }`; without calls → `{ role: "assistant", content }`.
|
|
202
234
|
* - `tool` (result) → `{ role: "user", content: [{ type: "tool_result",
|
|
203
235
|
* tool_use_id, content }] }`.
|
|
236
|
+
* - an unrecognized role is handled per `options.onUnknownRole` (default
|
|
237
|
+
* `"drop"`) — dropped, dropped-with-warning, or throws `UnknownRoleError` —
|
|
238
|
+
* identically to {@link toOpenAIMessages}. (`system`/`developer` are known
|
|
239
|
+
* roles folded into `system`, not "unknown"; they are never dropped here.)
|
|
204
240
|
*
|
|
205
241
|
* @example
|
|
206
242
|
* const messages = toAnthropicMessages(prompt.messages);
|
|
243
|
+
* @example Fail loudly on a malformed message array
|
|
244
|
+
* const messages = toAnthropicMessages(history, { onUnknownRole: "error" });
|
|
245
|
+
*/
|
|
246
|
+
export declare function toAnthropicMessages(messages: ReadonlyArray<RenderedChatMessage>, options?: ConvertMessagesOptions): AnthropicMessage[];
|
|
247
|
+
/**
|
|
248
|
+
* Fold the `system` and `developer` instruction turns into Anthropic's
|
|
249
|
+
* top-level `system` string. Anthropic has no `developer` role and takes system
|
|
250
|
+
* instructions as a separate request field, so pair this with
|
|
251
|
+
* {@link toAnthropicMessages} (which omits those turns). Returns `undefined`
|
|
252
|
+
* when there is no system/developer content.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* const res = await anthropic.messages.create(
|
|
256
|
+
* {
|
|
257
|
+
* ...prompt.config,
|
|
258
|
+
* system: toAnthropicSystem(prompt.messages),
|
|
259
|
+
* messages: toAnthropicMessages(prompt.messages),
|
|
260
|
+
* },
|
|
261
|
+
* { headers: to11.turn().headers(prompt) },
|
|
262
|
+
* );
|
|
207
263
|
*/
|
|
208
|
-
export declare function
|
|
264
|
+
export declare function toAnthropicSystem(messages: ReadonlyArray<RenderedChatMessage>): string | undefined;
|
package/dist/gateway.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/gateway.ts
|
|
2
|
+
import { UnknownRoleError } from "./errors";
|
|
2
3
|
import {
|
|
3
4
|
ENV_SLUG_REGEX
|
|
4
5
|
} from "./prompts/types";
|
|
@@ -29,16 +30,17 @@ function gatewayPromptHeaders(prompt) {
|
|
|
29
30
|
"x-to11-prompt-id": prompt.promptId,
|
|
30
31
|
"x-to11-prompt-version": String(prompt.version)
|
|
31
32
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
headers["x-to11-release-id"] = releaseId;
|
|
33
|
+
if (typeof prompt.releaseId === "string" && prompt.releaseId.length > 0) {
|
|
34
|
+
headers["x-to11-release-id"] = prompt.releaseId;
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
headers["x-to11-variant-name"] = variantName;
|
|
36
|
+
if (typeof prompt.variantName === "string" && prompt.variantName.length > 0) {
|
|
37
|
+
headers["x-to11-variant-name"] = prompt.variantName;
|
|
39
38
|
}
|
|
40
|
-
if (prompt.
|
|
41
|
-
headers["x-to11-prompt-
|
|
39
|
+
if (prompt.label.length > 0) {
|
|
40
|
+
headers["x-to11-prompt-label"] = prompt.label;
|
|
41
|
+
}
|
|
42
|
+
if (typeof prompt.slug === "string" && prompt.slug.length > 0) {
|
|
43
|
+
headers["x-to11-prompt-slug"] = prompt.slug;
|
|
42
44
|
}
|
|
43
45
|
return headers;
|
|
44
46
|
}
|
|
@@ -94,22 +96,44 @@ function requireToolCallId(id) {
|
|
|
94
96
|
}
|
|
95
97
|
return id;
|
|
96
98
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
var KNOWN_RENDERED_ROLES = new Set([
|
|
100
|
+
"system",
|
|
101
|
+
"user",
|
|
102
|
+
"assistant",
|
|
103
|
+
"developer",
|
|
104
|
+
"tool"
|
|
105
|
+
]);
|
|
106
|
+
function skipUnknownRole(role, index, mode) {
|
|
107
|
+
if (KNOWN_RENDERED_ROLES.has(role))
|
|
108
|
+
return false;
|
|
109
|
+
if (mode === "error")
|
|
110
|
+
throw new UnknownRoleError(role, index);
|
|
111
|
+
if (mode === "warn") {
|
|
112
|
+
console.warn(`[to11ai/sdk] dropped message[${index}] with unknown role ${JSON.stringify(role)} (expected system|user|assistant|developer|tool)`);
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
function toOpenAIMessages(messages, options = {}) {
|
|
117
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
118
|
+
const out = [];
|
|
119
|
+
messages.forEach((message, index) => {
|
|
120
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
121
|
+
return;
|
|
99
122
|
if (message.role === "tool") {
|
|
100
|
-
|
|
123
|
+
out.push({
|
|
101
124
|
role: "tool",
|
|
102
125
|
tool_call_id: requireToolCallId(message.toolCallId),
|
|
103
126
|
content: message.content ?? ""
|
|
104
|
-
};
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
105
129
|
}
|
|
106
130
|
if (message.role === "assistant") {
|
|
107
|
-
const
|
|
131
|
+
const msg = {
|
|
108
132
|
role: "assistant",
|
|
109
133
|
content: message.content ?? null
|
|
110
134
|
};
|
|
111
135
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
112
|
-
|
|
136
|
+
msg.tool_calls = message.toolCalls.map((call) => ({
|
|
113
137
|
id: requireToolCallId(call.id),
|
|
114
138
|
type: "function",
|
|
115
139
|
function: {
|
|
@@ -118,17 +142,22 @@ function toOpenAIMessages(messages) {
|
|
|
118
142
|
}
|
|
119
143
|
}));
|
|
120
144
|
}
|
|
121
|
-
|
|
145
|
+
out.push(msg);
|
|
146
|
+
return;
|
|
122
147
|
}
|
|
123
|
-
|
|
148
|
+
out.push({ role: message.role, content: message.content ?? "" });
|
|
124
149
|
});
|
|
150
|
+
return out;
|
|
125
151
|
}
|
|
126
|
-
function toAnthropicMessages(messages) {
|
|
152
|
+
function toAnthropicMessages(messages, options = {}) {
|
|
153
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
127
154
|
const out = [];
|
|
128
|
-
|
|
155
|
+
messages.forEach((message, index) => {
|
|
129
156
|
if (message.role === "system" || message.role === "developer") {
|
|
130
|
-
|
|
157
|
+
return;
|
|
131
158
|
}
|
|
159
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
160
|
+
return;
|
|
132
161
|
if (message.role === "tool") {
|
|
133
162
|
out.push({
|
|
134
163
|
role: "user",
|
|
@@ -140,7 +169,7 @@ function toAnthropicMessages(messages) {
|
|
|
140
169
|
}
|
|
141
170
|
]
|
|
142
171
|
});
|
|
143
|
-
|
|
172
|
+
return;
|
|
144
173
|
}
|
|
145
174
|
if (message.role === "assistant") {
|
|
146
175
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
@@ -160,18 +189,32 @@ function toAnthropicMessages(messages) {
|
|
|
160
189
|
} else {
|
|
161
190
|
out.push({ role: "assistant", content: message.content ?? "" });
|
|
162
191
|
}
|
|
163
|
-
|
|
192
|
+
return;
|
|
164
193
|
}
|
|
165
194
|
out.push({ role: "user", content: message.content ?? "" });
|
|
166
|
-
}
|
|
195
|
+
});
|
|
167
196
|
return out;
|
|
168
197
|
}
|
|
198
|
+
function toAnthropicSystem(messages) {
|
|
199
|
+
const parts = [];
|
|
200
|
+
for (const message of messages) {
|
|
201
|
+
if (message.role !== "system" && message.role !== "developer")
|
|
202
|
+
continue;
|
|
203
|
+
if (typeof message.content === "string" && message.content.length > 0) {
|
|
204
|
+
parts.push(message.content);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return parts.length > 0 ? parts.join(`
|
|
208
|
+
|
|
209
|
+
`) : undefined;
|
|
210
|
+
}
|
|
169
211
|
export {
|
|
170
212
|
toOpenAITools,
|
|
171
213
|
toOpenAIToolChoice,
|
|
172
214
|
toOpenAIMessages,
|
|
173
215
|
toAnthropicTools,
|
|
174
216
|
toAnthropicToolChoice,
|
|
217
|
+
toAnthropicSystem,
|
|
175
218
|
toAnthropicMessages,
|
|
176
219
|
gatewayPromptHeaders,
|
|
177
220
|
gatewayAuthHeaders
|