@inferencesh/sdk 0.4.8 → 0.4.13

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/dist/client.d.ts CHANGED
@@ -1,16 +1,13 @@
1
1
  import { ApiAppRunRequest, TaskDTO as Task, File, ChatDTO, ChatMessageDTO, AgentConfig } from './types';
2
2
  import { EventSource } from 'eventsource';
3
- /**
4
- * Ad-hoc agent configuration - extends AgentConfig with core_app_ref required
5
- * Uses Partial to make fields optional for ad-hoc usage
6
- */
7
- export type AdHocAgentConfig = Partial<AgentConfig> & {
8
- /** Core LLM app ref: namespace/name@shortid (required for ad-hoc agents) */
9
- core_app_ref: string;
10
- };
3
+ /** Options for creating an agent */
4
+ export interface AgentOptions {
5
+ /** Optional name for the adhoc agent (used for deduplication and display) */
6
+ name?: string;
7
+ }
11
8
  export interface SendMessageOptions {
12
- /** File attachments (Blob or base64 data URI) */
13
- files?: (Blob | string)[];
9
+ /** File attachments - Blob (will be uploaded) or FileDTO (already uploaded, has uri) */
10
+ files?: (Blob | File)[];
14
11
  /** Callback for message updates */
15
12
  onMessage?: (message: ChatMessageDTO) => void;
16
13
  /** Callback for chat updates */
@@ -137,11 +134,17 @@ export declare class Inference {
137
134
  * tools: [...]
138
135
  * })
139
136
  *
137
+ * // Ad-hoc agent with name for grouping
138
+ * const agent = client.agent(
139
+ * { core_app_ref: 'infsh/claude-sonnet-4@xyz789' },
140
+ * { name: 'My Assistant' }
141
+ * )
142
+ *
140
143
  * // Send messages
141
144
  * const response = await agent.sendMessage('Hello!')
142
145
  * ```
143
146
  */
144
- agent(config: string | AdHocAgentConfig): Agent;
147
+ agent(config: string | AgentConfig, options?: AgentOptions): Agent;
145
148
  }
146
149
  /**
147
150
  * Agent for chat interactions
@@ -151,15 +154,19 @@ export declare class Inference {
151
154
  export declare class Agent {
152
155
  private readonly client;
153
156
  private readonly config;
157
+ private readonly agentName;
154
158
  private chatId;
155
159
  private stream;
156
160
  private dispatchedToolCalls;
157
161
  /** @internal */
158
- constructor(client: Inference, config: string | AdHocAgentConfig);
162
+ constructor(client: Inference, config: string | AgentConfig, options?: AgentOptions);
159
163
  /** Get current chat ID */
160
164
  get currentChatId(): string | null;
161
165
  /** Send a message to the agent */
162
- sendMessage(text: string, options?: SendMessageOptions): Promise<ChatMessageDTO>;
166
+ sendMessage(text: string, options?: SendMessageOptions): Promise<{
167
+ userMessage: ChatMessageDTO;
168
+ assistantMessage: ChatMessageDTO;
169
+ }>;
163
170
  /** Get chat by ID */
164
171
  getChat(chatId?: string): Promise<ChatDTO | null>;
165
172
  /** Stop the current chat generation */
@@ -180,6 +187,24 @@ export declare class Agent {
180
187
  disconnect(): void;
181
188
  /** Reset the agent (start fresh chat) */
182
189
  reset(): void;
190
+ /**
191
+ * Start streaming for the current chat.
192
+ * Call this after sendMessage to receive real-time updates.
193
+ * This is useful when you want to manage streaming separately from sendMessage.
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * // Send message without waiting for streaming
198
+ * const { userMessage, assistantMessage } = await agent.sendMessage('hello');
199
+ *
200
+ * // Start streaming separately
201
+ * agent.startStreaming({
202
+ * onMessage: (msg) => console.log(msg.content),
203
+ * onChat: (chat) => console.log(chat.status),
204
+ * });
205
+ * ```
206
+ */
207
+ startStreaming(options?: Omit<SendMessageOptions, 'files'>): void;
183
208
  /** Stream events until chat becomes idle */
