@modelrelay/sdk 1.10.3 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -78
- package/dist/index.cjs +5476 -6582
- package/dist/index.d.cts +3527 -355
- package/dist/index.d.ts +3527 -355
- package/dist/index.js +5437 -6584
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -128,89 +128,84 @@ const text = await mr.responses.textForCustomer({
|
|
|
128
128
|
});
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
##
|
|
131
|
+
## Workflows
|
|
132
|
+
|
|
133
|
+
High-level helpers for common workflow patterns:
|
|
134
|
+
|
|
135
|
+
### Chain (Sequential)
|
|
136
|
+
|
|
137
|
+
Sequential LLM calls where each step's output feeds the next step's input:
|
|
132
138
|
|
|
133
139
|
```ts
|
|
134
|
-
import {
|
|
135
|
-
ModelRelay,
|
|
136
|
-
type LLMResponsesBindingV0,
|
|
137
|
-
parseNodeId,
|
|
138
|
-
parseOutputName,
|
|
139
|
-
parseSecretKey,
|
|
140
|
-
workflowV0,
|
|
141
|
-
} from "@modelrelay/sdk";
|
|
140
|
+
import { chain, llmStep } from "@modelrelay/sdk";
|
|
142
141
|
|
|
143
|
-
const
|
|
142
|
+
const summarizeReq = mr.responses
|
|
143
|
+
.new()
|
|
144
|
+
.model("claude-sonnet-4-20250514")
|
|
145
|
+
.system("Summarize the input concisely.")
|
|
146
|
+
.user("The quick brown fox...")
|
|
147
|
+
.build();
|
|
144
148
|
|
|
145
|
-
const
|
|
146
|
-
.
|
|
147
|
-
.
|
|
148
|
-
.
|
|
149
|
-
|
|
150
|
-
input: [
|
|
151
|
-
{ type: "message", role: "system", content: [{ type: "text", text: "You are Agent A." }] },
|
|
152
|
-
{ type: "message", role: "user", content: [{ type: "text", text: "Write 3 ideas for a landing page." }] },
|
|
153
|
-
],
|
|
154
|
-
})
|
|
155
|
-
.llmResponses(parseNodeId("agent_b"), {
|
|
156
|
-
model: "claude-sonnet-4-20250514",
|
|
157
|
-
input: [
|
|
158
|
-
{ type: "message", role: "system", content: [{ type: "text", text: "You are Agent B." }] },
|
|
159
|
-
{ type: "message", role: "user", content: [{ type: "text", text: "Write 3 objections a user might have." }] },
|
|
160
|
-
],
|
|
161
|
-
})
|
|
162
|
-
.llmResponses(parseNodeId("agent_c"), {
|
|
163
|
-
model: "claude-sonnet-4-20250514",
|
|
164
|
-
input: [
|
|
165
|
-
{ type: "message", role: "system", content: [{ type: "text", text: "You are Agent C." }] },
|
|
166
|
-
{ type: "message", role: "user", content: [{ type: "text", text: "Write 3 alternative headlines." }] },
|
|
167
|
-
],
|
|
168
|
-
})
|
|
169
|
-
.joinAll(parseNodeId("join"))
|
|
170
|
-
.llmResponses(
|
|
171
|
-
parseNodeId("aggregate"),
|
|
172
|
-
{
|
|
173
|
-
model: "claude-sonnet-4-20250514",
|
|
174
|
-
input: [
|
|
175
|
-
{
|
|
176
|
-
type: "message",
|
|
177
|
-
role: "system",
|
|
178
|
-
content: [{ type: "text", text: "Synthesize the best answer from the following agent outputs (JSON)." }],
|
|
179
|
-
},
|
|
180
|
-
{ type: "message", role: "user", content: [{ type: "text", text: "" }] }, // overwritten by bindings
|
|
181
|
-
],
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
// Bind the join output into the aggregator prompt (fan-in).
|
|
185
|
-
bindings: [
|
|
186
|
-
{
|
|
187
|
-
from: parseNodeId("join"),
|
|
188
|
-
to: "/input/1/content/0/text",
|
|
189
|
-
encoding: "json_string",
|
|
190
|
-
} satisfies LLMResponsesBindingV0,
|
|
191
|
-
],
|
|
192
|
-
},
|
|
193
|
-
)
|
|
194
|
-
.edge(parseNodeId("agent_a"), parseNodeId("join"))
|
|
195
|
-
.edge(parseNodeId("agent_b"), parseNodeId("join"))
|
|
196
|
-
.edge(parseNodeId("agent_c"), parseNodeId("join"))
|
|
197
|
-
.edge(parseNodeId("join"), parseNodeId("aggregate"))
|
|
198
|
-
.output(parseOutputName("result"), parseNodeId("aggregate"))
|
|
149
|
+
const translateReq = mr.responses
|
|
150
|
+
.new()
|
|
151
|
+
.model("claude-sonnet-4-20250514")
|
|
152
|
+
.system("Translate the input to French.")
|
|
153
|
+
.user("") // Bound from previous step
|
|
199
154
|
.build();
|
|
200
155
|
|
|
201
|
-
const
|
|
156
|
+
const spec = chain("summarize-translate")
|
|
157
|
+
.step(llmStep("summarize", summarizeReq))
|
|
158
|
+
.step(llmStep("translate", translateReq).withStream())
|
|
159
|
+
.outputLast("result")
|
|
160
|
+
.build();
|
|
161
|
+
```
|
|
202
162
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
163
|
+
### Parallel (Fan-out with Aggregation)
|
|
164
|
+
|
|
165
|
+
Concurrent LLM calls with optional aggregation:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { parallel, llmStep } from "@modelrelay/sdk";
|
|
169
|
+
|
|
170
|
+
const gpt4Req = mr.responses.new().model("gpt-4.1").user("Analyze this...").build();
|
|
171
|
+
const claudeReq = mr.responses.new().model("claude-sonnet-4-20250514").user("Analyze this...").build();
|
|
172
|
+
const synthesizeReq = mr.responses
|
|
173
|
+
.new()
|
|
174
|
+
.model("claude-sonnet-4-20250514")
|
|
175
|
+
.system("Synthesize the analyses into a unified view.")
|
|
176
|
+
.user("") // Bound from join output
|
|
177
|
+
.build();
|
|
178
|
+
|
|
179
|
+
const spec = parallel("multi-model-compare")
|
|
180
|
+
.step(llmStep("gpt4", gpt4Req))
|
|
181
|
+
.step(llmStep("claude", claudeReq))
|
|
182
|
+
.aggregate("synthesize", synthesizeReq)
|
|
183
|
+
.output("result", "synthesize")
|
|
184
|
+
.build();
|
|
211
185
|
```
|
|
212
186
|
|
|
213
|
-
|
|
187
|
+
### MapReduce (Parallel Map with Reduce)
|
|
188
|
+
|
|
189
|
+
Process items in parallel, then combine results:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { mapReduce } from "@modelrelay/sdk";
|
|
193
|
+
|
|
194
|
+
const combineReq = mr.responses
|
|
195
|
+
.new()
|
|
196
|
+
.model("claude-sonnet-4-20250514")
|
|
197
|
+
.system("Combine summaries into a cohesive overview.")
|
|
198
|
+
.user("") // Bound from join output
|
|
199
|
+
.build();
|
|
200
|
+
|
|
201
|
+
const spec = mapReduce("summarize-docs")
|
|
202
|
+
.item("doc1", doc1Req)
|
|
203
|
+
.item("doc2", doc2Req)
|
|
204
|
+
.item("doc3", doc3Req)
|
|
205
|
+
.reduce("combine", combineReq)
|
|
206
|
+
.output("result", "combine")
|
|
207
|
+
.build();
|
|
208
|
+
```
|
|
214
209
|
|
|
215
210
|
## Chat-Like Text Helpers
|
|
216
211
|
|
|
@@ -250,24 +245,60 @@ for await (const delta of deltas) {
|
|
|
250
245
|
|
|
251
246
|
## Structured Outputs with Zod
|
|
252
247
|
|
|
248
|
+
The simplest way to get typed structured output:
|
|
249
|
+
|
|
253
250
|
```ts
|
|
254
|
-
import { ModelRelay
|
|
251
|
+
import { ModelRelay } from "@modelrelay/sdk";
|
|
255
252
|
import { z } from "zod";
|
|
256
253
|
|
|
257
|
-
const mr =
|
|
254
|
+
const mr = ModelRelay.fromSecretKey("mr_sk_...");
|
|
258
255
|
|
|
259
256
|
const Person = z.object({
|
|
260
257
|
name: z.string(),
|
|
261
258
|
age: z.number(),
|
|
262
259
|
});
|
|
263
260
|
|
|
261
|
+
// Simple one-call API (recommended)
|
|
262
|
+
const person = await mr.responses.object<z.infer<typeof Person>>({
|
|
263
|
+
model: "claude-sonnet-4-20250514",
|
|
264
|
+
schema: Person,
|
|
265
|
+
prompt: "Extract: John Doe is 30 years old",
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
console.log(person.name); // "John Doe"
|
|
269
|
+
console.log(person.age); // 30
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
For parallel structured output calls:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const [security, performance] = await Promise.all([
|
|
276
|
+
mr.responses.object<SecurityReview>({
|
|
277
|
+
model: "claude-sonnet-4-20250514",
|
|
278
|
+
schema: SecuritySchema,
|
|
279
|
+
system: "You are a security expert.",
|
|
280
|
+
prompt: code,
|
|
281
|
+
}),
|
|
282
|
+
mr.responses.object<PerformanceReview>({
|
|
283
|
+
model: "claude-sonnet-4-20250514",
|
|
284
|
+
schema: PerformanceSchema,
|
|
285
|
+
system: "You are a performance expert.",
|
|
286
|
+
prompt: code,
|
|
287
|
+
}),
|
|
288
|
+
]);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
For more control (retries, custom handlers, metadata):
|
|
292
|
+
|
|
293
|
+
```ts
|
|
264
294
|
const result = await mr.responses.structured(
|
|
265
295
|
Person,
|
|
266
296
|
mr.responses.new().model("claude-sonnet-4-20250514").user("Extract: John Doe is 30").build(),
|
|
267
297
|
{ maxRetries: 2 },
|
|
268
298
|
);
|
|
269
299
|
|
|
270
|
-
console.log(result.value);
|
|
300
|
+
console.log(result.value); // { name: "John Doe", age: 30 }
|
|
301
|
+
console.log(result.attempts); // 1
|
|
271
302
|
```
|
|
272
303
|
|
|
273
304
|
## Streaming Structured Outputs
|
|
@@ -374,7 +405,7 @@ try {
|
|
|
374
405
|
} else if (error instanceof TransportError) {
|
|
375
406
|
console.log("Network error:", error.message);
|
|
376
407
|
} else if (error instanceof StreamTimeoutError) {
|
|
377
|
-
console.log("Stream timeout:", error.
|
|
408
|
+
console.log("Stream timeout:", error.kind); // "ttft" | "idle" | "total"
|
|
378
409
|
}
|
|
379
410
|
}
|
|
380
411
|
```
|