codai docs
SDK-uri

TypeScript

Pachetul codai-sdk pentru Node și runtime-uri edge — configurare, grupuri de resurse cu paritate completă OpenAPI, chat, streaming, agenți, sesiuni, erori și migrarea de la SDK-ul OpenAI.

codai-sdk este clientul oficial TypeScript. Zero dependențe (fetch de platformă), complet tipizat din OpenAPI-ul gateway-ului, doar ESM, Node 18+ și runtime-uri edge moderne. De la 0.3.0 fiecare operație a API-ului gateway are exact o metodă în SDK — un test de paritate din pachet eșuează când cele două diverg.

pnpm add codai-sdk

Configurare

import { Codai } from 'codai-sdk';

const codai = new Codai({
  apiKey: process.env.CODAI_API_KEY!, // obligatoriu
  baseUrl: 'https://ai.codai.ro',      // implicit
  sessionId: 'my-project',             // opțional — activează memoria de sesiune + sticky routing
  device: '<uuid>',                    // opțional — x-codai-device pentru sesiuni partajate / host-uri
  client: 'my-app/1.2.0',              // opțional — eticheta x-codai-client a suprafeței
  timeoutMs: 120_000,                  // implicit
  maxRetries: 2,                       // reîncercări pe 429 / 5xx cu backoff exponențial
});

Proprietate

Tip

Grupuri de resurse

Fiecare operație a gateway-ului este accesibilă ca metodă pe client, grupată după tag. Metodele de top din 0.2.x (chat(), chatStream(), embeddings(), models(), feedback(), mintToken(), agents.run(), audio.*) funcționează în continuare; chat, embeddings, models și feedback sunt grupuri de resurse apelabile, așa că codai.chat({ messages }) și codai.chat.completions.create({ messages }) sunt același apel.

