@to11ai/sdk 1.0.0 → 2.0.0-rc.1
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 +85 -18
- package/dist/client.d.ts +140 -39
- package/dist/client.js +91 -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 +62 -22
- package/dist/gateway.d.ts +57 -9
- package/dist/gateway.js +62 -22
- package/dist/generated/index.d.ts +1 -1
- package/dist/generated/types.gen.d.ts +1313 -166
- package/dist/generated/zod.gen.d.ts +406 -232
- package/dist/prompts/cache.d.ts +3 -3
- package/dist/prompts/resource.cjs +21 -16
- package/dist/prompts/resource.d.ts +59 -28
- package/dist/prompts/resource.js +21 -16
- package/dist/prompts/shape.d.ts +54 -0
- package/dist/prompts/types.d.ts +31 -13
- package/package.json +1 -1
- package/src/client.ts +326 -80
- package/src/config.ts +13 -0
- package/src/context.ts +198 -0
- package/src/errors.ts +33 -3
- package/src/gateway.ts +135 -33
- package/src/generated/index.ts +1 -1
- package/src/generated/types.gen.ts +1324 -166
- package/src/generated/zod.gen.ts +371 -268
- package/src/prompts/cache.ts +3 -6
- package/src/prompts/resource.ts +89 -41
- package/src/prompts/shape.ts +149 -0
- package/src/prompts/types.ts +35 -14
|
@@ -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 `FetchedPrompt`) 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,14 @@ 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
90
|
}
|
|
91
91
|
return headers;
|
|
92
92
|
}
|
|
@@ -142,22 +142,44 @@ function requireToolCallId(id) {
|
|
|
142
142
|
}
|
|
143
143
|
return id;
|
|
144
144
|
}
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
var KNOWN_RENDERED_ROLES = new Set([
|
|
146
|
+
"system",
|
|
147
|
+
"user",
|
|
148
|
+
"assistant",
|
|
149
|
+
"developer",
|
|
150
|
+
"tool"
|
|
151
|
+
]);
|
|
152
|
+
function skipUnknownRole(role, index, mode) {
|
|
153
|
+
if (KNOWN_RENDERED_ROLES.has(role))
|
|
154
|
+
return false;
|
|
155
|
+
if (mode === "error")
|
|
156
|
+
throw new import_errors.UnknownRoleError(role, index);
|
|
157
|
+
if (mode === "warn") {
|
|
158
|
+
console.warn(`[to11ai/sdk] dropped message[${index}] with unknown role ${JSON.stringify(role)} (expected system|user|assistant|developer|tool)`);
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
function toOpenAIMessages(messages, options = {}) {
|
|
163
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
164
|
+
const out = [];
|
|
165
|
+
messages.forEach((message, index) => {
|
|
166
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
167
|
+
return;
|
|
147
168
|
if (message.role === "tool") {
|
|
148
|
-
|
|
169
|
+
out.push({
|
|
149
170
|
role: "tool",
|
|
150
171
|
tool_call_id: requireToolCallId(message.toolCallId),
|
|
151
172
|
content: message.content ?? ""
|
|
152
|
-
};
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
153
175
|
}
|
|
154
176
|
if (message.role === "assistant") {
|
|
155
|
-
const
|
|
177
|
+
const msg = {
|
|
156
178
|
role: "assistant",
|
|
157
179
|
content: message.content ?? null
|
|
158
180
|
};
|
|
159
181
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
160
|
-
|
|
182
|
+
msg.tool_calls = message.toolCalls.map((call) => ({
|
|
161
183
|
id: requireToolCallId(call.id),
|
|
162
184
|
type: "function",
|
|
163
185
|
function: {
|
|
@@ -166,17 +188,22 @@ function toOpenAIMessages(messages) {
|
|
|
166
188
|
}
|
|
167
189
|
}));
|
|
168
190
|
}
|
|
169
|
-
|
|
191
|
+
out.push(msg);
|
|
192
|
+
return;
|
|
170
193
|
}
|
|
171
|
-
|
|
194
|
+
out.push({ role: message.role, content: message.content ?? "" });
|
|
172
195
|
});
|
|
196
|
+
return out;
|
|
173
197
|
}
|
|
174
|
-
function toAnthropicMessages(messages) {
|
|
198
|
+
function toAnthropicMessages(messages, options = {}) {
|
|
199
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
175
200
|
const out = [];
|
|
176
|
-
|
|
201
|
+
messages.forEach((message, index) => {
|
|
177
202
|
if (message.role === "system" || message.role === "developer") {
|
|
178
|
-
|
|
203
|
+
return;
|
|
179
204
|
}
|
|
205
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
206
|
+
return;
|
|
180
207
|
if (message.role === "tool") {
|
|
181
208
|
out.push({
|
|
182
209
|
role: "user",
|
|
@@ -188,7 +215,7 @@ function toAnthropicMessages(messages) {
|
|
|
188
215
|
}
|
|
189
216
|
]
|
|
190
217
|
});
|
|
191
|
-
|
|
218
|
+
return;
|
|
192
219
|
}
|
|
193
220
|
if (message.role === "assistant") {
|
|
194
221
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
@@ -208,9 +235,22 @@ function toAnthropicMessages(messages) {
|
|
|
208
235
|
} else {
|
|
209
236
|
out.push({ role: "assistant", content: message.content ?? "" });
|
|
210
237
|
}
|
|
211
|
-
|
|
238
|
+
return;
|
|
212
239
|
}
|
|
213
240
|
out.push({ role: "user", content: message.content ?? "" });
|
|
214
|
-
}
|
|
241
|
+
});
|
|
215
242
|
return out;
|
|
216
243
|
}
|
|
244
|
+
function toAnthropicSystem(messages) {
|
|
245
|
+
const parts = [];
|
|
246
|
+
for (const message of messages) {
|
|
247
|
+
if (message.role !== "system" && message.role !== "developer")
|
|
248
|
+
continue;
|
|
249
|
+
if (typeof message.content === "string" && message.content.length > 0) {
|
|
250
|
+
parts.push(message.content);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return parts.length > 0 ? parts.join(`
|
|
254
|
+
|
|
255
|
+
`) : undefined;
|
|
256
|
+
}
|
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,33 @@ 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
45
|
*
|
|
46
|
-
* The parameter type is WIDENED to accept either the enriched `
|
|
46
|
+
* The parameter type is WIDENED to accept either the enriched `render()`
|
|
47
47
|
* result or the legacy `ResolvedPrompt`. The new fields are read when present
|
|
48
48
|
* and omitted otherwise, so an old `resolve()` result still produces exactly
|
|
49
49
|
* the original three headers (non-breaking; D1/D7 shape coexistence).
|
|
50
50
|
*
|
|
51
51
|
* @example
|
|
52
|
-
* const result = await client.prompts.
|
|
52
|
+
* const result = await client.prompts.render(slug, { env: "prod" });
|
|
53
53
|
* const headers = gatewayPromptHeaders(result);
|
|
54
54
|
*/
|
|
55
|
-
export
|
|
55
|
+
export interface GatewayPromptProvenance {
|
|
56
|
+
promptId: string;
|
|
57
|
+
version: number;
|
|
58
|
+
/** The single label the prompt resolved through (env slug today). */
|
|
59
|
+
label: string;
|
|
60
|
+
/** Present on an enriched result; a legacy `resolve()` result omits it. */
|
|
61
|
+
releaseId?: string | null;
|
|
62
|
+
/** Present IFF the resolved release was weighted. */
|
|
63
|
+
variantName?: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function gatewayPromptHeaders(prompt: GatewayPromptProvenance): Record<string, string>;
|
|
56
66
|
/** OpenAI Chat Completions `tools[]` entry (`function` tool). */
|
|
57
67
|
export interface OpenAITool {
|
|
58
68
|
type: "function";
|
|
@@ -175,6 +185,16 @@ export declare function toAnthropicTools(tools: ReadonlyArray<ResolvedToolDefini
|
|
|
175
185
|
* await anthropic.messages.create({ model, system, messages, tools, tool_choice });
|
|
176
186
|
*/
|
|
177
187
|
export declare function toAnthropicToolChoice(toolChoice: ResolvedPrompt["toolChoice"]): AnthropicToolChoice | undefined;
|
|
188
|
+
/** Options shared by the message converters. */
|
|
189
|
+
export interface ConvertMessagesOptions {
|
|
190
|
+
/**
|
|
191
|
+
* Policy for a message whose role is outside the known set
|
|
192
|
+
* (`system | user | assistant | developer | tool`). `"drop"` (default) omits
|
|
193
|
+
* it silently; `"warn"` omits it and emits a `console.warn`; `"error"` throws
|
|
194
|
+
* {@link UnknownRoleError}. Both converters apply this identically.
|
|
195
|
+
*/
|
|
196
|
+
onUnknownRole?: UnknownRoleMode;
|
|
197
|
+
}
|
|
178
198
|
/**
|
|
179
199
|
* Shape rendered turns for the OpenAI Chat Completions `messages` field.
|
|
180
200
|
*
|
|
@@ -184,11 +204,15 @@ export declare function toAnthropicToolChoice(toolChoice: ResolvedPrompt["toolCh
|
|
|
184
204
|
* become `tool_calls[]` with JSON-stringified `arguments` (the OpenAI wire
|
|
185
205
|
* shape). A pure tool-call turn keeps `content: null`.
|
|
186
206
|
* - `tool` (result) → `{ role: "tool", tool_call_id, content }`.
|
|
207
|
+
* - an unrecognized role is handled per `options.onUnknownRole` (default
|
|
208
|
+
* `"drop"`) — dropped, dropped-with-warning, or throws `UnknownRoleError`.
|
|
187
209
|
*
|
|
188
210
|
* @example
|
|
189
211
|
* const messages = toOpenAIMessages(prompt.messages);
|
|
212
|
+
* @example Fail loudly on a malformed message array
|
|
213
|
+
* const messages = toOpenAIMessages(history, { onUnknownRole: "error" });
|
|
190
214
|
*/
|
|
191
|
-
export declare function toOpenAIMessages(messages: ReadonlyArray<RenderedChatMessage
|
|
215
|
+
export declare function toOpenAIMessages(messages: ReadonlyArray<RenderedChatMessage>, options?: ConvertMessagesOptions): OpenAIChatMessage[];
|
|
192
216
|
/**
|
|
193
217
|
* Shape rendered turns for the Anthropic Messages `messages` field.
|
|
194
218
|
*
|
|
@@ -201,8 +225,32 @@ export declare function toOpenAIMessages(messages: ReadonlyArray<RenderedChatMes
|
|
|
201
225
|
* ...tool_use] }`; without calls → `{ role: "assistant", content }`.
|
|
202
226
|
* - `tool` (result) → `{ role: "user", content: [{ type: "tool_result",
|
|
203
227
|
* tool_use_id, content }] }`.
|
|
228
|
+
* - an unrecognized role is handled per `options.onUnknownRole` (default
|
|
229
|
+
* `"drop"`) — dropped, dropped-with-warning, or throws `UnknownRoleError` —
|
|
230
|
+
* identically to {@link toOpenAIMessages}. (`system`/`developer` are known
|
|
231
|
+
* roles folded into `system`, not "unknown"; they are never dropped here.)
|
|
204
232
|
*
|
|
205
233
|
* @example
|
|
206
234
|
* const messages = toAnthropicMessages(prompt.messages);
|
|
235
|
+
* @example Fail loudly on a malformed message array
|
|
236
|
+
* const messages = toAnthropicMessages(history, { onUnknownRole: "error" });
|
|
237
|
+
*/
|
|
238
|
+
export declare function toAnthropicMessages(messages: ReadonlyArray<RenderedChatMessage>, options?: ConvertMessagesOptions): AnthropicMessage[];
|
|
239
|
+
/**
|
|
240
|
+
* Fold the `system` and `developer` instruction turns into Anthropic's
|
|
241
|
+
* top-level `system` string. Anthropic has no `developer` role and takes system
|
|
242
|
+
* instructions as a separate request field, so pair this with
|
|
243
|
+
* {@link toAnthropicMessages} (which omits those turns). Returns `undefined`
|
|
244
|
+
* when there is no system/developer content.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* const res = await anthropic.messages.create(
|
|
248
|
+
* {
|
|
249
|
+
* ...prompt.config,
|
|
250
|
+
* system: toAnthropicSystem(prompt.messages),
|
|
251
|
+
* messages: toAnthropicMessages(prompt.messages),
|
|
252
|
+
* },
|
|
253
|
+
* { headers: to11.turn().headers(prompt) },
|
|
254
|
+
* );
|
|
207
255
|
*/
|
|
208
|
-
export declare function
|
|
256
|
+
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,14 @@ 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;
|
|
42
41
|
}
|
|
43
42
|
return headers;
|
|
44
43
|
}
|
|
@@ -94,22 +93,44 @@ function requireToolCallId(id) {
|
|
|
94
93
|
}
|
|
95
94
|
return id;
|
|
96
95
|
}
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
var KNOWN_RENDERED_ROLES = new Set([
|
|
97
|
+
"system",
|
|
98
|
+
"user",
|
|
99
|
+
"assistant",
|
|
100
|
+
"developer",
|
|
101
|
+
"tool"
|
|
102
|
+
]);
|
|
103
|
+
function skipUnknownRole(role, index, mode) {
|
|
104
|
+
if (KNOWN_RENDERED_ROLES.has(role))
|
|
105
|
+
return false;
|
|
106
|
+
if (mode === "error")
|
|
107
|
+
throw new UnknownRoleError(role, index);
|
|
108
|
+
if (mode === "warn") {
|
|
109
|
+
console.warn(`[to11ai/sdk] dropped message[${index}] with unknown role ${JSON.stringify(role)} (expected system|user|assistant|developer|tool)`);
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
function toOpenAIMessages(messages, options = {}) {
|
|
114
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
115
|
+
const out = [];
|
|
116
|
+
messages.forEach((message, index) => {
|
|
117
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
118
|
+
return;
|
|
99
119
|
if (message.role === "tool") {
|
|
100
|
-
|
|
120
|
+
out.push({
|
|
101
121
|
role: "tool",
|
|
102
122
|
tool_call_id: requireToolCallId(message.toolCallId),
|
|
103
123
|
content: message.content ?? ""
|
|
104
|
-
};
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
105
126
|
}
|
|
106
127
|
if (message.role === "assistant") {
|
|
107
|
-
const
|
|
128
|
+
const msg = {
|
|
108
129
|
role: "assistant",
|
|
109
130
|
content: message.content ?? null
|
|
110
131
|
};
|
|
111
132
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
112
|
-
|
|
133
|
+
msg.tool_calls = message.toolCalls.map((call) => ({
|
|
113
134
|
id: requireToolCallId(call.id),
|
|
114
135
|
type: "function",
|
|
115
136
|
function: {
|
|
@@ -118,17 +139,22 @@ function toOpenAIMessages(messages) {
|
|
|
118
139
|
}
|
|
119
140
|
}));
|
|
120
141
|
}
|
|
121
|
-
|
|
142
|
+
out.push(msg);
|
|
143
|
+
return;
|
|
122
144
|
}
|
|
123
|
-
|
|
145
|
+
out.push({ role: message.role, content: message.content ?? "" });
|
|
124
146
|
});
|
|
147
|
+
return out;
|
|
125
148
|
}
|
|
126
|
-
function toAnthropicMessages(messages) {
|
|
149
|
+
function toAnthropicMessages(messages, options = {}) {
|
|
150
|
+
const mode = options.onUnknownRole ?? "drop";
|
|
127
151
|
const out = [];
|
|
128
|
-
|
|
152
|
+
messages.forEach((message, index) => {
|
|
129
153
|
if (message.role === "system" || message.role === "developer") {
|
|
130
|
-
|
|
154
|
+
return;
|
|
131
155
|
}
|
|
156
|
+
if (skipUnknownRole(message.role, index, mode))
|
|
157
|
+
return;
|
|
132
158
|
if (message.role === "tool") {
|
|
133
159
|
out.push({
|
|
134
160
|
role: "user",
|
|
@@ -140,7 +166,7 @@ function toAnthropicMessages(messages) {
|
|
|
140
166
|
}
|
|
141
167
|
]
|
|
142
168
|
});
|
|
143
|
-
|
|
169
|
+
return;
|
|
144
170
|
}
|
|
145
171
|
if (message.role === "assistant") {
|
|
146
172
|
if (message.toolCalls != null && message.toolCalls.length > 0) {
|
|
@@ -160,18 +186,32 @@ function toAnthropicMessages(messages) {
|
|
|
160
186
|
} else {
|
|
161
187
|
out.push({ role: "assistant", content: message.content ?? "" });
|
|
162
188
|
}
|
|
163
|
-
|
|
189
|
+
return;
|
|
164
190
|
}
|
|
165
191
|
out.push({ role: "user", content: message.content ?? "" });
|
|
166
|
-
}
|
|
192
|
+
});
|
|
167
193
|
return out;
|
|
168
194
|
}
|
|
195
|
+
function toAnthropicSystem(messages) {
|
|
196
|
+
const parts = [];
|
|
197
|
+
for (const message of messages) {
|
|
198
|
+
if (message.role !== "system" && message.role !== "developer")
|
|
199
|
+
continue;
|
|
200
|
+
if (typeof message.content === "string" && message.content.length > 0) {
|
|
201
|
+
parts.push(message.content);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return parts.length > 0 ? parts.join(`
|
|
205
|
+
|
|
206
|
+
`) : undefined;
|
|
207
|
+
}
|
|
169
208
|
export {
|
|
170
209
|
toOpenAITools,
|
|
171
210
|
toOpenAIToolChoice,
|
|
172
211
|
toOpenAIMessages,
|
|
173
212
|
toAnthropicTools,
|
|
174
213
|
toAnthropicToolChoice,
|
|
214
|
+
toAnthropicSystem,
|
|
175
215
|
toAnthropicMessages,
|
|
176
216
|
gatewayPromptHeaders,
|
|
177
217
|
gatewayAuthHeaders
|