codai-sdk 0.1.0 → 0.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/README.md CHANGED
@@ -5,12 +5,14 @@
5
5
 
6
6
  Official TypeScript SDK for the [codai](https://codai.ro) AI gateway — a single
7
7
  OpenAI-compatible endpoint with smart routing, sessions, server-side agents,
8
- streaming, embeddings, audio, and feedback.
8
+ streaming, embeddings, audio, outcome-billed tasks, shared sessions, devices,
9
+ hosts, orgs and account — **full parity with the public gateway OpenAPI**.
9
10
 
10
11
  - **Zero dependencies** — uses the platform `fetch`.
11
12
  - Works in **Node 18+** and modern edge runtimes.
12
13
  - **OpenAI-compatible** chat surface with codai extensions.
13
- - Fully typed.
14
+ - Fully typed — request/response types are **generated from the OpenAPI spec**.
15
+ - Every `operationId` of the gateway has exactly one method (parity-tested).
14
16
 
15
17
  ```bash
16
18
  npm install codai-sdk
@@ -35,6 +37,38 @@ console.log(res.content);
35
37
  console.log(res.routedTo); // which upstream model actually served
36
38
  ```
37
39
 
40
+ ## Resource groups
41
+
42
+ Everything hangs off the `Codai` client. The 0.2.x top-level methods (`chat()`,
43
+ `chatStream()`, `embeddings()`, `models()`, `feedback()`, `mintToken()`,
44
+ `agents.run()`, `audio.*`) still work; the resource groups below are the full
45
+ surface.
46
+
47
+ | Group | Methods | Gateway paths |
48
+ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
49
+ | `chat.completions` | `create`, `stream` | `POST /v1/chat/completions` |
50
+ | `messages` | `create`, `stream` | `POST /v1/messages` (Anthropic wire) |
51
+ | `responses` | `create`, `stream` | `POST /v1/responses` (OpenAI Responses wire) |
52
+ | `embeddings` | `create` | `POST /v1/embeddings` |
53
+ | `audio` | `transcribe`, `transcribeDetailed`, `speech`, `speechDetailed` | `POST /v1/audio/transcriptions`, `POST /v1/audio/speech` |
54
+ | `tokens` | `create` | `POST /v1/tokens` |
55
+ | `models` | `list` | `GET /v1/models` |
56
+ | `health` | `get`, `ready`, `status` | `GET /health`, `GET /health/ready`, `GET /status` |
57
+ | `agents` | `run`; `runs.create`, `runs.get`, `runs.steps`, `runs.stats`, `runs.cancel`, `runs.stream` | `/v1/agents/run`, `/v1/agents/runs/*` |
58
+ | `tools` | `search`, `fetch` | `POST /v1/tools/search`, `POST /v1/tools/fetch` |
59
+ | `tasks` | `list`, `pending`, `stats`, `get`, `confirm` | `/v1/tasks/*` |
60
+ | `sessions` | `create`, `list`, `get`, `update`, `delete`, `dispatch`, `stream`; `events.list/append`; `controls.list/submit/markApplied`; `lease.acquire/renew/release`; `shares.list/create/delete/sharedWithMe` | `/v1/sessions/*` |
61
+ | `devices` | `list`, `update`, `delete`, `dispatchInbox` | `/v1/devices/*` |
62
+ | `hosts` | `list`, `exec`, `postResult`, `stream` | `/v1/hosts/*` |
63
+ | `orgs` | `create`, `list`; `members.list/add/remove` | `/v1/orgs/*` |
64
+ | `account` | `get`, `update` | `GET/PATCH /v1/account` |
65
+ | `receipt` | `get` | `GET /v1/receipt` |
66
+ | `feedback` | `submit` | `POST /v1/feedback` |
67
+ | `phoneModels` | `list` | `GET /v1/phone/models` |
68
+
69
+ Every method takes an optional last argument `{ ext, signal }` where `ext` is a
70
+ typed bag of `X-Codai-*` extension headers (see below).
71
+
38
72
  ## Streaming
39
73
 
40
74
  ```ts
@@ -45,6 +79,47 @@ for await (const delta of codai.chatStream({
45
79
  }
46
80
  ```
47
81
 
82
+ After the stream ends, await `.final` for metadata (request id, token usage,
83
+ which model served, and any tool calls) — e.g. to submit feedback on a
84
+ streamed response:
85
+
86
+ ```ts
87
+ const stream = codai.chatStream({
88
+ messages: [{ role: 'user', content: 'Write a haiku about TypeScript.' }],
89
+ });
90
+
91
+ for await (const delta of stream) {
92
+ process.stdout.write(delta);
93
+ }
94
+
95
+ const { requestId, usage, routedTo, toolCalls } = await stream.final;
96
+ if (requestId) await codai.feedback(requestId, 1);
97
+ ```
98
+
99
+ `chat.completions.stream()` is the same stream; `stream.chunks()` yields the raw
100
+ `chat.completion.chunk` objects.
101
+
102
+ ## Anthropic Messages and OpenAI Responses wires
103
+
104
+ ```ts
105
+ // Anthropic Messages shape — text blocks, tool_use, streaming events
106
+ const msg = await codai.messages.create({
107
+ messages: [{ role: 'user', content: 'Salut!' }],
108
+ max_tokens: 256,
109
+ });
110
+ msg.text; // concatenated text blocks
111
+
112
+ for await (const ev of codai.messages.stream({ messages, max_tokens: 256 })) {
113
+ if (ev.type === 'content_block_delta') {
114
+ /* ev.delta */
115
+ }
116
+ }
117
+
118
+ // OpenAI Responses shape
119
+ const r = await codai.responses.create({ input: 'Say hi.' });
120
+ r.outputText;
121
+ ```
122
+
48
123
  ## Server-side agent
49
124
 
50
125
  Run a plan-and-execute loop on the gateway — the heavy lifting (planning, tool
@@ -59,6 +134,75 @@ const run = await codai.agents.run({
59
134
  console.log(run.result);
60
135
  ```
61
136
 
137
+ Async runs are persisted and can be polled, streamed and cancelled:
138
+
139
+ ```ts
140
+ const { id } = await codai.agents.runs.create({ task: '…', client_request_id: 'ci-8812' });
141
+ for await (const ev of codai.agents.runs.stream(id)) {
142
+ if (ev.event === 'step') console.log(ev.data.kind, ev.data.summary);
143
+ if (ev.event === 'done') console.log(ev.data.status, ev.data.result);
144
+ }
145
+ const run = await codai.agents.runs.get(id);
146
+ ```
147
+
148
+ ## Tools, tasks and receipts
149
+
150
+ ```ts
151
+ const hits = await codai.tools.search({ query: 'BNR EUR RON' });
152
+ const page = await codai.tools.fetch({ url: 'https://bnr.ro', format: 'markdown' });
153
+
154
+ const { tasks, next_cursor } = await codai.tasks.list({ outcome: 'unconfirmed', limit: 20 });
155
+ await codai.tasks.confirm(tasks[0].id, { outcome: 'confirmed' });
156
+
157
+ const receipt = await codai.receipt.get({ sessionId: 'my-project' });
158
+ receipt.cost_usd;
159
+ ```
160
+
161
+ ## Shared sessions, devices and hosts
162
+
163
+ Shared-sessions and hosts calls need a device UUID (`x-codai-device`). Set it once
164
+ on the client:
165
+
166
+ ```ts
167
+ const codai = new Codai({
168
+ apiKey,
169
+ device: '6f1c2b3a-4d5e-4f60-8a9b-0c1d2e3f4a5b',
170
+ deviceName: 'ci-runner',
171
+ devicePlatform: 'cli',
172
+ });
173
+
174
+ const session = await codai.sessions.create({ session_key: 'desktop-main', title: 'Refactor' });
175
+ await codai.sessions.lease.acquire(session.id);
176
+ await codai.sessions.events.append(session.id, {
177
+ events: [{ kind: 'user', payload: { text: 'hello' }, client_event_id: 'e1' }],
178
+ });
179
+
180
+ for await (const frame of codai.sessions.stream(session.id, { after: 0 })) {
181
+ // frame.event ∈ 'event' | 'control' | 'lease' | 'presence'
182
+ }
183
+
184
+ await codai.sessions.dispatch(session.id, { device_id: phoneId, text: 'continue on the phone' });
185
+
186
+ // Host relay: run one op on a connected desktop
187
+ const hosts = await codai.hosts.list();
188
+ const out = await codai.hosts.exec(hosts[0].device_id, 'shell', { cmd: 'git status' });
189
+ ```
190
+
191
+ ## Orgs and account
192
+
193
+ ```ts
194
+ const org = await codai.orgs.create('Acme Robotics');
195
+ await codai.orgs.members.add(org.id, { email: 'ana@example.com', role: 'admin' });
196
+ await codai.sessions.shares.create(session.id, {
197
+ principal_type: 'org',
198
+ principal_id: org.id,
199
+ role: 'editor',
200
+ });
201
+
202
+ const me = await codai.account.get();
203
+ await codai.account.update({ training_opt_out: true });
204
+ ```
205
+
62
206
  ## Feedback
63
207
 
64
208
  ```ts
@@ -97,6 +241,9 @@ const codai = new Codai({
97
241
  apiKey: process.env.CODAI_API_KEY!,
98
242
  baseUrl: 'https://ai.codai.ro', // default
99
243
  sessionId: 'my-project', // enables session memory + stickiness
244
+ device: '<uuid>', // shared sessions / hosts (x-codai-device)
245
+ client: 'my-app/1.2.0', // x-codai-client surface tag
246
+ defaults: { effort: 'medium' }, // any other X-Codai-* defaults
100
247
  timeoutMs: 120_000,
101
248
  maxRetries: 2,
102
249
  });
@@ -104,7 +251,8 @@ const codai = new Codai({
104
251
 
105
252
  ## codai extensions
106
253
 
107
- The chat surface is OpenAI-compatible, with a few opt-in extensions:
254
+ The chat surface is OpenAI-compatible, with opt-in extensions. On `chat()` the
255
+ 0.2.x option names still work:
108
256
 
109
257
  | Option | Description |
110
258
  | ----------------- | ----------------------------------------------------------------------- |
@@ -113,6 +261,35 @@ The chat surface is OpenAI-compatible, with a few opt-in extensions:
113
261
  | `compact: "auto"` | Server-side context compaction. |
114
262
  | `bestOf` | Best-of-N sampling override (`0` disables, `3` forces). |
115
263
 
264
+ Every method also accepts `{ ext }` — a typed `CodaiRequestExtensions` bag that
265
+ covers all 41 documented `X-Codai-*` request headers (`effort`, `thinking`,
266
+ `thinkingBudget`, `cache`, `noTask`, `taskId`, `incognito`, `noRecall`,
267
+ `provenOnly`, `repo`, `agentId`, `disableSubagents`, `mode`, `serverTools`,
268
+ `orchestrate`, `cascade`, `bestOf`, `reflect`, `stepVerify`, `plan`, `consensus`,
269
+ `compact`, `retrieval`, `heuristics`, `identity`, `playbook`, `debug`, `device`,
270
+ `shareToken`, …):
271
+
272
+ ```ts
273
+ await codai.chat.completions.create(
274
+ { messages, model: 'codai' },
275
+ { ext: { effort: 'high', thinking: true, thinkingBudget: 8192, taskId: 'task_42' } },
276
+ );
277
+ ```
278
+
279
+ ## Generated types
280
+
281
+ `paths`, `components` and `operations` from the gateway OpenAPI are exported, and
282
+ every resource re-exports friendly aliases (`ChatCompletionRequest`, `Task`,
283
+ `Session`, `AccountView`, …):
284
+
285
+ ```ts
286
+ import type { components, Task, SessionStreamEvent } from 'codai-sdk';
287
+ type Receipt = components['schemas']['AccountReceipt'];
288
+ ```
289
+
290
+ Regenerate after a spec change with `pnpm gen`; `operations.test.ts` fails when
291
+ the spec and the client drift.
292
+
116
293
  ## Migrating from the OpenAI SDK
117
294
 
118
295
  The chat payload is OpenAI-shaped, so migration is mostly swapping the client:
@@ -132,11 +309,28 @@ try {
132
309
  await codai.chat({ messages: [{ role: 'user', content: 'hi' }] });
133
310
  } catch (err) {
134
311
  if (err instanceof CodaiError) {
135
- console.error(err.status, err.message, err.body);
312
+ console.error(err.status, err.code, err.requestId, err.retryAfter, err.body);
136
313
  }
137
314
  }
138
315
  ```
139
316
 
317
+ `code` is the stable gateway error code (`invalid_api_key`, `rate_limit_exceeded`,
318
+ `quota_exceeded`, `lease_held`, `host_offline`, …); `retryAfter` is the
319
+ `Retry-After` value in seconds on 429s.
320
+
321
+ ## Migrating from 0.2.x
322
+
323
+ Nothing breaks: every 0.2.x method keeps its signature. What changed underneath:
324
+
325
+ - `chat`, `embeddings`, `models` and `feedback` are now **callable resource
326
+ groups** — `codai.chat({...})` still works and `codai.chat.completions.create({...})`
327
+ is the same call with the raw OpenAI body.
328
+ - `ChatResult` gained `eventId`, `toolCalls`, `headers`; `ChatStreamResult` gained
329
+ `finishReason`, `headers`, `usage.cachedTokens`.
330
+ - `CodaiError` gained `code`, `requestId`, `retryAfter`.
331
+ - Streams are no longer subject to `timeoutMs` and are never retried.
332
+ - New client options: `device`, `deviceName`, `devicePlatform`, `client`, `defaults`, `fetch`.
333
+
140
334
  ## License
141
335
 
142
336
  [MIT](./LICENSE) © codai