dyadic 1.2.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,22 +1,32 @@
1
- # Changelog
2
-
3
- ## 1.2.0 (2026-10-07)
4
-
5
- Security release.
6
-
7
- - The API key is only ever sent to the configured API origin. `request()` with an
8
- absolute URL on another host, port or scheme, with credentials in it, or
9
- protocol-relative, now raises instead of sending the key; redirects are not
10
- followed. Plain `http://` base URLs are accepted only for local or
11
- private-network APIs.
12
- - Retries no longer repeat work that may already have run. GET, HEAD, DELETE and
13
- OPTIONS retry on busy or failing servers; other requests retry only when they
14
- never started (no connection, or Dyadic's `rate_limit_exceeded` /
15
- `concurrency_limit` refusal). `409` is no longer retried.
16
- - Every attempt of one call sends the same `Idempotency-Key` (choose it with
17
- `{ idempotencyKey }`, or mark a call safe to replay with `{ idempotent: true }`).
18
- - The package documents server-side use only (a key must never ship to a browser), builds before `npm test` and `npm pack`, and has a lockfile.
19
-
20
- ## 1.1.0
21
-
22
- - Runs with your own agents and functions, and connected apps (Workspace saves).
1
+ # Changelog
2
+
3
+ ## 1.3.1 (2026-10-10)
4
+
5
+ - The version the SDK reports (`__version__` / `VERSION`, and the User-Agent header) is the package's
6
+ own: 1.3.0 still said 1.2.0.
7
+
8
+ ## 1.3.0 (2026-10-10)
9
+
10
+ - `decisions.create(input, questions, model?)`: typed answers (a predicate's probability, a
11
+ choice or a score) from `POST /v1/decisions`.
12
+
13
+ ## 1.2.0 (2026-10-07)
14
+
15
+ Security release.
16
+
17
+ - The API key is only ever sent to the configured API origin. `request()` with an
18
+ absolute URL on another host, port or scheme, with credentials in it, or
19
+ protocol-relative, now raises instead of sending the key; redirects are not
20
+ followed. Plain `http://` base URLs are accepted only for local or
21
+ private-network APIs.
22
+ - Retries no longer repeat work that may already have run. GET, HEAD, DELETE and
23
+ OPTIONS retry on busy or failing servers; other requests retry only when they
24
+ never started (no connection, or Dyadic's `rate_limit_exceeded` /
25
+ `concurrency_limit` refusal). `409` is no longer retried.
26
+ - Every attempt of one call sends the same `Idempotency-Key` (choose it with
27
+ `{ idempotencyKey }`, or mark a call safe to replay with `{ idempotent: true }`).
28
+ - The package documents server-side use only (a key must never ship to a browser), builds before `npm test` and `npm pack`, and has a lockfile.
29
+
30
+ ## 1.1.0
31
+
32
+ - Runs with your own agents and functions, and connected apps (Workspace saves).
package/README.md CHANGED
@@ -1,94 +1,114 @@
1
- # dyadic
2
-
3
- ```sh
4
- npm install dyadic
5
- ```
6
-
7
- The official TypeScript and JavaScript SDK for the [Dyadic API](https://dyadic.app/developers/): Iris with streaming, function calling, images and typed errors. No dependencies; for server-side code on Node 18+, Deno and Bun (keep your key off the browser, see below).
8
-
9
- ```ts
10
- import { Dyadic } from "dyadic";
11
-
12
- const client = new Dyadic({ apiKey: process.env.DYADIC_API_KEY });
13
- console.log(await client.ask("Hello!", { model: "iris" }));
14
- ```
15
-
16
- ## Keep your key on the server
17
-
18
- **A Dyadic API key is a long-lived secret that spends your credits. Never ship it
19
- to a browser**: not in a bundle, page source, local storage or a request your
20
- front end makes, where any visitor or extension can copy it. Use the SDK in
21
- server-side code (Node 18+, Deno, Bun, edge functions) and have your front end
22
- call your own server:
23
-
24
- ```ts
25
- // server.ts: the key stays here
26
- import { Dyadic } from "dyadic";
27
- const client = new Dyadic({ apiKey: process.env.DYADIC_API_KEY });
28
-
29
- export async function POST(request: Request) {
30
- const { question } = await request.json();
31
- // Check your own user's session and limits here before spending credits.
32
- return Response.json({ answer: await client.ask(String(question).slice(0, 4000), { model: "iris" }) });
33
- }
34
- ```
35
-
36
- `request()` only ever sends the key to the API you configured: a relative path
37
- joins `baseURL`, and an absolute URL on any other host, port or scheme (or with
38
- credentials in it) throws `DyadicError` instead of being sent. Redirects are not
39
- followed. Plain `http://` is accepted only for a local or private-network API.
40
-
41
- ## Retries
42
-
43
- GET, HEAD, DELETE and OPTIONS are retried on busy or failing servers. Anything
44
- else (a completion, an upload, a run) may already have run, and been billed,
45
- when it fails, so it is retried only if it provably never started: the
46
- connection was never made, or Dyadic refused it with `rate_limit_exceeded` or
47
- `concurrency_limit`. Every attempt of one call carries the same
48
- `Idempotency-Key`; pass `{ idempotencyKey }` to choose it, or `{ idempotent: true }`
49
- for a call that is safe to send twice.
50
-
51
- ## Runs: your own agent, Dyadic's tools, your functions
52
-
53
- ```ts
54
- const run = await client.runs.run({
55
- agent: { id: "helpdesk", name: "Helpdesk", instructions: "You answer customers of a small shop." },
56
- messages: [{ role: "user", content: "Where is order 1042?" }],
57
- builtin_tools: ["data.web_search"],
58
- functions: {
59
- lookup_order: {
60
- description: "Look up an order by id.",
61
- parameters: { type: "object", properties: { order_id: { type: "string" } }, required: ["order_id"] },
62
- handler: async ({ order_id }) => ({ order_id, status: "shipped" }),
63
- },
64
- },
65
- end_user: "customer-17",
66
- });
67
- console.log(run.output_text);
68
- ```
69
-
70
- `client.runs.create({ ..., stream: true })` streams the run's events.
71
-
72
- ## Connected apps: saving into a person's Workspace
73
-
74
- With the person's approval, an application can save into their Dyadic
75
- Workspace, naming them by the Discord account linked to their Dyadic account.
76
-
77
- ```ts
78
- const status = await client.connections.discord(discordId);
79
- if (!status.authorized) console.log("Allow this app:", status.authorize_url); // show the person this link
80
-
81
- try {
82
- const saved = await client.workspace.saves.create({ discord_id: discordId, kind: "message", title: "From #general",
83
- text: "Worth keeping", url: "https://discord.com/channels/1/2/3" });
84
- console.log(saved.url);
85
- } catch (error) {
86
- if (error instanceof DyadicError && error.code === "authorization_required") console.log("Allow this app:", error.body.authorize_url);
87
- }
88
- ```
89
-
90
- `kind` is `message`, `conversation` (with `messages`, saved as a chat the
91
- person can continue unless `as_chat: false`), `image` or `file` (with `files`:
92
- base64, up to 10 of 8 MB).
93
-
94
- Full guide: https://dyadic.app/developers/
1
+ # dyadic
2
+
3
+ ```sh
4
+ npm install dyadic
5
+ ```
6
+
7
+ The official TypeScript and JavaScript SDK for the [Dyadic API](https://dyadic.app/developers/): Iris with streaming, function calling, images and typed errors. No dependencies; for server-side code on Node 18+, Deno and Bun (keep your key off the browser, see below).
8
+
9
+ ```ts
10
+ import { Dyadic } from "dyadic";
11
+
12
+ const client = new Dyadic({ apiKey: process.env.DYADIC_API_KEY });
13
+ console.log(await client.ask("Hello!", { model: "iris" }));
14
+ ```
15
+
16
+ ## Keep your key on the server
17
+
18
+ **A Dyadic API key is a long-lived secret that spends your credits. Never ship it
19
+ to a browser**: not in a bundle, page source, local storage or a request your
20
+ front end makes, where any visitor or extension can copy it. Use the SDK in
21
+ server-side code (Node 18+, Deno, Bun, edge functions) and have your front end
22
+ call your own server:
23
+
24
+ ```ts
25
+ // server.ts: the key stays here
26
+ import { Dyadic } from "dyadic";
27
+ const client = new Dyadic({ apiKey: process.env.DYADIC_API_KEY });
28
+
29
+ export async function POST(request: Request) {
30
+ const { question } = await request.json();
31
+ // Check your own user's session and limits here before spending credits.
32
+ return Response.json({ answer: await client.ask(String(question).slice(0, 4000), { model: "iris" }) });
33
+ }
34
+ ```
35
+
36
+ `request()` only ever sends the key to the API you configured: a relative path
37
+ joins `baseURL`, and an absolute URL on any other host, port or scheme (or with
38
+ credentials in it) throws `DyadicError` instead of being sent. Redirects are not
39
+ followed. Plain `http://` is accepted only for a local or private-network API.
40
+
41
+ ## Retries
42
+
43
+ GET, HEAD, DELETE and OPTIONS are retried on busy or failing servers. Anything
44
+ else (a completion, an upload, a run) may already have run, and been billed,
45
+ when it fails, so it is retried only if it provably never started: the
46
+ connection was never made, or Dyadic refused it with `rate_limit_exceeded` or
47
+ `concurrency_limit`. Every attempt of one call carries the same
48
+ `Idempotency-Key`; pass `{ idempotencyKey }` to choose it, or `{ idempotent: true }`
49
+ for a call that is safe to send twice.
50
+
51
+ ## Runs: your own agent, Dyadic's tools, your functions
52
+
53
+ ```ts
54
+ const run = await client.runs.run({
55
+ agent: { id: "helpdesk", name: "Helpdesk", instructions: "You answer customers of a small shop." },
56
+ messages: [{ role: "user", content: "Where is order 1042?" }],
57
+ builtin_tools: ["data.web_search"],
58
+ functions: {
59
+ lookup_order: {
60
+ description: "Look up an order by id.",
61
+ parameters: { type: "object", properties: { order_id: { type: "string" } }, required: ["order_id"] },
62
+ handler: async ({ order_id }) => ({ order_id, status: "shipped" }),
63
+ },
64
+ },
65
+ end_user: "customer-17",
66
+ });
67
+ console.log(run.output_text);
68
+ ```
69
+
70
+ `client.runs.create({ ..., stream: true })` streams the run's events.
71
+
72
+ ## Decisions: typed answers, fast and cheap
73
+
74
+ Ask one or more questions about some text (or text and images) and get typed answers
75
+ back: a probability (`predicate`), one of your values (`choice`) or a rubric level
76
+ (`score`). A fraction of a second and a fraction of a cent; billed at cost.
77
+
78
+ ```ts
79
+ const decision = await client.decisions.create({
80
+ input: "I can't log in after resetting my password.",
81
+ questions: [
82
+ { type: "predicate", name: "urgent", instructions: "The customer is blocked from using the product." },
83
+ { type: "choice", name: "team", instructions: "Which team should handle this.", choices: [
84
+ { value: "account", description: "Sign-in and account access" },
85
+ { value: "billing", description: "Payments and invoices" },
86
+ { value: "other", description: "Anything else" },
87
+ ] },
88
+ ],
89
+ });
90
+ ```
91
+
92
+ ## Connected apps: saving into a person's Workspace
93
+
94
+ With the person's approval, an application can save into their Dyadic
95
+ Workspace, naming them by the Discord account linked to their Dyadic account.
96
+
97
+ ```ts
98
+ const status = await client.connections.discord(discordId);
99
+ if (!status.authorized) console.log("Allow this app:", status.authorize_url); // show the person this link
100
+
101
+ try {
102
+ const saved = await client.workspace.saves.create({ discord_id: discordId, kind: "message", title: "From #general",
103
+ text: "Worth keeping", url: "https://discord.com/channels/1/2/3" });
104
+ console.log(saved.url);
105
+ } catch (error) {
106
+ if (error instanceof DyadicError && error.code === "authorization_required") console.log("Allow this app:", error.body.authorize_url);
107
+ }
108
+ ```
109
+
110
+ `kind` is `message`, `conversation` (with `messages`, saved as a chat the
111
+ person can continue unless `as_chat: false`), `image` or `file` (with `files`:
112
+ base64, up to 10 of 8 MB).
113
+
114
+ Full guide: https://dyadic.app/developers/
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "1.2.0";
1
+ export declare const VERSION = "1.3.1";
2
2
  export declare const DEFAULT_BASE_URL = "https://dyadic.app/api/openai/v1";
3
3
  export type Model = "iris" | "iris-think" | (string & {});
4
4
  export type Role = "system" | "developer" | "user" | "assistant" | "tool";
@@ -310,6 +310,59 @@ export interface Moderation {
310
310
  category_applied_input_types: Record<string, string[]>;
311
311
  }[];
312
312
  }
313
+ export type DecisionQuestion = {
314
+ type: "predicate";
315
+ name: string;
316
+ instructions: string;
317
+ } | {
318
+ type: "choice";
319
+ name: string;
320
+ instructions: string;
321
+ choices: {
322
+ value: string;
323
+ description: string;
324
+ }[];
325
+ } | {
326
+ type: "score";
327
+ name: string;
328
+ instructions: string;
329
+ levels: {
330
+ label: string;
331
+ description: string;
332
+ }[];
333
+ };
334
+ export type DecisionAnswer = {
335
+ type: "predicate";
336
+ name: string;
337
+ probability: number;
338
+ } | {
339
+ type: "choice";
340
+ name: string;
341
+ choice: string;
342
+ probabilities: {
343
+ value: string;
344
+ probability: number;
345
+ }[];
346
+ confidence: number;
347
+ } | {
348
+ type: "score";
349
+ name: string;
350
+ score: number;
351
+ probabilities: {
352
+ value: number;
353
+ label: string;
354
+ probability: number;
355
+ }[];
356
+ confidence: number;
357
+ } | {
358
+ type: "refusal";
359
+ name: string;
360
+ };
361
+ export interface Decision {
362
+ id: string | null;
363
+ model: string;
364
+ answers: DecisionAnswer[];
365
+ }
313
366
  export interface FileObject {
314
367
  id: string;
315
368
  object: "file";
@@ -631,6 +684,17 @@ export declare class Dyadic {
631
684
  model?: string;
632
685
  }) => Promise<Moderation>;
633
686
  };