184
209
  private streamUntilIdle;
185
210
  }
package/dist/client.js CHANGED
@@ -109,14 +109,24 @@ class Inference {
109
109
  _createEventSource(endpoint) {
110
110
  const targetUrl = new URL(`${this.baseUrl}${endpoint}`);
111
111
  const isProxyMode = !!this.proxyUrl;
112
- const fetchUrl = isProxyMode ? this.proxyUrl : targetUrl.toString();
112
+ // For proxy mode: Browser EventSource can't send custom headers,
113
+ // so append target URL as query param instead
114
+ let fetchUrl;
115
+ if (isProxyMode) {
116
+ const proxyUrlWithQuery = new URL(this.proxyUrl, window?.location?.origin || 'http://localhost');
117
+ proxyUrlWithQuery.searchParams.set('__inf_target', targetUrl.toString());
118
+ fetchUrl = proxyUrlWithQuery.toString();
119
+ }
120
+ else {
121
+ fetchUrl = targetUrl.toString();
122
+ }
113
123
  return new eventsource_1.EventSource(fetchUrl, {
114
124
  fetch: (input, init) => {
115
125
  const headers = {
116
126
  ...init?.headers,
117
127
  };
118
128
  if (isProxyMode) {
119
- // Proxy mode: send target URL as header
129
+ // Proxy mode: also send target URL as header (for non-browser clients)
120
130
  headers["x-inf-target-url"] = targetUrl.toString();
121
131
  }
122
132
  else {
@@ -246,10 +256,21 @@ class Inference {
246
256
  }
247
257
  },
248
258
  onPartialData: (data, fields) => {
249
- // Call onPartialUpdate if provided
250
- if (onPartialUpdate) {
251
- const stripped = this._stripTask(data);
252
- onPartialUpdate(stripped, fields);
259
+ // Strip and send partial update if callback provided
260
+ const stripped = this._stripTask(data);
261
+ onPartialUpdate?.(stripped, fields);
262
+ // Also check for status changes in partial updates
263
+ if (data.status === types_1.TaskStatusCompleted) {
264
+ streamManager.stop();
265
+ resolve(stripped);
266
+ }
267
+ else if (data.status === types_1.TaskStatusFailed) {
268
+ streamManager.stop();
269
+ reject(new Error(data.error || "task failed"));
270
+ }
271
+ else if (data.status === types_1.TaskStatusCancelled) {
272
+ streamManager.stop();
273
+ reject(new Error("task cancelled"));
253
274
  }
254
275
  },
255
276
  onError: (error) => {
@@ -346,12 +367,18 @@ class Inference {
346
367
  * tools: [...]
347
368
  * })
348
369
  *
370
+ * // Ad-hoc agent with name for grouping
371
+ * const agent = client.agent(
372
+ * { core_app_ref: 'infsh/claude-sonnet-4@xyz789' },
373
+ * { name: 'My Assistant' }
374
+ * )
375
+ *
349
376
  * // Send messages
350
377
  * const response = await agent.sendMessage('Hello!')
351
378
  * ```
352
379
  */
353
- agent(config) {
354
- return new Agent(this, config);
380
+ agent(config, options) {
381
+ return new Agent(this, config, options);
355
382
  }
356
383
  }
357
384
  exports.Inference = Inference;
@@ -365,12 +392,13 @@ exports.Inference = Inference;
365
392
  */
366
393
  class Agent {
367
394
  /** @internal */
368
- constructor(client, config) {
395
+ constructor(client, config, options) {
369
396
  this.chatId = null;
370
397
  this.stream = null;
371
398
  this.dispatchedToolCalls = new Set();
372
399
  this.client = client;
373
400
  this.config = config;
401
+ this.agentName = options?.name;
374
402
  }
375
403
  /** Get current chat ID */
376
404
  get currentChatId() {
@@ -379,40 +407,69 @@ class Agent {
379
407
  /** Send a message to the agent */
380
408
  async sendMessage(text, options = {}) {
381
409
  const isTemplate = typeof this.config === 'string';
382
- // Upload files if provided
410
+ const hasCallbacks = !!(options.onMessage || options.onChat || options.onToolCall);
411
+ // Process files - either already uploaded (FileDTO with uri) or needs upload (Blob)
383
412
  let imageUri;
384
413
  let fileUris;
385
414
  if (options.files && options.files.length > 0) {
386
- const uploadedFiles = await Promise.all(options.files.map(f => this.client.uploadFile(f)));
387
- const images = uploadedFiles.filter(f => f.content_type?.startsWith('image/'));
388
- const others = uploadedFiles.filter(f => !f.content_type?.startsWith('image/'));
415
+ // Separate files that need uploading from those already uploaded
416
+ const toUpload = [];
417
+ const alreadyUploaded = [];
418
+ for (const file of options.files) {
419
+ // FileDTO has a uri property, Blob does not
420
+ if ('uri' in file && typeof file.uri === 'string') {
421
+ alreadyUploaded.push(file);
422
+ }
423
+ else {
424
+ toUpload.push(file);
425
+ }
426
+ }
427
+ // Upload any Blobs that need uploading
428
+ const uploadedFiles = toUpload.length > 0
429
+ ? await Promise.all(toUpload.map((blob) => this.client.uploadFile(blob)))
430
+ : [];
431
+ // Combine all files (already uploaded + newly uploaded)
432
+ const allFiles = [...alreadyUploaded, ...uploadedFiles];
433
+ // Separate images from other files
434
+ const images = allFiles.filter((f) => f.content_type?.startsWith('image/'));
435
+ const others = allFiles.filter((f) => !f.content_type?.startsWith('image/'));
389
436
  if (images.length > 0)
390
437
  imageUri = images[0].uri;
391
438
  if (others.length > 0)
392
- fileUris = others.map(f => f.uri);
439
+ fileUris = others.map((f) => f.uri);
393
440
  }
394
441
  const body = isTemplate
395
442
  ? {
396
443
  chat_id: this.chatId,
397
- agent_ref: this.config,
444
+ agent: this.config,
398
445
  input: { text, image: imageUri, files: fileUris, role: 'user', context: [], system_prompt: '', context_size: 0 },
399
446
  }
400
447
  : {
401
448
  chat_id: this.chatId,
402
449
  agent_config: this.config,
450
+ agent_name: this.agentName,
403
451
  input: { text, image: imageUri, files: fileUris, role: 'user', context: [], system_prompt: '', context_size: 0 },
404
452
  };
453
+ // For existing chats with callbacks: Start streaming BEFORE POST so we don't miss updates
454
+ let streamPromise = null;
455
+ if (this.chatId && hasCallbacks) {
456
+ streamPromise = this.streamUntilIdle(options);
457
+ }
458
+ // Make the POST request
405
459
  const response = await this.client._request('post', '/agents/run', { data: body });
406
- // Start streaming for new chats or continue existing stream
460
+ // For new chats: Set chatId and start streaming immediately after POST
407
461
  const isNewChat = !this.chatId && response.assistant_message.chat_id;
408
462
  if (isNewChat) {
409
463
  this.chatId = response.assistant_message.chat_id;
464
+ if (hasCallbacks) {
465
+ streamPromise = this.streamUntilIdle(options);
466
+ }
410
467
  }
411
- // Wait for streaming to complete if callbacks are provided
412
- if (options.onMessage || options.onChat || options.onToolCall) {
413
- await this.streamUntilIdle(options);
468
+ // Wait for streaming to complete
469
+ if (streamPromise) {
470
+ await streamPromise;
414
471
  }
415
- return response.assistant_message;
472
+ return { userMessage: response.user_message, assistantMessage: response.assistant_message };
416
473
  }
417
474
  /** Get chat by ID */
418
475
  async getChat(chatId) {
@@ -450,6 +507,28 @@ class Agent {
450
507
  this.chatId = null;
451
508
  this.dispatchedToolCalls.clear();
452
509
  }
510
+ /**
511
+ * Start streaming for the current chat.
512
+ * Call this after sendMessage to receive real-time updates.
513
+ * This is useful when you want to manage streaming separately from sendMessage.
514
+ *
515
+ * @example
516
+ * ```typescript
517
+ * // Send message without waiting for streaming
518
+ * const { userMessage, assistantMessage } = await agent.sendMessage('hello');
519
+ *
520
+ * // Start streaming separately
521
+ * agent.startStreaming({
522
+ * onMessage: (msg) => console.log(msg.content),
523
+ * onChat: (chat) => console.log(chat.status),
524
+ * });
525
+ * ```
526
+ */
527
+ startStreaming(options = {}) {
528
+ if (!this.chatId)
529
+ return;
530
+ this.streamUntilIdle(options);
531
+ }
453
532
  /** Stream events until chat becomes idle */
454
533
  streamUntilIdle(options) {
455
534
  if (!this.chatId)
@@ -461,6 +540,7 @@ class Agent {
461
540
  createEventSource: async () => this.client._createEventSource(`/chats/${this.chatId}/stream`),
462
541
  autoReconnect: true,
463
542
  });
543
+ // Listen for Chat object updates (status changes)
464
544
  this.stream.addEventListener('chats', (chat) => {
465
545
  options.onChat?.(chat);
466
546
  // Resolve when chat becomes idle (generation complete)
@@ -468,6 +548,7 @@ class Agent {
468
548
  resolve();
469
549
  }
470
550
  });
551
+ // Listen for ChatMessage updates
471
552
  this.stream.addEventListener('chat_messages', (message) => {
472
553
  options.onMessage?.(message);
473
554
  if (message.tool_invocations && options.onToolCall) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Inference, inference, InferenceConfig, RunOptions, UploadFileOptions, Agent, AdHocAgentConfig, SendMessageOptions, } from './client';
1
+ export { Inference, inference, InferenceConfig, RunOptions, UploadFileOptions, Agent, AgentOptions, SendMessageOptions, } from './client';
2
2
  export { tool, appTool, agentTool, webhookTool, internalTools, string, number, integer, boolean, enumOf, object, array, optional } from './tool-builder';
3
3
  export { StreamManager, PartialDataWrapper } from './stream';
4
4
  export { InferenceError, RequirementsNotMetException } from './errors';
@@ -1,37 +1,30 @@
1
1
  /**
2
- * Express.js proxy handler for Inference.sh API
2
+ * @inferencesh/sdk/proxy/express - Express.js Proxy Handler
3
3
  *
4
4
  * @example
5
5
  * ```typescript
6
6
  * import express from "express";
7
- * import * as inferenceProxy from "@inferencesh/sdk/proxy/express";
7
+ * import { createHandler, PROXY_ROUTE } from "@inferencesh/sdk/proxy/express";
8
8
  *
9
9
  * const app = express();
10
10
  * app.use(express.json());
11
- * app.all(inferenceProxy.route, inferenceProxy.handler);
11
+ * app.all(PROXY_ROUTE, createHandler());
12
12
  * ```
13
13
  */
14
14
  import type { RequestHandler } from "express";
15
- /**
16
- * The default proxy route path.
17
- */
15
+ /** Default proxy route path */
16
+ export declare const PROXY_ROUTE = "/api/inference/proxy";
17
+ /** @deprecated Use PROXY_ROUTE */
18
18
  export declare const route = "/api/inference/proxy";
19
+ export interface ExpressProxyOptions {
20
+ /** Custom API key (overrides INFERENCE_API_KEY env var) */
21
+ apiKey?: string;
22
+ }
19
23
  /**
20
- * Express middleware handler for the Inference.sh proxy.
24
+ * Creates an Express middleware handler for the Inference.sh proxy.
21
25
  *
22
26
  * Requires `express.json()` middleware to be applied before this handler.
23
- *
24
- * @example
25
- * ```typescript
26
- * import express from "express";
27
- * import cors from "cors";
28
- * import * as inferenceProxy from "@inferencesh/sdk/proxy/express";
29
- *
30
- * const app = express();
31
- * app.use(express.json());
32
- *
33
- * // If clients are external, enable CORS
34
- * app.all(inferenceProxy.route, cors(), inferenceProxy.handler);
35
- * ```
36
27
  */
37
- export declare const handler: RequestHandler;
28
+ export declare function createHandler(options?: ExpressProxyOptions): RequestHandler;
29
+ /** @deprecated Use createHandler() */
30
+ export declare const handler: RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
@@ -1,84 +1,71 @@
1
1
  "use strict";
2
2
  /**
3
- * Express.js proxy handler for Inference.sh API
3
+ * @inferencesh/sdk/proxy/express - Express.js Proxy Handler
4
4
  *
5
5
  * @example
6
6
  * ```typescript
7
7
  * import express from "express";
8
- * import * as inferenceProxy from "@inferencesh/sdk/proxy/express";
8
+ * import { createHandler, PROXY_ROUTE } from "@inferencesh/sdk/proxy/express";
9
9
  *
10
10
  * const app = express();
11
11
  * app.use(express.json());
12
- * app.all(inferenceProxy.route, inferenceProxy.handler);
12
+ * app.all(PROXY_ROUTE, createHandler());
13
13
  * ```
14
14
  */
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.handler = exports.route = void 0;
16
+ exports.handler = exports.route = exports.PROXY_ROUTE = void 0;
17
+ exports.createHandler = createHandler;
17
18
  const index_1 = require("./index");
19
+ /** Default proxy route path */
20
+ exports.PROXY_ROUTE = index_1.PROXY_PATH;
21
+ /** @deprecated Use PROXY_ROUTE */
22
+ exports.route = exports.PROXY_ROUTE;
18
23
  /**
19
- * The default proxy route path.
20
- */
21
- exports.route = index_1.DEFAULT_PROXY_ROUTE;
22
- /**
23
- * Express middleware handler for the Inference.sh proxy.
24
+ * Creates an Express middleware handler for the Inference.sh proxy.
24
25
  *
25
26
  * Requires `express.json()` middleware to be applied before this handler.
26
- *
27
- * @example
28
- * ```typescript
29
- * import express from "express";
30
- * import cors from "cors";
31
- * import * as inferenceProxy from "@inferencesh/sdk/proxy/express";
32
- *
33
- * const app = express();
34
- * app.use(express.json());
35
- *
36
- * // If clients are external, enable CORS
37
- * app.all(inferenceProxy.route, cors(), inferenceProxy.handler);
38
- * ```
39
27
  */
40
- const handler = async (req, res, _next) => {
41
- const responseHeaders = {};
42
- return (0, index_1.handleRequest)({
43
- id: "express",
44
- method: req.method,
45
- getRequestBody: async () => JSON.stringify(req.body),
46
- getHeaders: () => req.headers,
47
- getHeader: (name) => req.headers[name],
48
- sendHeader: (name, value) => {
49
- responseHeaders[name] = value;
50
- res.setHeader(name, value);
51
- },
52
- respondWith: (status, data) => {
53
- return res.status(status).json(data);
54
- },
55
- sendResponse: async (response) => {
56
- // Copy status
57
- res.status(response.status);
58
- // Handle streaming responses
59
- const contentType = response.headers.get("content-type");
60
- if (contentType?.includes("text/event-stream") ||
61
- contentType?.includes("application/octet-stream")) {
62
- // Stream the response
63
- if (response.body) {
64
- const reader = response.body.getReader();
65
- while (true) {
66
- const { done, value } = await reader.read();
67
- if (done)
68
- break;
69
- res.write(value);
28
+ function createHandler(options) {
29
+ return async (req, res, _next) => {
30
+ return (0, index_1.processProxyRequest)({
31
+ framework: "express",
32
+ method: req.method,
33
+ body: async () => JSON.stringify(req.body),
34
+ headers: () => req.headers,
35
+ header: (name) => req.headers[name],
36
+ query: (name) => {
37
+ const v = req.query[name];
38
+ return typeof v === "string" ? v : undefined;
39
+ },
40
+ setHeader: (name, value) => res.setHeader(name, value),
41
+ error: (status, data) => res.status(status).json(data),
42
+ respond: async (response) => {
43
+ res.status(response.status);
44
+ // Handle streaming responses
45
+ const contentType = response.headers.get("content-type");
46
+ if (contentType?.includes("text/event-stream") ||
47
+ contentType?.includes("application/octet-stream")) {
48
+ if (response.body) {
49
+ const reader = response.body.getReader();
50
+ while (true) {
51
+ const { done, value } = await reader.read();
52
+ if (done)
53
+ break;
54
+ res.write(value);
55
+ }
70
56
  }
57
+ res.end();
58
+ return res;
59
+ }
60
+ // Handle JSON responses
61
+ if (contentType?.includes("application/json")) {
62
+ return res.json(await response.json());
71
63
  }
72
- res.end();
73
- return res;
74
- }
75
- // Handle JSON responses
76
- if (contentType?.includes("application/json")) {
77
- return res.json(await response.json());
78
- }
79
- // Handle text responses
80
- return res.send(await response.text());
81
- },
82
- });
83
- };
84
- exports.handler = handler;
64
+ // Handle text responses
65
+ return res.send(await response.text());
66
+ },
67
+ }, options);
68
+ };
69
+ }
70
+ /** @deprecated Use createHandler() */
71
+ exports.handler = createHandler();
@@ -1,59 +1,27 @@
1
1
  /**
2
- * Hono proxy handler for Inference.sh API
2
+ * @inferencesh/sdk/proxy/hono - Hono Framework Proxy Handler
3
3
  *
4
- * Lightweight handler for Hono framework, ideal for edge/serverless deployments.
4
+ * Lightweight handler for Hono, ideal for edge/serverless deployments.
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
8
  * import { Hono } from "hono";
9
- * import { createRouteHandler } from "@inferencesh/sdk/proxy/hono";
9
+ * import { createHandler } from "@inferencesh/sdk/proxy/hono";
10
10
  *
11
11
  * const app = new Hono();
12
- * const proxyHandler = createRouteHandler();
13
- *
14
- * app.all("/api/inference/proxy", proxyHandler);
12
+ * app.all("/api/inference/proxy", createHandler());
15
13
  * ```
16
14
  */
17
15
  import type { Context } from "hono";
18
16
  export interface HonoProxyOptions {
19
- /**
20
- * Custom function to resolve the API key.
21
- * Defaults to reading from INFERENCE_API_KEY environment variable.
22
- */
17
+ /** Custom function to resolve the API key */
23
18
  resolveApiKey?: () => Promise<string | undefined>;
24
19
  }
25
- type RouteHandler = (context: Context) => Promise<Response>;
20
+ type HonoHandler = (context: Context) => Promise<Response>;
26
21
  /**
27
- * Creates a Hono route handler that proxies requests to the Inference.sh API.
28
- *
29
- * This is a drop-in handler for Hono applications that keeps API keys safe
30
- * by running on your server.
31
- *
32
- * @param options - Proxy options
33
- * @returns A Hono route handler function
34
- *
35
- * @example
36
- * ```typescript
37
- * import { Hono } from "hono";
38
- * import { createRouteHandler } from "@inferencesh/sdk/proxy/hono";
39
- *
40
- * const app = new Hono();
41
- * const proxyHandler = createRouteHandler();
42
- *
43
- * app.all("/api/inference/proxy", proxyHandler);
44
- *
45
- * export default app;
46
- * ```
47
- *
48
- * @example Custom API key resolver
49
- * ```typescript
50
- * const proxyHandler = createRouteHandler({
51
- * resolveApiKey: async () => {
52
- * // Load from a secrets manager
53
- * return await getSecretFromVault("INFERENCE_API_KEY");
54
- * },
55
- * });
56
- * ```
22
+ * Creates a Hono route handler for the Inference.sh proxy.
57
23
  */
58
- export declare function createRouteHandler({ resolveApiKey, }?: HonoProxyOptions): RouteHandler;
24
+ export declare function createHandler({ resolveApiKey, }?: HonoProxyOptions): HonoHandler;
25
+ /** @deprecated Use createHandler */
26
+ export declare const createRouteHandler: typeof createHandler;
59
27
  export {};