@inferencesh/sdk 0.1.3 → 0.2.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
@@ -24,11 +24,9 @@ Get your API key from the [inference.sh dashboard](https://app.inference.sh/sett
24
24
  ## Quick Start
25
25
 
26
26
  ```typescript
27
- import { Inference, TaskStatusCompleted } from '@inferencesh/sdk';
27
+ import { inference } from '@inferencesh/sdk';
28
28
 
29
- const client = new Inference({
30
- apiKey: 'your-api-key'
31
- });
29
+ const client = inference({ apiKey: 'your-api-key' });
32
30
 
33
31
  // Run a task and wait for the result
34
32
  const result = await client.run({
@@ -46,9 +44,9 @@ console.log(result.output);
46
44
  ### Basic Usage
47
45
 
48
46
  ```typescript
49
- import { Inference } from '@inferencesh/sdk';
47
+ import { inference } from '@inferencesh/sdk';
50
48
 
51
- const client = new Inference({ apiKey: 'your-api-key' });
49
+ const client = inference({ apiKey: 'your-api-key' });
52
50
 
53
51
  // Wait for result (default behavior)
54
52
  const result = await client.run({
@@ -59,6 +57,18 @@ const result = await client.run({
59
57
  console.log('Output:', result.output);
60
58
  ```
61
59
 
60
+ ### With Setup Parameters
61
+
62
+ Setup parameters configure the app instance (e.g., model selection). Workers with matching setup are "warm" and skip setup:
63
+
64
+ ```typescript
65
+ const result = await client.run({
66
+ app: 'my-app',
67
+ setup: { model: 'schnell' }, // Setup parameters
68
+ input: { prompt: 'hello' }
69
+ });
70
+ ```
71
+
62
72
  ### Fire and Forget
63
73
 
64
74
  ```typescript
@@ -135,11 +145,90 @@ const task = await client.run(
135
145
  await client.cancel(task.id);
136
146
  ```
137
147
 
148
+ ## Agent Chat
149
+
150
+ Chat with AI agents using `client.agent()`.
151
+
152
+ ### Using a Template Agent
153
+
154
+ Use an existing agent from your workspace by its `namespace/name@shortid`:
155
+
156
+ ```typescript
157
+ import { inference } from '@inferencesh/sdk';
158
+
159
+ const client = inference({ apiKey: 'your-api-key' });
160
+
161
+ // Create agent from template
162
+ const agent = client.agent('my-org/assistant@abc123');
163
+
164
+ // Send a message with streaming
165
+ await agent.sendMessage('Hello!', {
166
+ onMessage: (msg) => {
167
+ if (msg.content) {
168
+ for (const c of msg.content) {
169
+ if (c.type === 'text' && c.text) {
170
+ process.stdout.write(c.text);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ });
176
+
177
+ // Clean up
178
+ agent.disconnect();
179
+ ```
180
+
181
+ ### Creating an Ad-Hoc Agent
182
+
183
+ Create agents on-the-fly without saving to your workspace:
184
+
185
+ ```typescript
186
+ import { inference, tool, string } from '@inferencesh/sdk';
187
+
188
+ const client = inference({ apiKey: 'your-api-key' });
189
+
190
+ // Create ad-hoc agent
191
+ const agent = client.agent({
192
+ coreApp: 'infsh/claude-sonnet-4@abc123', // LLM to use
193
+ systemPrompt: 'You are a helpful assistant.',
194
+ tools: [
195
+ tool('get_weather')
196
+ .description('Get current weather')
197
+ .params({ city: string('City name') })
198
+ .handler(async (args) => {
199
+ // Your tool logic here
200
+ return JSON.stringify({ temp: 72, conditions: 'sunny' });
201
+ })
202
+ .build()
203
+ ]
204
+ });
205
+
206
+ await agent.sendMessage('What is the weather in Paris?', {
207
+ onMessage: (msg) => console.log(msg),
208
+ onToolCall: async (call) => {
209
+ // Tool handlers are auto-executed if defined
210
+ }
211
+ });
212
+ ```
213
+
214
+ ### Agent Methods
215
+
216
+ | Method | Description |
217
+ |--------|-------------|
218
+ | `sendMessage(text, options?)` | Send a message to the agent |
219
+ | `getChat(chatId?)` | Get chat history |
220
+ | `stopChat(chatId?)` | Stop current generation |
221
+ | `submitToolResult(toolId, resultOrAction)` | Submit result for a client tool (string or {action, form_data}) |
222
+ | `streamMessages(chatId?, options?)` | Stream message updates |
223
+ | `streamChat(chatId?, options?)` | Stream chat updates |
224
+ | `disconnect()` | Clean up streams |
225
+ | `reset()` | Start a new conversation |
226
+
138
227
  ## API Reference
139
228
 
140
- ### `new Inference(config)`
229
+ ### `inference(config)`
141
230
 
142
- Creates a new Inference client.
231
+ Creates a new inference client.
143
232
 
144
233
  | Parameter | Type | Required | Description |
145
234
  |-----------|------|----------|-------------|
@@ -156,6 +245,7 @@ Runs a task on inference.sh.
156
245
  |-----------|------|----------|-------------|
157
246
  | `params.app` | `string` | Yes | App identifier (e.g., `'username/app-name'`) |
158
247
  | `params.input` | `object` | Yes | Input parameters for the app |
248
+ | `params.setup` | `object` | No | Setup parameters (affects worker warmth/scheduling) |
159
249
  | `params.infra` | `string` | No | Infrastructure: `'cloud'` or `'private'` |
160
250
  | `params.variant` | `string` | No | App variant to use |
161
251
 
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Headless Agent SDK
3
+ *
4
+ * Chat with AI agents without UI dependencies.
5
+ */
6
+ import { ChatDTO, ChatMessageDTO, AgentRuntimeConfig } from './types';
7
+ export interface AgentConfig {
8
+ apiKey: string;
9
+ baseUrl?: string;
10
+ }
11
+ /**
12
+ * Ad-hoc agent configuration - extends AgentRuntimeConfig with core_app_ref required
13
+ * Uses Partial to make name/system_prompt optional for ad-hoc usage
14
+ */
15
+ export type AdHocAgentOptions = Partial<AgentRuntimeConfig> & {
16
+ /** Core LLM app ref: namespace/name@shortid (required for ad-hoc agents) */
17
+ core_app_ref: string;
18
+ };
19
+ /** Template agent configuration */
20
+ export interface TemplateAgentOptions {
21
+ /** Agent reference: namespace/name@version (e.g., "my-org/assistant@abc123") */
22
+ agent: string;
23
+ }
24
+ export type AgentOptions = AdHocAgentOptions | TemplateAgentOptions;
25
+ export interface SendMessageOptions {
26
+ /** File attachments (Blob or base64 data URI) */
27
+ files?: (Blob | string)[];
28
+ /** Callback for message updates */
29
+ onMessage?: (message: ChatMessageDTO) => void;
30
+ /** Callback for chat updates */
31
+ onChat?: (chat: ChatDTO) => void;
32
+ /** Callback when a client tool needs execution */
33
+ onToolCall?: (invocation: {
34
+ id: string;
35
+ name: string;
36
+ args: Record<string, unknown>;
37
+ }) => void;
38
+ }
39
+ export declare class Agent {
40
+ private readonly apiKey;
41
+ private readonly baseUrl;
42
+ private readonly options;
43
+ private chatId;
44
+ private stream;
45
+ constructor(config: AgentConfig, options: AgentOptions);
46
+ /** Get current chat ID */
47
+ get currentChatId(): string | null;
48
+ /** Send a message to the agent */
49
+ sendMessage(text: string, options?: SendMessageOptions): Promise<ChatMessageDTO>;
50
+ /** Get chat by ID */
51
+ getChat(chatId?: string): Promise<ChatDTO | null>;
52
+ /** Stop the current chat generation */
53
+ stopChat(): Promise<void>;
54
+ /**
55
+ * Submit a tool result
56
+ * @param toolInvocationId - The tool invocation ID
57
+ * @param resultOrAction - Either a raw result string, or an object with action and optional form_data (will be JSON-serialized)
58
+ */
59
+ submitToolResult(toolInvocationId: string, resultOrAction: string | {
60
+ action: {
61
+ type: string;
62
+ payload?: Record<string, unknown>;
63
+ };
64
+ form_data?: Record<string, unknown>;
65
+ }): Promise<void>;
66
+ /** Stop streaming and cleanup */
67
+ disconnect(): void;
68
+ /** Reset the agent (start fresh chat) */
69
+ reset(): void;
70
+ /** Upload a file and return the file object */
71
+ uploadFile(data: Blob | string): Promise<{
72
+ uri: string;
73
+ content_type?: string;
74
+ }>;
75
+ private startStreaming;
76
+ private createEventSource;
77
+ private request;
78
+ }
package/dist/agent.js ADDED
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ /**
3
+ * Headless Agent SDK
4
+ *
5
+ * Chat with AI agents without UI dependencies.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.Agent = void 0;
9
+ const stream_1 = require("./stream");
10
+ const eventsource_1 = require("eventsource");
11
+ const types_1 = require("./types");
12
+ // =============================================================================
13
+ // Agent Class
14
+ // =============================================================================
15
+ class Agent {
16
+ constructor(config, options) {
17
+ this.chatId = null;
18
+ // Unified stream for both Chat and ChatMessage events (uses TypedEvents)
19
+ this.stream = null;
20
+ this.apiKey = config.apiKey;
21
+ this.baseUrl = config.baseUrl || 'https://api.inference.sh';
22
+ this.options = options;
23
+ }
24
+ /** Get current chat ID */
25
+ get currentChatId() {
26
+ return this.chatId;
27
+ }
28
+ /** Send a message to the agent */
29
+ async sendMessage(text, options = {}) {
30
+ const isAdHoc = 'core_app_ref' in this.options;
31
+ // Upload files if provided
32
+ let imageUri;
33
+ let fileUris;
34
+ if (options.files && options.files.length > 0) {
35
+ const uploadedFiles = await Promise.all(options.files.map(f => this.uploadFile(f)));
36
+ // Separate images from other files
37
+ const images = uploadedFiles.filter(f => f.content_type?.startsWith('image/'));
38
+ const others = uploadedFiles.filter(f => !f.content_type?.startsWith('image/'));
39
+ if (images.length > 0)
40
+ imageUri = images[0].uri;
41
+ if (others.length > 0)
42
+ fileUris = others.map(f => f.uri);
43
+ }
44
+ // Both template and ad-hoc use /agents/run
45
+ const input = { text, image: imageUri, files: fileUris, role: 'user', context: [], system_prompt: '', context_size: 0 };
46
+ const body = isAdHoc
47
+ ? { chat_id: this.chatId, agent_config: this.options, input }
48
+ : { chat_id: this.chatId, agent: this.options.agent, input };
49
+ const response = await this.request('post', '/agents/run', { data: body });
50
+ // Update chat ID if new
51
+ if (!this.chatId && response.assistant_message.chat_id) {
52
+ this.chatId = response.assistant_message.chat_id;
53
+ this.startStreaming(options);
54
+ }
55
+ return response.assistant_message;
56
+ }
57
+ /** Get chat by ID */
58
+ async getChat(chatId) {
59
+ const id = chatId || this.chatId;
60
+ if (!id)
61
+ return null;
62
+ return this.request('get', `/chats/${id}`);
63
+ }
64
+ /** Stop the current chat generation */
65
+ async stopChat() {
66
+ if (!this.chatId)
67
+ return;
68
+ await this.request('post', `/chats/${this.chatId}/stop`);
69
+ }
70
+ /**
71
+ * Submit a tool result
72
+ * @param toolInvocationId - The tool invocation ID
73
+ * @param resultOrAction - Either a raw result string, or an object with action and optional form_data (will be JSON-serialized)
74
+ */
75
+ async submitToolResult(toolInvocationId, resultOrAction) {
76
+ // Serialize widget actions to JSON string
77
+ const result = typeof resultOrAction === 'string'
78
+ ? resultOrAction
79
+ : JSON.stringify(resultOrAction);
80
+ await this.request('post', `/tools/${toolInvocationId}`, { data: { result } });
81
+ }
82
+ /** Stop streaming and cleanup */
83
+ disconnect() {
84
+ this.stream?.stop();
85
+ this.stream = null;
86
+ }
87
+ /** Reset the agent (start fresh chat) */
88
+ reset() {
89
+ this.disconnect();
90
+ this.chatId = null;
91
+ }
92
+ /** Upload a file and return the file object */
93
+ async uploadFile(data) {
94
+ // Create file record
95
+ let contentType = 'application/octet-stream';
96
+ let size;
97
+ if (data instanceof Blob) {
98
+ contentType = data.type || 'application/octet-stream';
99
+ size = data.size;
100
+ }
101
+ const files = await this.request('post', '/files', { data: { files: [{ uri: '', content_type: contentType, size }] } });
102
+ const file = files[0];
103
+ if (!file.upload_url)
104
+ throw new Error('No upload URL');
105
+ // Convert to blob if needed
106
+ let blob;
107
+ if (data instanceof Blob) {
108
+ blob = data;
109
+ }
110
+ else if (data.startsWith('data:')) {
111
+ const matches = data.match(/^data:([^;]+);base64,(.+)$/);
112
+ if (!matches)
113
+ throw new Error('Invalid data URI');
114
+ const bytes = Uint8Array.from(atob(matches[2]), c => c.charCodeAt(0));
115
+ blob = new Blob([bytes], { type: matches[1] });
116
+ }
117
+ else {
118
+ const bytes = Uint8Array.from(atob(data), c => c.charCodeAt(0));
119
+ blob = new Blob([bytes], { type: contentType });
120
+ }
121
+ // Upload to signed URL
122
+ const uploadResp = await fetch(file.upload_url, {
123
+ method: 'PUT',
124
+ body: blob,
125
+ headers: { 'Content-Type': blob.type },
126
+ });
127
+ if (!uploadResp.ok)
128
+ throw new Error('Upload failed');
129
+ return { uri: file.uri, content_type: file.content_type };
130
+ }
131
+ // =============================================================================
132
+ // Private Methods
133
+ // =============================================================================
134
+ startStreaming(options) {
135
+ if (!this.chatId)
136
+ return;
137
+ // Unified stream with TypedEvents (single SSE connection for both Chat and ChatMessage)
138
+ this.stream = new stream_1.StreamManager({
139
+ createEventSource: async () => this.createEventSource(`/chats/${this.chatId}/stream`),
140
+ autoReconnect: true,
141
+ });
142
+ // Listen for Chat object updates (status changes)
143
+ this.stream.addEventListener('chats', (chat) => {
144
+ options.onChat?.(chat);
145
+ });
146
+ // Listen for ChatMessage updates
147
+ this.stream.addEventListener('chat_messages', (message) => {
148
+ options.onMessage?.(message);
149
+ // Check for client tool invocations
150
+ if (message.tool_invocations && options.onToolCall) {
151
+ for (const inv of message.tool_invocations) {
152
+ if (inv.type === types_1.ToolTypeClient && inv.status === types_1.ToolInvocationStatusAwaitingInput) {
153
+ options.onToolCall({
154
+ id: inv.id,
155
+ name: inv.function?.name || '',
156
+ args: inv.function?.arguments || {},
157
+ });
158
+ }
159
+ }
160
+ }
161
+ });
162
+ this.stream.connect();
163
+ }
164
+ createEventSource(endpoint) {
165
+ return new eventsource_1.EventSource(`${this.baseUrl}${endpoint}`, {
166
+ fetch: (input, init) => fetch(input, {
167
+ ...init,
168
+ headers: { ...init.headers, Authorization: `Bearer ${this.apiKey}` },
169
+ }),
170
+ });
171
+ }
172
+ async request(method, endpoint, options = {}) {
173
+ const response = await fetch(`${this.baseUrl}${endpoint}`, {
174
+ method: method.toUpperCase(),
175
+ headers: {
176
+ 'Content-Type': 'application/json',
177
+ Authorization: `Bearer ${this.apiKey}`,
178
+ },
179
+ body: options.data ? JSON.stringify(options.data) : undefined,
180
+ });
181
+ const json = await response.json();
182
+ if (!response.ok || !json.success) {
183
+ throw new Error(json.error?.message || 'Request failed');
184
+ }
185
+ return json.data;
186
+ }
187
+ }
188
+ exports.Agent = Agent;
package/dist/client.d.ts CHANGED
@@ -1,4 +1,27 @@
1
- import { ApiTaskRequest, TaskDTO as Task, File } from './types';
1
+ import { ApiAppRunRequest, TaskDTO as Task, File, ChatDTO, ChatMessageDTO, AgentRuntimeConfig } from './types';
2
+ import { EventSource } from 'eventsource';
3
+ /**
4
+ * Ad-hoc agent configuration - extends AgentRuntimeConfig with core_app_ref required
5
+ * Uses Partial to make name/system_prompt optional for ad-hoc usage
6
+ */
7
+ export type AdHocAgentConfig = Partial<AgentRuntimeConfig> & {
8
+ /** Core LLM app ref: namespace/name@shortid (required for ad-hoc agents) */
9
+ core_app_ref: string;
10
+ };
11
+ export interface SendMessageOptions {
12
+ /** File attachments (Blob or base64 data URI) */
13
+ files?: (Blob | string)[];
14
+ /** Callback for message updates */
15
+ onMessage?: (message: ChatMessageDTO) => void;
16
+ /** Callback for chat updates */
17
+ onChat?: (chat: ChatDTO) => void;
18
+ /** Callback when a client tool needs execution */
19
+ onToolCall?: (invocation: {
20
+ id: string;
21
+ name: string;
22
+ args: Record<string, unknown>;
23
+ }) => void;
24
+ }
2
25
  export interface UploadFileOptions {
3
26
  filename?: string;
4
27
  contentType?: string;
@@ -38,8 +61,13 @@ export declare class Inference {
38
61
  private readonly apiKey;
39
62
  private readonly baseUrl;
40
63
  constructor(config: InferenceConfig);
41
- private request;
42
- private createEventSource;
64
+ /** @internal */
65
+ _request<T>(method: "get" | "post" | "put" | "delete", endpoint: string, options?: {
66
+ params?: Record<string, any>;
67
+ data?: Record<string, any>;
68
+ }): Promise<T>;
69
+ /** @internal */
70
+ _createEventSource(endpoint: string): EventSource;
43
71
  private _stripTask;
44
72
  private processInputData;
45
73
  /**
@@ -49,22 +77,33 @@ export declare class Inference {
49
77
  * @param options - Run options for waiting, updates, and reconnection
50
78
  * @returns The completed task result
51
79
  *
80
+ * App reference format: `namespace/name@shortid` (version is required)
81
+ *
82
+ * The short ID ensures your code always runs the same version,
83
+ * protecting against breaking changes from app updates.
84
+ *
52
85
  * @example
53
86
  * ```typescript
54
- * // Simple usage - wait for result
55
- * const result = await client.run({ app: 'my-app', input: { prompt: 'hello' } });
87
+ * // Run a specific version (required)
88
+ * const result = await client.run({
89
+ * app: 'okaris/flux@abc1', // version @abc1 is pinned
90
+ * input: { prompt: 'hello' }
91
+ * });
56
92
  *
57
93
  * // With status updates
58
94
  * const result = await client.run(
59
- * { app: 'my-app', input: { prompt: 'hello' } },
95
+ * { app: 'okaris/flux@abc1', input: { prompt: 'hello' } },
60
96
  * { onUpdate: (update) => console.log(update.status) }
61
97
  * );
62
98
  *
63
99
  * // Fire and forget
64
- * const task = await client.run({ app: 'my-app', input: {} }, { wait: false });
100
+ * const task = await client.run(
101
+ * { app: 'okaris/flux@abc1', input: {} },
102
+ * { wait: false }
103
+ * );
65
104
  * ```
66
105
  */
67
- run(params: ApiTaskRequest, options?: RunOptions): Promise<Task>;
106
+ run(params: ApiAppRunRequest, options?: RunOptions): Promise<Task>;
68
107
  uploadFile(data: string | Blob, options?: UploadFileOptions): Promise<File>;
69
108
  /**
70
109
  * Cancel a running task
@@ -72,8 +111,75 @@ export declare class Inference {
72
111
  * @param taskId - The ID of the task to cancel
73
112
  */
74
113
  cancel(taskId: string): Promise<void>;
114
+ /**
115
+ * Create an agent for chat interactions
116
+ *
117
+ * @param config - Either a template reference string (namespace/name@version) or ad-hoc config
118
+ * @returns An Agent instance for chat operations
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * // Template agent
123
+ * const agent = client.agent('okaris/assistant@abc123')
124
+ *
125
+ * // Ad-hoc agent
126
+ * const agent = client.agent({
127
+ * core_app_ref: 'infsh/claude-sonnet-4@xyz789',
128
+ * system_prompt: 'You are a helpful assistant',
129
+ * tools: [...]
130
+ * })
131
+ *
132
+ * // Send messages
133
+ * const response = await agent.sendMessage('Hello!')
134
+ * ```
135
+ */
136
+ agent(config: string | AdHocAgentConfig): Agent;
137
+ }
138
+ /**
139
+ * Agent for chat interactions
140
+ *
141
+ * Created via `client.agent()` - do not instantiate directly.
142
+ */
143
+ export declare class Agent {
144
+ private readonly client;
145
+ private readonly config;
146
+ private chatId;
147
+ private stream;
148
+ private dispatchedToolCalls;
149
+ /** @internal */
150
+ constructor(client: Inference, config: string | AdHocAgentConfig);
151
+ /** Get current chat ID */
152
+ get currentChatId(): string | null;
153
+ /** Send a message to the agent */
154
+ sendMessage(text: string, options?: SendMessageOptions): Promise<ChatMessageDTO>;
155
+ /** Get chat by ID */
156
+ getChat(chatId?: string): Promise<ChatDTO | null>;
157
+ /** Stop the current chat generation */
158
+ stopChat(): Promise<void>;
159
+ /**
160
+ * Submit a tool result
161
+ * @param toolInvocationId - The tool invocation ID
162
+ * @param resultOrAction - Either a raw result string, or an object with action and optional form_data (will be JSON-serialized)
163
+ */
164
+ submitToolResult(toolInvocationId: string, resultOrAction: string | {
165
+ action: {
166
+ type: string;
167
+ payload?: Record<string, unknown>;
168
+ };
169
+ form_data?: Record<string, unknown>;
170
+ }): Promise<void>;
171
+ /** Stop streaming and cleanup */
172
+ disconnect(): void;
173
+ /** Reset the agent (start fresh chat) */
174
+ reset(): void;
175
+ /** Stream events until chat becomes idle */
176
+ private streamUntilIdle;
75
177
  }
76
178
  /**
77
- * @deprecated Use `Inference` instead. Will be removed in v1.0.0
179
+ * Factory function for creating an Inference client (lowercase for branding)
180
+ * @example
181
+ * ```typescript
182
+ * const client = inference({ apiKey: 'your-api-key' });
183
+ * ```
78
184
  */
79
- export declare const inference: typeof Inference;
185
+ export declare function inference(config: InferenceConfig): Inference;