687
+ /** Typed answers: each question a predicate (a probability), a choice or a score, about the input. */
688
+ readonly decisions: {
689
+ create: (params: {
690
+ input: string | {
691
+ role: "user";
692
+ content: unknown[];
693
+ }[];
694
+ questions: DecisionQuestion[];
695
+ model?: string;
696
+ }) => Promise<Decision>;
697
+ };
634
698
  readonly images: {
635
699
  generate: (params: ImageParams) => Promise<ImageResponse>;
636
700
  edit: ({ image, ...params }: Omit<ImageParams, "model"> & {
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // The official TypeScript SDK for the Dyadic API: Iris
2
2
  // through the OpenAI-compatible endpoints at https://dyadic.app/api/openai/v1.
3
- export const VERSION = "1.2.0";
3
+ export const VERSION = "1.3.1";
4
4
  export const DEFAULT_BASE_URL = "https://dyadic.app/api/openai/v1";
5
5
  /** Bytes from the API (speech audio, file downloads). */
6
6
  export class Binary {
@@ -221,6 +221,10 @@ export class Dyadic {
221
221
  this.moderations = {
222
222
  create: (params) => this.request("POST", "/moderations", params),
223
223
  };
224
+ /** Typed answers: each question a predicate (a probability), a choice or a score, about the input. */
225
+ this.decisions = {
226
+ create: (params) => this.request("POST", "/decisions", params),
227
+ };
224
228
  this.images = {
225
229
  generate: (params) => this.request("POST", "/images/generations", params),
226
230
  edit: ({ image, ...params }) => this.request("POST", "/images/edits", undefined, false, { form: form(params, { image: [image, "image.png"] }) }),
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
- {
2
- "name": "dyadic",
3
- "version": "1.2.0",
4
- "description": "The official TypeScript and JavaScript SDK for the Dyadic API: runs with your own agents and functions, plus the OpenAI-compatible API.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
9
- "files": ["dist", "README.md", "CHANGELOG.md"],
10
- "engines": { "node": ">=18" },
11
- "license": "MIT",
12
- "homepage": "https://dyadic.app/developers/",
13
- "scripts": { "build": "tsc", "pretest": "tsc", "test": "node --test test/client.test.js test/transport.test.js", "prepack": "tsc" },
14
- "devDependencies": { "typescript": "5.6.3" }
15
- }
1
+ {
2
+ "name": "dyadic",
3
+ "version": "1.3.1",
4
+ "description": "The official TypeScript and JavaScript SDK for the Dyadic API: runs with your own agents and functions, plus the OpenAI-compatible API.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
9
+ "files": ["dist", "README.md", "CHANGELOG.md"],
10
+ "engines": { "node": ">=18" },
11
+ "license": "MIT",
12
+ "homepage": "https://dyadic.app/developers/",
13
+ "scripts": { "build": "tsc", "pretest": "tsc", "test": "node --test test/client.test.js test/transport.test.js", "prepack": "tsc" },
14
+ "devDependencies": { "typescript": "5.6.3" }
15
+ }