ProprietateMetodeGateway
chat.completionscreate, streamPOST /v1/chat/completions
messagescreate, streamPOST /v1/messages — formatul Anthropic Messages
responsescreate, streamPOST /v1/responses — formatul OpenAI Responses
embeddingscreatePOST /v1/embeddings
audiotranscribe, transcribeDetailed, speech, speechDetailed/v1/audio/*
tokenscreatePOST /v1/tokens
modelslistGET /v1/models
healthget, ready, status/health, /health/ready, /status
agentsrun; runs.create, runs.get, runs.steps, runs.stats, runs.cancel, runs.stream/v1/agents/*
toolssearch, fetch/v1/tools/*
taskslist, pending, stats, get, confirm/v1/tasks/*
sessionscreate, list, get, update, delete, dispatch, stream; events.*, controls.*, lease.*, shares.*/v1/sessions/*
deviceslist, update, delete, dispatchInbox/v1/devices/*
hostslist, exec, postResult, stream/v1/hosts/*
orgscreate, list; members.list, members.add, members.remove/v1/orgs/*
accountget, update/v1/account
receiptgetGET /v1/receipt
feedbacksubmitPOST /v1/feedback
phoneModelslistGET /v1/phone/models

Fiecare metodă primește un ultim argument opțional { ext, signal }. ext este un obiect tipizat CodaiRequestExtensions care acoperă toate header-ele de cerere X-Codai-* documentate (effort, thinking, thinkingBudget, cache, noTask, taskId, incognito, mode: 'agent', bestOf, compact, shareToken, …) — vezi header-e.

await codai.chat.completions.create(
  { messages, model: 'codai' },
  { ext: { effort: 'high', thinking: true, thinkingBudget: 8192 } },
);

// stream-urile SSE native codai sunt iteratoare async de frame-uri { event, data }
const { id } = await codai.agents.runs.create({ task: 'Rezumă README-ul repo-ului.' });
for await (const ev of codai.agents.runs.stream(id)) {
  if (ev.event === 'done') console.log(ev.data.status, ev.data.result);
}

// sesiunile partajate au nevoie de un id de dispozitiv pe client
const session = await codai.sessions.create({ session_key: 'desktop-main' });
await codai.sessions.lease.acquire(session.id);
for await (const frame of codai.sessions.stream(session.id, { after: 0 })) {
  // frame.event ∈ 'event' | 'control' | 'lease' | 'presence'
}

Tipurile de cerere și răspuns sunt generate din documentul OpenAPI (src/generated/gateway.ts, pnpm gen) și re-exportate atât brut (paths, components, operations), cât și ca aliasuri prietenoase (ChatCompletionRequest, Task, Session, AccountView, …).

Chat

const res = await codai.chat({
  messages: [{ role: 'user', content: 'Explică iteratorii async într-o linie.' }],
});

res.content;    // textul asistentului (prima alegere)
res.toolCalls;  // tool calls pe prima alegere (array gol dacă nu există)
res.routedTo;   // modelul upstream care a servit — din x-codai-routed-to
res.requestId;  // din x-codai-trace-id / x-request-id
res.eventId;    // din x-codai-event-id — ținta exactă pentru feedback()
res.usage;      // { promptTokens, completionTokens } | null
res.execVerify; // 'passed' | 'refined' | … pe codai-labs, altfel null
res.raw;        // răspunsul complet de formă OpenAI
res.headers;    // toate header-ele de răspuns x-codai-*

ChatOptions:

Proprietate

Tip

Tool calls

Trimiți tool-uri OpenAI de tip funcție și citești res.raw.choices[0].message.tool_calls. SDK-ul nu execută niciodată tool-uri pentru tine.

const res = await codai.chat({
  messages: [{ role: 'user', content: 'Vremea la Cluj?' }],
  tools: [
    {
      type: 'function',
      function: {
        name: 'get_weather',
        parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
      },
    },
  ],
});

const call = (res.raw.choices as any[])[0].message.tool_calls?.[0];
if (call) {
  const result = await getWeather(JSON.parse(call.function.arguments).city);
  const followUp = await codai.chat({
    messages: [
      { role: 'user', content: 'Vremea la Cluj?' },
      { role: 'assistant', content: '', ...(res.raw.choices as any[])[0].message },
      { role: 'tool', tool_call_id: call.id, content: JSON.stringify(result) },
    ],
  });
}

Streaming

chatStream() returnează un obiect care este atât un iterabil async de delta-uri de text, cât și purtătorul unei promisiuni .final cu metadatele pe care le afli doar la sfârșit.

const stream = codai.chatStream({
  messages: [{ role: 'user', content: 'Scrie un haiku despre TypeScript.' }],
});

for await (const delta of stream) {
  process.stdout.write(delta);
}

const { content, requestId, usage, routedTo, toolCalls } = await stream.final;
if (requestId) await codai.feedback(requestId, 1);

ChatStreamResult:

CâmpSemnificație
contentTextul concatenat.
toolCallsTool calls asamblate din delta-urile pe bucăți — id, function.name, function.arguments deja unite.
requestId, routedTo, execVerifyLa fel ca pe chat().
usageDin chunk-ul final de usage al gateway-ului, cu cachedTokens când e raportat.
finishReasonfinish_reason al ultimului chunk cu conținut.
headersHeader-ele răspunsului (x-codai-cost-estimate: pending pe stream-urile native).

.final se respinge dacă stream-ul dă eroare; SDK-ul suprimă un avertisment de unhandled-rejection dacă nu-l aștepți niciodată. Doar frame-urile data: sunt parsate — comentariile keep-alive sunt ignorate. stream.chunks() produce obiectele brute chat.completion.chunk când ai nevoie de mai mult decât delta-uri de text.

Agent server-side

agents.run() rulează o buclă plan-and-execute pe gateway. Procesul tău rămâne subțire; planificarea, folosirea tool-urilor și iterarea se întâmplă server-side și se facturează pe cheia ta.

const run = await codai.agents.run({
  task: 'Rezumă punctele cheie ale textului furnizat.',
  context: '…input-ul tău…',    // opțional, ≤ 256 000 caractere
  system: 'Răspunde în română.', // opțional, ≤ 32 000 caractere
  model: 'codai',              // opțional
});

run.result;  // string
run.model;   // upstream-ul care a servit
run.eventId; // id-ul rândului de usage — îl trimiți la feedback()
run.usage;   // obiect usage brut | null

task este obligatoriu (≤ 64 000 caractere). Rulările de agent folosesc același plafon de task-uri ca chat-ul.

Rulările persistente stau sub agents.runs: create() returnează 202 cu id-ul rulării, get() o interoghează, steps() listează traseul de pași persistat, stats(days) agregă rulările tale, cancel() trece o rulare aflată în coadă sau în execuție pe cancelled, iar stream() este un iterator async de evenimente step / done / timeout.

Feedback

Voturile pe o cerere finalizată antrenează priorurile de rutare. rating este 1 sau -1; comment e opțional (≤ 500 caractere).

const res = await codai.chat({ messages: [{ role: 'user', content: 'salut' }] });
if (res.requestId) await codai.feedback(res.requestId, 1, 'exact ce trebuia');

Forma apelabilă trimite requestId ca event_id. Ca să notezi după session_id sau după propriul client_request_id, folosești codai.feedback.submit({ session_id, rating }) — vezi header-e.

Embeddings

const { embeddings, raw } = await codai.embeddings({
  input: ['salut', 'lume'],
  model: 'codai-embed', // implicit
  dimensions: 512,      // opțional
});
embeddings[0]; // number[]

Audio

import { readFile, writeFile } from 'node:fs/promises';

// Speech-to-text — încărcare multipart, maximum 25 MB. Returnează string-ul transcris.
const text = await codai.audio.transcribe({
  file: await readFile('clip.webm'), // Blob | Uint8Array | ArrayBuffer
  filename: 'clip.webm',             // extensia determină detecția formatului
  model: 'codai-transcribe',         // implicit
});

// Text-to-speech — returnează byte-ii audio (mp3 implicit).
const audio = await codai.audio.speech({ input: 'Salut de la codai.', voice: 'alloy' });
await writeFile('salut.mp3', Buffer.from(audio));

transcribe() este singura metodă care nu reîncearcă — un body multipart nu poate fi retrimis în siguranță. Trimite mai departe și language, prompt, responseFormat și temperature; transcribeDetailed() / speechDetailed() returnează pe lângă acestea body-ul brut, contentType și header-ele.

Tokeni efemeri

Pentru browsere și webview-uri care nu trebuie să dețină niciodată cheia ta:

const { token, expiresAt, scope } = await codai.mintToken('realtime', 600);
// dai `token` clientului; funcționează doar pe suprafața realtime, cel mult ttl secunde

Scopuri: 'realtime' | 'audio' | 'embeddings'. TTL 60–3 600 s. Vezi tokeni efemeri.

Modele

const ids = await codai.models();        // Array<{ id: string }> — forma din 0.2.x
const full = await codai.models.list();  // CoreModel[] cu obiectul de capabilități `codai`

Returnează array-ul data din GET /v1/models — tot ce poate adresa cheia ta.

Erori

Fiecare răspuns non-2xx după reîncercări aruncă un CodaiError cu statusul HTTP, code-ul stabil al gateway-ului, requestId (x-codai-trace-id), retryAfter (secunde, pe 429) și body-ul parsat (anvelopa de eroare). Eșecurile de rețea după reîncercări aruncă cu status: 0.

import { Codai, CodaiError } from 'codai-sdk';

try {
  await codai.chat({ messages: [{ role: 'user', content: 'salut' }] });
} catch (err) {
  if (err instanceof CodaiError) {
    console.error(err.status, err.code, err.requestId, err.message);
    if (err.code === 'quota_exceeded') {
      /* alocarea de task-uri a planului consumată — aștepți err.retryAfter secunde */
    }
  }
}

Valorile stabile de code sunt listate la limite și prețuri.

Migrarea de la SDK-ul OpenAI

Payload-ul are formă OpenAI, așa că schimbarea e clientul, nu mesajele.

// înainte
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey, baseURL: 'https://ai.codai.ro/v1' });
const r = await openai.chat.completions.create({ model: 'codai', messages });
r.choices[0].message.content;

// după
import { Codai } from 'codai-sdk';
const codai = new Codai({ apiKey });
const res = await codai.chat({ messages });
res.content;

Ce câștigi: routedTo, requestId și usage ca câmpuri de prim rang, .final pe stream-uri cu tool calls asamblate, acces dintr-o opțiune la sesiuni / mod agent / compactare / best-of și reîncercări cu backoff. Ce păstrezi: clientul openai funcționează în continuare pe https://ai.codai.ro/v1 dacă îl preferi — ambii pot împărți aceeași cheie și același id de sesiune.

Export implicit

Codai este și exportul implicit, așa că import Codai from 'codai-sdk' funcționează.

Pe această pagină