codai-sdk 0.1.0 → 0.2.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 CHANGED
@@ -45,6 +45,23 @@ for await (const delta of codai.chatStream({
45
45
  }
46
46
  ```
47
47
 
48
+ After the stream ends, await `.final` for metadata (request id, token usage,
49
+ which model served, and any tool calls) — e.g. to submit feedback on a
50
+ streamed response:
51
+
52
+ ```ts
53
+ const stream = codai.chatStream({
54
+ messages: [{ role: 'user', content: 'Write a haiku about TypeScript.' }],
55
+ });
56
+
57
+ for await (const delta of stream) {
58
+ process.stdout.write(delta);
59
+ }
60
+
61
+ const { requestId, usage, routedTo, toolCalls } = await stream.final;
62
+ if (requestId) await codai.feedback(requestId, 1);
63
+ ```
64
+
48
65
  ## Server-side agent
49
66
 
50
67
  Run a plan-and-execute loop on the gateway — the heavy lifting (planning, tool
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@
4
4
  * Full-surface client:
5
5
  * - chat(): OpenAI-compatible completions with codai extensions
6
6
  * (session, agent mode, compaction, best-of)
7
- * - chatStream(): SSE streaming with an async-iterator interface
7
+ * - chatStream(): SSE streaming — async-iterate text deltas, then await
8
+ * `.final` for requestId / usage / routedTo / tool calls
8
9
  * - agents.run(): server-side agent loop (POST /v1/agents/run)
9
10
  * - feedback(): thumbs rating on a completed request
10
11
  * - models(): list models available to the key
@@ -65,6 +66,37 @@ interface ChatResult {
65
66
  completionTokens: number;
66
67
  } | null;
67
68
  }
69
+ interface TokenUsage {
70
+ promptTokens: number;
71
+ completionTokens: number;
72
+ }
73
+ /** Final metadata for a completed stream — resolved via `chatStream(...).final`. */
74
+ interface ChatStreamResult {
75
+ /** Full concatenated assistant text. */
76
+ content: string;
77
+ /** Tool calls assembled from streamed deltas (empty if none). */
78
+ toolCalls: Array<Record<string, unknown>>;
79
+ /** Request id — pass to feedback(). */
80
+ requestId: string | null;
81
+ /** Which upstream model actually served (x-codai-routed-to). */
82
+ routedTo: string | null;
83
+ /** Verification status for verified-execution responses, if present. */
84
+ execVerify: string | null;
85
+ /** Token usage if the gateway sent a final usage frame. */
86
+ usage: TokenUsage | null;
87
+ }
88
+ /**
89
+ * A live chat stream. Async-iterate it to get text deltas, and await `.final`
90
+ * afterwards for metadata (requestId, usage, routedTo, tool calls).
91
+ *
92
+ * const stream = codai.chatStream({ messages });
93
+ * for await (const delta of stream) process.stdout.write(delta);
94
+ * const { requestId, usage } = await stream.final;
95
+ */
96
+ interface ChatStream extends AsyncIterable<string> {
97
+ /** Resolves once the stream completes (or rejects if it errors). */
98
+ readonly final: Promise<ChatStreamResult>;
99
+ }
68
100
  interface AgentRunOptions {
69
101
  task: string;
70
102
  context?: string;
@@ -117,8 +149,11 @@ declare class Codai {
117
149
  private readonly maxRetries;
118
150
  constructor(opts: CodaiClientOptions);
119
151
  chat(opts: ChatOptions): Promise<ChatResult>;
120
- /** Streaming chat: async-iterate text deltas; `final` resolves afterwards. */
121
- chatStream(opts: ChatOptions): AsyncGenerator<string, void, undefined>;
152
+ /**
153
+ * Streaming chat. Async-iterate the returned object for text deltas, then
154
+ * `await stream.final` for metadata (requestId, usage, routedTo, toolCalls).
155
+ */
156
+ chatStream(opts: ChatOptions): ChatStream;
122
157
  readonly agents: {
123
158
  run: (opts: AgentRunOptions) => Promise<AgentRunResult>;
124
159
  };
@@ -147,4 +182,4 @@ declare class Codai {
147
182
  private request;
148
183
  }
149
184
 
150
- export { type AgentRunOptions, type AgentRunResult, type ChatMessage, type ChatOptions, type ChatResult, Codai, type CodaiClientOptions, CodaiError, type EmbeddingsOptions, type EphemeralScope, type EphemeralToken, type SpeechOptions, type TranscriptionOptions, Codai as default };
185
+ export { type AgentRunOptions, type AgentRunResult, type ChatMessage, type ChatOptions, type ChatResult, type ChatStream, type ChatStreamResult, Codai, type CodaiClientOptions, CodaiError, type EmbeddingsOptions, type EphemeralScope, type EphemeralToken, type SpeechOptions, type TokenUsage, type TranscriptionOptions, Codai as default };
package/dist/index.js CHANGED
@@ -10,6 +10,26 @@ var CodaiError = class extends Error {
10
10
  body;
11
11
  };
12
12
  var RETRYABLE = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
13
+ function mergeToolCall(acc, index, fragment) {
14
+ const existing = acc.get(index) ?? { index };
15
+ for (const [key, val] of Object.entries(fragment)) {
16
+ if (key === "index") continue;
17
+ if (key === "function" && val && typeof val === "object") {
18
+ const fn = existing.function ?? {};
19
+ for (const [fk, fv] of Object.entries(val)) {
20
+ if (fk === "arguments") {
21
+ fn.arguments = String(fn.arguments ?? "") + String(fv ?? "");
22
+ } else {
23
+ fn[fk] = fv;
24
+ }
25
+ }
26
+ existing.function = fn;
27
+ } else {
28
+ existing[key] = val;
29
+ }
30
+ }
31
+ acc.set(index, existing);
32
+ }
13
33
  var Codai = class {
14
34
  apiKey;
15
35
  baseUrl;
@@ -45,36 +65,107 @@ var Codai = class {
45
65
  } : null
46
66
  };
47
67
  }
48
- /** Streaming chat: async-iterate text deltas; `final` resolves afterwards. */
49
- async *chatStream(opts) {
50
- const res = await this.request("/v1/chat/completions", this.chatBody(opts, true), {
68
+ /**
69
+ * Streaming chat. Async-iterate the returned object for text deltas, then
70
+ * `await stream.final` for metadata (requestId, usage, routedTo, toolCalls).
71
+ */
72
+ chatStream(opts) {
73
+ const requestPromise = this.request("/v1/chat/completions", this.chatBody(opts, true), {
51
74
  signal: opts.signal,
52
75
  headers: this.chatHeaders(opts)
53
76
  });
54
- const reader = res.body?.getReader();
55
- if (!reader) throw new CodaiError("no response body", res.status, null);
56
- const decoder = new TextDecoder();
57
- let buffer = "";
58
- for (; ; ) {
59
- const { done, value } = await reader.read();
60
- if (done) break;
61
- buffer += decoder.decode(value, { stream: true });
62
- const frames = buffer.split("\n\n");
63
- buffer = frames.pop() ?? "";
64
- for (const frame of frames) {
65
- for (const line of frame.split("\n")) {
66
- if (!line.startsWith("data:")) continue;
67
- const data = line.slice(5).trim();
68
- if (data === "[DONE]") return;
69
- try {
70
- const chunk = JSON.parse(data);
71
- const delta = chunk.choices?.[0]?.delta?.content;
72
- if (delta) yield delta;
73
- } catch {
77
+ let resolveFinal;
78
+ let rejectFinal;
79
+ const final = new Promise((resolve, reject) => {
80
+ resolveFinal = resolve;
81
+ rejectFinal = reject;
82
+ });
83
+ final.catch(() => {
84
+ });
85
+ const toolCallsByIndex = /* @__PURE__ */ new Map();
86
+ async function* iterate() {
87
+ let content = "";
88
+ let usage = null;
89
+ let res;
90
+ try {
91
+ res = await requestPromise;
92
+ } catch (err) {
93
+ rejectFinal(err);
94
+ throw err;
95
+ }
96
+ const reader = res.body?.getReader();
97
+ if (!reader) {
98
+ const e = new CodaiError("no response body", res.status, null);
99
+ rejectFinal(e);
100
+ throw e;
101
+ }
102
+ const decoder = new TextDecoder();
103
+ let buffer = "";
104
+ let finished = false;
105
+ const buildResult = () => ({
106
+ content,
107
+ toolCalls: [...toolCallsByIndex.values()],
108
+ requestId: res.headers.get("x-request-id"),
109
+ routedTo: res.headers.get("x-codai-routed-to"),
110
+ execVerify: res.headers.get("x-codai-exec-verify"),
111
+ usage
112
+ });
113
+ try {
114
+ for (; ; ) {
115
+ const { done, value } = await reader.read();
116
+ if (done) break;
117
+ buffer += decoder.decode(value, { stream: true });
118
+ const frames = buffer.split("\n\n");
119
+ buffer = frames.pop() ?? "";
120
+ for (const frame of frames) {
121
+ for (const line of frame.split("\n")) {
122
+ if (!line.startsWith("data:")) continue;
123
+ const data = line.slice(5).trim();
124
+ if (data === "[DONE]") {
125
+ finished = true;
126
+ break;
127
+ }
128
+ let chunk;
129
+ try {
130
+ chunk = JSON.parse(data);
131
+ } catch {
132
+ continue;
133
+ }
134
+ if (chunk.usage) {
135
+ usage = {
136
+ promptTokens: chunk.usage.prompt_tokens ?? 0,
137
+ completionTokens: chunk.usage.completion_tokens ?? 0
138
+ };
139
+ }
140
+ const delta = chunk.choices?.[0]?.delta;
141
+ if (delta?.tool_calls) {
142
+ for (const tc of delta.tool_calls) {
143
+ const idx = typeof tc.index === "number" ? tc.index : 0;
144
+ mergeToolCall(toolCallsByIndex, idx, tc);
145
+ }
146
+ }
147
+ if (delta?.content) {
148
+ content += delta.content;
149
+ yield delta.content;
150
+ }
151
+ }
152
+ if (finished) break;
74
153
  }
154
+ if (finished) break;
75
155
  }
156
+ resolveFinal(buildResult());
157
+ } catch (err) {
158
+ rejectFinal(err);
159
+ throw err;
160
+ } finally {
161
+ reader.releaseLock?.();
76
162
  }
77
163
  }
164
+ const gen = iterate.call(this);
165
+ return {
166
+ [Symbol.asyncIterator]: () => gen,
167
+ final
168
+ };
78
169
  }
79
170
  // -------------------------------------------------------------- agents
80
171
  agents = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * codai-sdk — official TypeScript client for the codai AI gateway.\n *\n * Full-surface client:\n * - chat(): OpenAI-compatible completions with codai extensions\n * (session, agent mode, compaction, best-of)\n * - chatStream(): SSE streaming with an async-iterator interface\n * - agents.run(): server-side agent loop (POST /v1/agents/run)\n * - feedback(): thumbs rating on a completed request\n * - models(): list models available to the key\n *\n * Zero dependencies; works in Node 18+ (global fetch) and modern edge\n * runtimes. Memory tools (memory_recall / memory_remember) and skills\n * (use_skill) are GATEWAY-NATIVE — the model invokes them server-side;\n * nothing to call from the SDK beyond enabling a session.\n */\n\nexport interface CodaiClientOptions {\n apiKey: string;\n /** Defaults to https://ai.codai.ro */\n baseUrl?: string;\n /** Stable conversation/session id — enables session memory + stickiness. */\n sessionId?: string;\n /** Default request timeout (ms). Default 120_000. */\n timeoutMs?: number;\n /** Retries on 429/5xx with exponential backoff. Default 2. */\n maxRetries?: number;\n}\n\nexport interface ChatMessage {\n role: 'system' | 'user' | 'assistant' | 'tool';\n content: string | Array<Record<string, unknown>>;\n tool_call_id?: string;\n name?: string;\n}\n\nexport interface ChatOptions {\n model?: string;\n messages: ChatMessage[];\n temperature?: number;\n maxTokens?: number;\n tools?: Array<Record<string, unknown>>;\n /** codai extension: plan-and-execute agent mode (Pro+). */\n agentMode?: boolean;\n /** codai extension: server-side context compaction ('auto'). */\n compact?: 'auto';\n /** codai extension: best-of-N override (0 disables, 3 forces). */\n bestOf?: 0 | 3;\n /** Attribution id recorded on tool calls (agent fleets). */\n agentId?: string;\n /** Per-call session override. */\n sessionId?: string;\n signal?: AbortSignal;\n}\n\nexport interface ChatResult {\n /** Assistant message content (first choice). */\n content: string;\n /** Raw OpenAI-shaped response. */\n raw: Record<string, unknown>;\n /** Request id — pass to feedback(). */\n requestId: string | null;\n /** Which upstream model actually served (x-codai-routed-to). */\n routedTo: string | null;\n /** Verification status for verified-execution responses ('passed'|'refined'|…), if present. */\n execVerify: string | null;\n usage: { promptTokens: number; completionTokens: number } | null;\n}\n\nexport interface AgentRunOptions {\n task: string;\n context?: string;\n system?: string;\n model?: string;\n signal?: AbortSignal;\n}\n\nexport interface AgentRunResult {\n result: string;\n model: string;\n eventId: string | null;\n usage: Record<string, unknown> | null;\n}\n\nexport type EphemeralScope = 'realtime' | 'audio' | 'embeddings';\n\nexport interface EphemeralToken {\n token: string;\n expiresAt: string;\n scope: EphemeralScope;\n}\n\nexport interface TranscriptionOptions {\n /** Audio file contents. */\n file: Blob | Uint8Array | ArrayBuffer;\n /** Filename hint (extension drives format detection). Default audio.webm */\n filename?: string;\n model?: string;\n signal?: AbortSignal;\n}\n\nexport interface SpeechOptions {\n input: string;\n model?: string;\n voice?: string;\n signal?: AbortSignal;\n}\n\nexport interface EmbeddingsOptions {\n input: string | string[];\n model?: string;\n dimensions?: number;\n signal?: AbortSignal;\n}\n\nexport class CodaiError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body: unknown,\n ) {\n super(message);\n this.name = 'CodaiError';\n }\n}\n\nconst RETRYABLE = new Set([429, 500, 502, 503, 504]);\n\nexport class Codai {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly sessionId?: string;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n\n constructor(opts: CodaiClientOptions) {\n if (!opts.apiKey) throw new Error('Codai: apiKey is required');\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? 'https://ai.codai.ro').replace(/\\/$/, '');\n this.sessionId = opts.sessionId;\n this.timeoutMs = opts.timeoutMs ?? 120_000;\n this.maxRetries = opts.maxRetries ?? 2;\n }\n\n // ---------------------------------------------------------------- chat\n async chat(opts: ChatOptions): Promise<ChatResult> {\n const res = await this.request('/v1/chat/completions', this.chatBody(opts, false), {\n signal: opts.signal,\n headers: this.chatHeaders(opts),\n });\n const raw = (await res.json()) as Record<string, unknown>;\n const choices = raw.choices as Array<{ message?: { content?: string } }> | undefined;\n const usage = raw.usage as { prompt_tokens?: number; completion_tokens?: number } | undefined;\n return {\n content: String(choices?.[0]?.message?.content ?? ''),\n raw,\n requestId: res.headers.get('x-request-id'),\n routedTo: res.headers.get('x-codai-routed-to'),\n execVerify: res.headers.get('x-codai-exec-verify'),\n usage: usage\n ? {\n promptTokens: usage.prompt_tokens ?? 0,\n completionTokens: usage.completion_tokens ?? 0,\n }\n : null,\n };\n }\n\n /** Streaming chat: async-iterate text deltas; `final` resolves afterwards. */\n async *chatStream(opts: ChatOptions): AsyncGenerator<string, void, undefined> {\n const res = await this.request('/v1/chat/completions', this.chatBody(opts, true), {\n signal: opts.signal,\n headers: this.chatHeaders(opts),\n });\n const reader = res.body?.getReader();\n if (!reader) throw new CodaiError('no response body', res.status, null);\n const decoder = new TextDecoder();\n let buffer = '';\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n // SSE frames are \\n\\n separated; keep the trailing partial in buffer.\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() ?? '';\n for (const frame of frames) {\n for (const line of frame.split('\\n')) {\n if (!line.startsWith('data:')) continue;\n const data = line.slice(5).trim();\n if (data === '[DONE]') return;\n try {\n const chunk = JSON.parse(data) as {\n choices?: Array<{ delta?: { content?: string } }>;\n };\n const delta = chunk.choices?.[0]?.delta?.content;\n if (delta) yield delta;\n } catch {\n /* keep-alives / comments */\n }\n }\n }\n }\n }\n\n // -------------------------------------------------------------- agents\n readonly agents = {\n run: async (opts: AgentRunOptions): Promise<AgentRunResult> => {\n const res = await this.request(\n '/v1/agents/run',\n {\n task: opts.task,\n ...(opts.context ? { context: opts.context } : {}),\n ...(opts.system ? { system: opts.system } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n },\n { signal: opts.signal },\n );\n const body = (await res.json()) as Record<string, unknown>;\n return {\n result: String(body.result ?? ''),\n model: String(body.model ?? ''),\n eventId: (body.event_id as string) ?? null,\n usage: (body.usage as Record<string, unknown>) ?? null,\n };\n },\n };\n\n // ------------------------------------------------------------ feedback\n /** Rate a completed request: +1 (helpful) or -1 (not helpful). */\n async feedback(requestId: string, rating: 1 | -1, comment?: string): Promise<void> {\n await this.request('/v1/feedback', {\n event_id: requestId,\n rating,\n ...(comment ? { comment } : {}),\n });\n }\n\n // -------------------------------------------------------------- models\n async models(): Promise<Array<{ id: string }>> {\n const res = await this.request('/v1/models', null, { method: 'GET' });\n const body = (await res.json()) as { data?: Array<{ id: string }> };\n return body.data ?? [];\n }\n\n // -------------------------------------------------------------- tokens\n /**\n * Mint a short-lived scoped token (60–3600s) for browser/webview clients.\n * The token authenticates only its scope's surface; chat is not mintable.\n */\n async mintToken(scope: EphemeralScope, ttlSeconds?: number): Promise<EphemeralToken> {\n const res = await this.request('/v1/tokens', {\n scope,\n ...(ttlSeconds !== undefined ? { ttl_seconds: ttlSeconds } : {}),\n });\n const body = (await res.json()) as {\n token: string;\n expires_at: string;\n scope: EphemeralScope;\n };\n return { token: body.token, expiresAt: body.expires_at, scope: body.scope };\n }\n\n // ---------------------------------------------------------- embeddings\n async embeddings(opts: EmbeddingsOptions): Promise<{ embeddings: number[][]; raw: unknown }> {\n const res = await this.request(\n '/v1/embeddings',\n {\n model: opts.model ?? 'codai-embed',\n input: opts.input,\n ...(opts.dimensions !== undefined ? { dimensions: opts.dimensions } : {}),\n },\n { signal: opts.signal },\n );\n const body = (await res.json()) as { data?: Array<{ embedding: number[] }> };\n return { embeddings: (body.data ?? []).map((d) => d.embedding), raw: body };\n }\n\n // --------------------------------------------------------------- audio\n readonly audio = {\n /** Speech-to-text (multipart upload, max 25 MB). Returns the transcript. */\n transcribe: async (opts: TranscriptionOptions): Promise<string> => {\n const form = new FormData();\n const blob = opts.file instanceof Blob ? opts.file : new Blob([opts.file as BlobPart]);\n form.append('file', blob, opts.filename ?? 'audio.webm');\n form.append('model', opts.model ?? 'codai-transcribe');\n const res = await fetch(`${this.baseUrl}/v1/audio/transcriptions`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.apiKey}` },\n body: form,\n signal: opts.signal ?? AbortSignal.timeout(this.timeoutMs),\n });\n if (!res.ok) {\n let errBody: unknown = null;\n try {\n errBody = await res.json();\n } catch {\n /* non-JSON */\n }\n throw new CodaiError(`codai /v1/audio/transcriptions → ${res.status}`, res.status, errBody);\n }\n const body = (await res.json()) as { text?: string };\n return body.text ?? '';\n },\n\n /** Text-to-speech. Returns the audio bytes (default mp3). */\n speech: async (opts: SpeechOptions): Promise<ArrayBuffer> => {\n const res = await this.request(\n '/v1/audio/speech',\n {\n model: opts.model ?? 'codai-tts',\n input: opts.input,\n voice: opts.voice ?? 'alloy',\n },\n { signal: opts.signal },\n );\n return res.arrayBuffer();\n },\n };\n\n // ------------------------------------------------------------ internal\n private chatBody(opts: ChatOptions, stream: boolean): Record<string, unknown> {\n return {\n model: opts.model ?? 'codai',\n messages: opts.messages,\n stream,\n ...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),\n ...(opts.maxTokens !== undefined ? { max_tokens: opts.maxTokens } : {}),\n ...(opts.tools ? { tools: opts.tools } : {}),\n };\n }\n\n private chatHeaders(opts: ChatOptions): Record<string, string> {\n const h: Record<string, string> = {};\n const session = opts.sessionId ?? this.sessionId;\n if (session) h['x-codai-session-id'] = session;\n if (opts.agentMode) h['x-codai-mode'] = 'agent';\n if (opts.compact) h['x-codai-compact'] = opts.compact;\n if (opts.bestOf !== undefined) h['x-codai-best-of'] = String(opts.bestOf);\n if (opts.agentId) h['x-codai-agent-id'] = opts.agentId;\n return h;\n }\n\n private async request(\n path: string,\n body: unknown,\n init: { signal?: AbortSignal; headers?: Record<string, string>; method?: string } = {},\n ): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n let lastErr: unknown;\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const res = await fetch(url, {\n method: init.method ?? 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n ...(body !== null ? { 'Content-Type': 'application/json' } : {}),\n ...init.headers,\n },\n ...(body !== null ? { body: JSON.stringify(body) } : {}),\n signal: init.signal ?? AbortSignal.timeout(this.timeoutMs),\n });\n if (res.ok) return res;\n if (RETRYABLE.has(res.status) && attempt < this.maxRetries) {\n await sleep(500 * 2 ** attempt + Math.random() * 250);\n continue;\n }\n let errBody: unknown = null;\n try {\n errBody = await res.json();\n } catch {\n /* non-JSON error */\n }\n throw new CodaiError(`codai ${path} → ${res.status}`, res.status, errBody);\n } catch (err) {\n if (err instanceof CodaiError) throw err;\n lastErr = err;\n if (attempt < this.maxRetries) {\n await sleep(500 * 2 ** attempt);\n continue;\n }\n }\n }\n throw new CodaiError(`codai ${path} failed after retries: ${String(lastErr)}`, 0, null);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nexport default Codai;\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * codai-sdk — official TypeScript client for the codai AI gateway.\n *\n * Full-surface client:\n * - chat(): OpenAI-compatible completions with codai extensions\n * (session, agent mode, compaction, best-of)\n * - chatStream(): SSE streaming — async-iterate text deltas, then await\n * `.final` for requestId / usage / routedTo / tool calls\n * - agents.run(): server-side agent loop (POST /v1/agents/run)\n * - feedback(): thumbs rating on a completed request\n * - models(): list models available to the key\n *\n * Zero dependencies; works in Node 18+ (global fetch) and modern edge\n * runtimes. Memory tools (memory_recall / memory_remember) and skills\n * (use_skill) are GATEWAY-NATIVE — the model invokes them server-side;\n * nothing to call from the SDK beyond enabling a session.\n */\n\nexport interface CodaiClientOptions {\n apiKey: string;\n /** Defaults to https://ai.codai.ro */\n baseUrl?: string;\n /** Stable conversation/session id — enables session memory + stickiness. */\n sessionId?: string;\n /** Default request timeout (ms). Default 120_000. */\n timeoutMs?: number;\n /** Retries on 429/5xx with exponential backoff. Default 2. */\n maxRetries?: number;\n}\n\nexport interface ChatMessage {\n role: 'system' | 'user' | 'assistant' | 'tool';\n content: string | Array<Record<string, unknown>>;\n tool_call_id?: string;\n name?: string;\n}\n\nexport interface ChatOptions {\n model?: string;\n messages: ChatMessage[];\n temperature?: number;\n maxTokens?: number;\n tools?: Array<Record<string, unknown>>;\n /** codai extension: plan-and-execute agent mode (Pro+). */\n agentMode?: boolean;\n /** codai extension: server-side context compaction ('auto'). */\n compact?: 'auto';\n /** codai extension: best-of-N override (0 disables, 3 forces). */\n bestOf?: 0 | 3;\n /** Attribution id recorded on tool calls (agent fleets). */\n agentId?: string;\n /** Per-call session override. */\n sessionId?: string;\n signal?: AbortSignal;\n}\n\nexport interface ChatResult {\n /** Assistant message content (first choice). */\n content: string;\n /** Raw OpenAI-shaped response. */\n raw: Record<string, unknown>;\n /** Request id — pass to feedback(). */\n requestId: string | null;\n /** Which upstream model actually served (x-codai-routed-to). */\n routedTo: string | null;\n /** Verification status for verified-execution responses ('passed'|'refined'|…), if present. */\n execVerify: string | null;\n usage: { promptTokens: number; completionTokens: number } | null;\n}\n\nexport interface TokenUsage {\n promptTokens: number;\n completionTokens: number;\n}\n\n/** Final metadata for a completed stream — resolved via `chatStream(...).final`. */\nexport interface ChatStreamResult {\n /** Full concatenated assistant text. */\n content: string;\n /** Tool calls assembled from streamed deltas (empty if none). */\n toolCalls: Array<Record<string, unknown>>;\n /** Request id — pass to feedback(). */\n requestId: string | null;\n /** Which upstream model actually served (x-codai-routed-to). */\n routedTo: string | null;\n /** Verification status for verified-execution responses, if present. */\n execVerify: string | null;\n /** Token usage if the gateway sent a final usage frame. */\n usage: TokenUsage | null;\n}\n\n/**\n * A live chat stream. Async-iterate it to get text deltas, and await `.final`\n * afterwards for metadata (requestId, usage, routedTo, tool calls).\n *\n * const stream = codai.chatStream({ messages });\n * for await (const delta of stream) process.stdout.write(delta);\n * const { requestId, usage } = await stream.final;\n */\nexport interface ChatStream extends AsyncIterable<string> {\n /** Resolves once the stream completes (or rejects if it errors). */\n readonly final: Promise<ChatStreamResult>;\n}\n\nexport interface AgentRunOptions {\n task: string;\n context?: string;\n system?: string;\n model?: string;\n signal?: AbortSignal;\n}\n\nexport interface AgentRunResult {\n result: string;\n model: string;\n eventId: string | null;\n usage: Record<string, unknown> | null;\n}\n\nexport type EphemeralScope = 'realtime' | 'audio' | 'embeddings';\n\nexport interface EphemeralToken {\n token: string;\n expiresAt: string;\n scope: EphemeralScope;\n}\n\nexport interface TranscriptionOptions {\n /** Audio file contents. */\n file: Blob | Uint8Array | ArrayBuffer;\n /** Filename hint (extension drives format detection). Default audio.webm */\n filename?: string;\n model?: string;\n signal?: AbortSignal;\n}\n\nexport interface SpeechOptions {\n input: string;\n model?: string;\n voice?: string;\n signal?: AbortSignal;\n}\n\nexport interface EmbeddingsOptions {\n input: string | string[];\n model?: string;\n dimensions?: number;\n signal?: AbortSignal;\n}\n\nexport class CodaiError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body: unknown,\n ) {\n super(message);\n this.name = 'CodaiError';\n }\n}\n\nconst RETRYABLE = new Set([429, 500, 502, 503, 504]);\n\n/**\n * Merge a streamed tool-call fragment into the accumulator at `index`.\n * OpenAI streams tool calls piecewise: id/name arrive first, then the\n * `arguments` string is appended across many deltas.\n */\nfunction mergeToolCall(\n acc: Map<number, Record<string, unknown>>,\n index: number,\n fragment: Record<string, unknown>,\n): void {\n const existing = acc.get(index) ?? { index };\n for (const [key, val] of Object.entries(fragment)) {\n if (key === 'index') continue;\n if (key === 'function' && val && typeof val === 'object') {\n const fn = (existing.function as Record<string, unknown>) ?? {};\n for (const [fk, fv] of Object.entries(val as Record<string, unknown>)) {\n if (fk === 'arguments') {\n fn.arguments = String(fn.arguments ?? '') + String(fv ?? '');\n } else {\n fn[fk] = fv;\n }\n }\n existing.function = fn;\n } else {\n existing[key] = val;\n }\n }\n acc.set(index, existing);\n}\n\nexport class Codai {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly sessionId?: string;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n\n constructor(opts: CodaiClientOptions) {\n if (!opts.apiKey) throw new Error('Codai: apiKey is required');\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? 'https://ai.codai.ro').replace(/\\/$/, '');\n this.sessionId = opts.sessionId;\n this.timeoutMs = opts.timeoutMs ?? 120_000;\n this.maxRetries = opts.maxRetries ?? 2;\n }\n\n // ---------------------------------------------------------------- chat\n async chat(opts: ChatOptions): Promise<ChatResult> {\n const res = await this.request('/v1/chat/completions', this.chatBody(opts, false), {\n signal: opts.signal,\n headers: this.chatHeaders(opts),\n });\n const raw = (await res.json()) as Record<string, unknown>;\n const choices = raw.choices as Array<{ message?: { content?: string } }> | undefined;\n const usage = raw.usage as { prompt_tokens?: number; completion_tokens?: number } | undefined;\n return {\n content: String(choices?.[0]?.message?.content ?? ''),\n raw,\n requestId: res.headers.get('x-request-id'),\n routedTo: res.headers.get('x-codai-routed-to'),\n execVerify: res.headers.get('x-codai-exec-verify'),\n usage: usage\n ? {\n promptTokens: usage.prompt_tokens ?? 0,\n completionTokens: usage.completion_tokens ?? 0,\n }\n : null,\n };\n }\n\n /**\n * Streaming chat. Async-iterate the returned object for text deltas, then\n * `await stream.final` for metadata (requestId, usage, routedTo, toolCalls).\n */\n chatStream(opts: ChatOptions): ChatStream {\n const requestPromise = this.request('/v1/chat/completions', this.chatBody(opts, true), {\n signal: opts.signal,\n headers: this.chatHeaders(opts),\n });\n\n let resolveFinal!: (r: ChatStreamResult) => void;\n let rejectFinal!: (e: unknown) => void;\n const final = new Promise<ChatStreamResult>((resolve, reject) => {\n resolveFinal = resolve;\n rejectFinal = reject;\n });\n // Avoid unhandled-rejection noise if the caller never awaits `final`.\n final.catch(() => {});\n\n // Accumulate tool-call fragments by index (OpenAI streams them piecewise).\n const toolCallsByIndex = new Map<number, Record<string, unknown>>();\n\n async function* iterate(this: Codai): AsyncGenerator<string, void, undefined> {\n let content = '';\n let usage: TokenUsage | null = null;\n let res: Response;\n try {\n res = await requestPromise;\n } catch (err) {\n rejectFinal(err);\n throw err;\n }\n\n const reader = res.body?.getReader();\n if (!reader) {\n const e = new CodaiError('no response body', res.status, null);\n rejectFinal(e);\n throw e;\n }\n const decoder = new TextDecoder();\n let buffer = '';\n let finished = false;\n\n const buildResult = (): ChatStreamResult => ({\n content,\n toolCalls: [...toolCallsByIndex.values()],\n requestId: res.headers.get('x-request-id'),\n routedTo: res.headers.get('x-codai-routed-to'),\n execVerify: res.headers.get('x-codai-exec-verify'),\n usage,\n });\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n // SSE frames are \\n\\n separated; keep the trailing partial in buffer.\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() ?? '';\n for (const frame of frames) {\n for (const line of frame.split('\\n')) {\n if (!line.startsWith('data:')) continue;\n const data = line.slice(5).trim();\n if (data === '[DONE]') {\n finished = true;\n break;\n }\n let chunk: {\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n choices?: Array<{\n delta?: {\n content?: string;\n tool_calls?: Array<{ index?: number } & Record<string, unknown>>;\n };\n }>;\n };\n try {\n chunk = JSON.parse(data);\n } catch {\n continue; // keep-alives / comments\n }\n if (chunk.usage) {\n usage = {\n promptTokens: chunk.usage.prompt_tokens ?? 0,\n completionTokens: chunk.usage.completion_tokens ?? 0,\n };\n }\n const delta = chunk.choices?.[0]?.delta;\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = typeof tc.index === 'number' ? tc.index : 0;\n mergeToolCall(toolCallsByIndex, idx, tc);\n }\n }\n if (delta?.content) {\n content += delta.content;\n yield delta.content;\n }\n }\n if (finished) break;\n }\n if (finished) break;\n }\n resolveFinal(buildResult());\n } catch (err) {\n rejectFinal(err);\n throw err;\n } finally {\n reader.releaseLock?.();\n }\n }\n\n const gen = iterate.call(this);\n return {\n [Symbol.asyncIterator]: () => gen,\n final,\n };\n }\n\n // -------------------------------------------------------------- agents\n readonly agents = {\n run: async (opts: AgentRunOptions): Promise<AgentRunResult> => {\n const res = await this.request(\n '/v1/agents/run',\n {\n task: opts.task,\n ...(opts.context ? { context: opts.context } : {}),\n ...(opts.system ? { system: opts.system } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n },\n { signal: opts.signal },\n );\n const body = (await res.json()) as Record<string, unknown>;\n return {\n result: String(body.result ?? ''),\n model: String(body.model ?? ''),\n eventId: (body.event_id as string) ?? null,\n usage: (body.usage as Record<string, unknown>) ?? null,\n };\n },\n };\n\n // ------------------------------------------------------------ feedback\n /** Rate a completed request: +1 (helpful) or -1 (not helpful). */\n async feedback(requestId: string, rating: 1 | -1, comment?: string): Promise<void> {\n await this.request('/v1/feedback', {\n event_id: requestId,\n rating,\n ...(comment ? { comment } : {}),\n });\n }\n\n // -------------------------------------------------------------- models\n async models(): Promise<Array<{ id: string }>> {\n const res = await this.request('/v1/models', null, { method: 'GET' });\n const body = (await res.json()) as { data?: Array<{ id: string }> };\n return body.data ?? [];\n }\n\n // -------------------------------------------------------------- tokens\n /**\n * Mint a short-lived scoped token (60–3600s) for browser/webview clients.\n * The token authenticates only its scope's surface; chat is not mintable.\n */\n async mintToken(scope: EphemeralScope, ttlSeconds?: number): Promise<EphemeralToken> {\n const res = await this.request('/v1/tokens', {\n scope,\n ...(ttlSeconds !== undefined ? { ttl_seconds: ttlSeconds } : {}),\n });\n const body = (await res.json()) as {\n token: string;\n expires_at: string;\n scope: EphemeralScope;\n };\n return { token: body.token, expiresAt: body.expires_at, scope: body.scope };\n }\n\n // ---------------------------------------------------------- embeddings\n async embeddings(opts: EmbeddingsOptions): Promise<{ embeddings: number[][]; raw: unknown }> {\n const res = await this.request(\n '/v1/embeddings',\n {\n model: opts.model ?? 'codai-embed',\n input: opts.input,\n ...(opts.dimensions !== undefined ? { dimensions: opts.dimensions } : {}),\n },\n { signal: opts.signal },\n );\n const body = (await res.json()) as { data?: Array<{ embedding: number[] }> };\n return { embeddings: (body.data ?? []).map((d) => d.embedding), raw: body };\n }\n\n // --------------------------------------------------------------- audio\n readonly audio = {\n /** Speech-to-text (multipart upload, max 25 MB). Returns the transcript. */\n transcribe: async (opts: TranscriptionOptions): Promise<string> => {\n const form = new FormData();\n const blob = opts.file instanceof Blob ? opts.file : new Blob([opts.file as BlobPart]);\n form.append('file', blob, opts.filename ?? 'audio.webm');\n form.append('model', opts.model ?? 'codai-transcribe');\n const res = await fetch(`${this.baseUrl}/v1/audio/transcriptions`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.apiKey}` },\n body: form,\n signal: opts.signal ?? AbortSignal.timeout(this.timeoutMs),\n });\n if (!res.ok) {\n let errBody: unknown = null;\n try {\n errBody = await res.json();\n } catch {\n /* non-JSON */\n }\n throw new CodaiError(`codai /v1/audio/transcriptions → ${res.status}`, res.status, errBody);\n }\n const body = (await res.json()) as { text?: string };\n return body.text ?? '';\n },\n\n /** Text-to-speech. Returns the audio bytes (default mp3). */\n speech: async (opts: SpeechOptions): Promise<ArrayBuffer> => {\n const res = await this.request(\n '/v1/audio/speech',\n {\n model: opts.model ?? 'codai-tts',\n input: opts.input,\n voice: opts.voice ?? 'alloy',\n },\n { signal: opts.signal },\n );\n return res.arrayBuffer();\n },\n };\n\n // ------------------------------------------------------------ internal\n private chatBody(opts: ChatOptions, stream: boolean): Record<string, unknown> {\n return {\n model: opts.model ?? 'codai',\n messages: opts.messages,\n stream,\n ...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),\n ...(opts.maxTokens !== undefined ? { max_tokens: opts.maxTokens } : {}),\n ...(opts.tools ? { tools: opts.tools } : {}),\n };\n }\n\n private chatHeaders(opts: ChatOptions): Record<string, string> {\n const h: Record<string, string> = {};\n const session = opts.sessionId ?? this.sessionId;\n if (session) h['x-codai-session-id'] = session;\n if (opts.agentMode) h['x-codai-mode'] = 'agent';\n if (opts.compact) h['x-codai-compact'] = opts.compact;\n if (opts.bestOf !== undefined) h['x-codai-best-of'] = String(opts.bestOf);\n if (opts.agentId) h['x-codai-agent-id'] = opts.agentId;\n return h;\n }\n\n private async request(\n path: string,\n body: unknown,\n init: { signal?: AbortSignal; headers?: Record<string, string>; method?: string } = {},\n ): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n let lastErr: unknown;\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const res = await fetch(url, {\n method: init.method ?? 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n ...(body !== null ? { 'Content-Type': 'application/json' } : {}),\n ...init.headers,\n },\n ...(body !== null ? { body: JSON.stringify(body) } : {}),\n signal: init.signal ?? AbortSignal.timeout(this.timeoutMs),\n });\n if (res.ok) return res;\n if (RETRYABLE.has(res.status) && attempt < this.maxRetries) {\n await sleep(500 * 2 ** attempt + Math.random() * 250);\n continue;\n }\n let errBody: unknown = null;\n try {\n errBody = await res.json();\n } catch {\n /* non-JSON error */\n }\n throw new CodaiError(`codai ${path} → ${res.status}`, res.status, errBody);\n } catch (err) {\n if (err instanceof CodaiError) throw err;\n lastErr = err;\n if (attempt < this.maxRetries) {\n await sleep(500 * 2 ** attempt);\n continue;\n }\n }\n }\n throw new CodaiError(`codai ${path} failed after retries: ${String(lastErr)}`, 0, null);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nexport default Codai;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codai-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Official TypeScript SDK for the codai AI gateway - chat, streaming, agents, feedback, models.",
5
5
  "type": "module",
6
6
  "license": "MIT",