@inferencesh/sdk

中文文档
0.38.4发布于 2026/11/01MITTypeScriptES Module构建来源已验证

Official JavaScript/TypeScript SDK for inference.sh - Run AI models with a simple API

原始使用文档
文档目录40 节
  1. Installation
  2. Getting an API Key
  3. Quick Start
  4. Usage
  5. Basic Usage
  6. With Setup Parameters
  7. Fire and Forget
  8. Real-time Status Updates
  9. Streaming vs Polling
  10. Batch Processing
  11. File Upload
  12. Cancel a Task
  13. Stream Functions (Live Sockets)
  14. Sessions (Stateful Execution)
  15. Agent Chat
  16. Using a Template Agent
  17. Creating an Ad-Hoc Agent
  18. Tool builder
  19. Lifecycle hooks
  20. Queued messages
  21. File attachments
  22. Structured Output
  23. Agent Methods
  24. API Reference
  25. inference(config)
  26. client.tasks.run(params, options?)
  27. client.tasks.get(taskId)
  28. client.tasks.cancel(taskId)
  29. client.files.upload(data, options?)
  30. client.agents.create(templateOrConfig) / client.agent(...)
  31. client.sessions
  32. client.sockets
  33. Task Status Constants
  34. Credential Constants
  35. Instance Status Constants
  36. Tool Parameter Types
  37. TypeScript Support
  38. Requirements
  39. resources
  40. license

@inferencesh/sdk — ai inference api for javascript & typescript

npm version npm downloads License: MIT TypeScript

official javascript/typescript sdk for inference.sh — the ai agent runtime for serverless ai inference.

run ai models, build ai agents, and deploy generative ai applications with a simple api. access models including flux, stable diffusion, llms (claude, gpt, gemini), video generation (veo, seedance), and more.

Installation

npm install @inferencesh/sdk
# or
yarn add @inferencesh/sdk
# or
pnpm add @inferencesh/sdk

Getting an API Key

Get your API key from the inference.sh dashboard.

Quick Start

import { inference, TaskStatusCompleted } from '@inferencesh/sdk';

const client = inference({ apiKey: 'your-api-key' });

// Run a task and wait for the result
const result = await client.tasks.run({
  app: 'your-app',
  input: {
    prompt: 'Hello, world!'
  }
});

if (result.status === TaskStatusCompleted) {
  console.log(result.output);
}

Usage

Basic Usage

import { inference, TaskStatusCompleted } from '@inferencesh/sdk';

const client = inference({ apiKey: 'your-api-key' });

// Wait for result (default behavior)
const result = await client.tasks.run({
  app: 'my-app',
  input: { prompt: 'Generate something amazing' }
});

if (result.status === TaskStatusCompleted) {
  console.log('Output:', result.output);
}

With Setup Parameters

Setup parameters configure the app instance (e.g., model selection). Workers with matching setup are "warm" and skip setup:

const result = await client.tasks.run({
  app: 'my-app',
  setup: { model: 'schnell' },  // Setup parameters
  input: { prompt: 'hello' }
});

Fire and Forget

// Get task info immediately without waiting
const task = await client.tasks.run(
  { app: 'my-app', input: { prompt: 'hello' } },
  { wait: false }
);

console.log('Task ID:', task.id);
console.log('Status:', task.status);

Real-time Status Updates

By default, the client streams task progress over NDJSON (/tasks/{id}/stream) and invokes onUpdate as the task changes. Use onPartialUpdate when you only need specific fields from a partial stream payload:

const result = await client.run(
  { app: 'my-app', input: { prompt: 'hello' } },
  {
    onUpdate: (update) => {
      console.log('Status:', update.status);
      console.log('Progress:', update.logs);
    },
    onPartialUpdate: (update, fields) => {
      console.log('Changed fields:', fields, update.status);
    },
  }
);

Streaming vs Polling

SSE/NDJSON streaming is the default. For edge runtimes that cannot keep long-lived connections open (Convex actions, Cloudflare Workers, etc.), disable streaming and use lightweight status polling instead:

const client = inference({
  apiKey: 'your-api-key',
  stream: false,           // poll /tasks/{id}/status instead of streaming
  pollIntervalMs: 2000,    // default: 2000
});

// Per-call override
const result = await client.run(
  { app: 'my-app', input: { prompt: 'hello' } },
  { stream: false, onUpdate: (u) => console.log(u.status) }
);

In polling mode, the SDK checks /tasks/{id}/status and fetches the full task when the status changes. If that fetch fails after a status transition, run() rejects with the underlying error.

Batch Processing

async function processImages(images: string[]) {
  const results = [];

  for (const image of images) {
    const result = await client.tasks.run({
      app: 'image-processor',
      input: { image }
    }, {
      onUpdate: (update) => console.log(`Processing: ${update.status}`)
    });

    results.push(result);
  }

  return results;
}

File Upload

// Upload from base64
const file = await client.files.upload('data:image/png;base64,...', {
  filename: 'image.png',
  contentType: 'image/png'
});

// Use the uploaded file in a task
const result = await client.tasks.run({
  app: 'image-app',
  input: { image: file.uri }
});

Cancel a Task

const task = await client.tasks.run(
  { app: 'long-running-app', input: {} },
  { wait: false }
);

// Cancel if needed
await client.tasks.cancel(task.id);

Stream Functions (Live Sockets)

A stream function keeps a socket open with its caller for the life of the task: frames go both ways until the caller closes or the app returns. client.live starts the task and dials its socket; the run response carries where to dial (task.socket).

const { task, session } = await client.live(
  { app: 'infsh/voice-loop', function: 'stream', input: { effect: 'robot' } },
  {
    onState: (state) => console.log(state), // connecting → waiting → live → ended
    onBinary: (pcm) => speaker.write(new Int16Array(pcm)),
    onPatch: (patch) => console.log(patch), // e.g. { frames: 120 }
  }
);

session.sendBinary(micFrame);          // one item of the input's binary live field
session.sendPatch({ effect: 'echo' }); // change an ordinary input while it runs
session.close();                       // the function returns and the task completes
await session.ended;

The session is waiting until the app's first frame (a cold start can take a minute) and gives up if the task ends before then. It dials again with a fresh credential when the relay restarts under it. client.sockets.open(taskOrId, handlers) reconnects to a running task's socket, e.g. after a page reload.

What a function's socket carries is in its schemas: a live field is {"type": "array", "format": "stream", "items": ...}. splitLiveSchema(schema) separates the ordinary fields (the request body) from the live ones, and pcmFormat(field.media) reads the sample rate of a PCM audio field.

On Node 18–21 there is no global WebSocket: pass one from the ws package as { webSocket: WebSocket }.

Sessions (Stateful Execution)

Sessions allow you to maintain state across multiple task invocations. The worker stays warm between calls, preserving loaded models and in-memory state.

// Start a new session
const result = await client.tasks.run({
  app: 'my-stateful-app',
  input: { prompt: 'hello' },
  session: 'new'
});

const sessionId = result.session_id;
console.log('Session ID:', sessionId);

// Continue the session with another call
const result2 = await client.tasks.run({
  app: 'my-stateful-app',
  input: { prompt: 'remember what I said?' },
  session: sessionId
});

Custom Session Timeout

By default, sessions expire after 60 seconds of inactivity. You can customize this with session_timeout (1-3600 seconds):

// Create a session with 5-minute idle timeout
const result = await client.tasks.run({
  app: 'my-stateful-app',
  input: { prompt: 'hello' },
  session: 'new',
  session_timeout: 300  // 5 minutes
});

// Session stays alive for 5 minutes after each call

Notes:

  • session_timeout is only valid when session: 'new'
  • Minimum timeout: 1 second
  • Maximum timeout: 3600 seconds (1 hour)
  • Each successful call resets the idle timer

Session management API

Manage sessions directly without running a task:

// Inspect a session
const info = await client.sessions.get(sessionId);
console.log(info.status, info.expires_at, info.call_count);

// List active sessions
const sessions = await client.sessions.list();

// Extend idle timeout without a task call (sliding window)
await client.sessions.keepalive(sessionId);

// Release the worker immediately
await client.sessions.end(sessionId);

Session errors

import {
  SessionNotFoundError,
  SessionExpiredError,
  SessionEndedError,
} from '@inferencesh/sdk';

try {
  await client.tasks.run({
    app: 'my-stateful-app',
    input: { prompt: 'hello' },
    session: sessionId,
  });
} catch (error) {
  if (
    error instanceof SessionNotFoundError ||
    error instanceof SessionExpiredError ||
    error instanceof SessionEndedError
  ) {
    // Start a new session and retry
    const result = await client.tasks.run({
      app: 'my-stateful-app',
      input: { prompt: 'hello' },
      session: 'new',
    });
  } else {
    throw error;
  }
}

For complete session documentation including error handling, best practices, and advanced patterns, see the Sessions Developer Guide.

Agent Chat

Chat with AI agents using client.agents.create().

Using a Template Agent

Use an existing agent from your workspace by its namespace/name@shortid:

import { inference } from '@inferencesh/sdk';

const client = inference({ apiKey: 'your-api-key' });

// Create agent from template
const agent = client.agents.create('my-org/assistant@abc123');

// Send a message with streaming
await agent.sendMessage('Hello!', {
  onMessage: (msg) => {
    if (msg.content) {
      for (const c of msg.content) {
        if (c.type === 'text' && c.text) {
          process.stdout.write(c.text);
        }
      }
    }
  }
});

// Clean up
agent.disconnect();

Creating an Ad-Hoc Agent

Create agents on-the-fly without saving to your workspace:

import { inference, tool, string } from '@inferencesh/sdk';

const client = inference({ apiKey: 'your-api-key' });

// Create ad-hoc agent (config uses API field names: core_app, system_prompt)
const weatherTool = tool('get_weather')
  .describe('Get current weather')
  .param('city', string('City name'))
  .build();

const agent = client.agents.create({
  core_app: { ref: 'infsh/claude-sonnet-4@abc123' },
  system_prompt: 'You are a helpful assistant.',
  tools: [weatherTool], // only schemas are sent to the API; handlers stay client-side
});

await agent.sendMessage('What is the weather in Paris?', {
  onMessage: (msg) => console.log(msg),
  onToolCall: async (call) => {
    const result = await runMyClientTool(call.name, call.args);
    await agent.submitToolResult(call.id, result);
  },
});

For multi-turn chats, the SDK opens the chat stream before sending the next message so updates are not missed. Use stopChat() to cancel in-flight generation (POST /chats/{id}/stop), and reset() to clear the current chat and start fresh.

Tool builder

Use the fluent builders to define AgentTool schemas. Client tools (tool) run in your app via onToolCall; server-side tools run on inference.sh.

BuilderRuns onDescription
tool(name)ClientLocal handler; only the schema is sent to the API
appTool(name, appRef)ServerInvoke another inference app
agentTool(name, agentRef)ServerDelegate to a sub-agent
httpTool(name, url) / callTool(name, url)ServerHTTP request with credential injection (preferred over webhookTool)
webhookTool(name, url)ServerUnsigned webhook (legacy; use httpTool for new tools)
mcpTool(name, { server, toolName })ServerCall a tool on an MCP server (by slug), signed in with the runner's own connection to the server's provider
internalTools()ServerBuilt-in tools: plan, memory, widget, finish, skills, meta, remote, knowledge, artifact, agent
import {
  inference,
  tool,
  appTool,
  httpTool,
  mcpTool,
  internalTools,
  string,
  CredentialProviderGoogle,
} from '@inferencesh/sdk';

const clientTool = tool('get_weather')
  .describe('Get current weather')
  .param('city', string('City name'))
  .build();

// HTTP tool authenticated with a connected account (token injected server-side)
const gmailSend = httpTool('gmail_send', 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send')
  .describe('Send an email via Gmail')
  .method('POST')
  .auth({ credential: CredentialProviderGoogle })
  .build();

// API key or bearer auth
const fetchData = httpTool('fetch', 'https://api.example.com/data')
  .method('GET')
  .auth({ apiKey: 'YOUR_KEY', header: 'X-API-Key' }) // default header: X-API-Key
  .header('Accept', 'application/json')
  .build();

const bearerFetch = httpTool('bearer_fetch', 'https://api.example.com')
  .auth({ bearer: 'YOUR_TOKEN' })
  .build();

const imageGen = appTool('generate_image', 'infsh/flux-schnell@abc123')
  .param('prompt', string('Image description'))
  .requireApproval()
  .build();

// MCP server by slug; the runner's connection to its provider signs the call
const mcpSearch = mcpTool('notion_search', { server: 'notion', toolName: 'search' })
  .describe('Search Notion pages')
  .param('query', string('Search query'))
  .build();

const agent = client.agents.create({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  system_prompt: 'You are helpful.',
  tools: [clientTool, gmailSend, imageGen, mcpSearch],
  internal_tools: internalTools().memory().build(),
});

callTool is an alias for httpTool. Run npx tsx examples/tool-builder.ts for more schema examples (no API key required).

Lifecycle hooks

Use lifecycleHook() to attach hooks to agent events. Hooks can call a webhook or delegate to another agent as a task:

import { inference, lifecycleHook, HookEventTurnStart, HookEventToolCall } from '@inferencesh/sdk';

const client = inference({ apiKey: 'your-api-key' });

const agent = client.agents.create({
  core_app: { ref: 'openrouter/claude-sonnet-4@abc' },
  hooks: [
    lifecycleHook(HookEventTurnStart)
      .webhook('https://example.com/on-turn')
      .timeout(10)
      .build(),
    lifecycleHook(HookEventToolCall)
      .task('my-org/approval-agent')
      .async(true)
      .build(),
  ],
});

Queued messages

sendMessage automatically queues when the agent is busy processing a previous message. Queued messages appear with a queued status until the agent picks them up.

To cancel a queued message before it is processed, use cancelMessage on the React provider or chats.cancelMessage on the API client:

// Via the API client
await client.chats.cancelMessage(messageId);

File attachments

Pass files in sendMessage options. Blob values are uploaded first; objects with a uri (already uploaded via client.files.upload) are attached as-is:

const uploaded = await client.files.upload(imageBlob, {
  filename: 'photo.png',
  contentType: 'image/png',
});

await agent.sendMessage('Describe this image', {
  files: [imageBlob, uploaded], // Blob uploads; FileDTO reuses uri
  onMessage: (msg) => console.log(msg),
});

Structured Output

Use output_schema to get structured JSON responses:

const agent = client.agents.create({
  core_app: { ref: 'infsh/claude-sonnet-4@latest' },
  output_schema: {
    type: 'object',
    properties: {
      summary: { type: 'string' },
      sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
      confidence: { type: 'number' },
    },
    required: ['summary', 'sentiment', 'confidence'],
  },
  internal_tools: { finish: true },
});

const output = await agent.run('Analyze: Great product!');

agent.run() sends a message with polling (no SSE), waits until the chat is idle, and returns chat.output (parsed finish-tool result, or null if none).

Agent Methods

MethodDescription
sendMessage(text, options?)Send a message; streams or polls until idle when callbacks or stream: false
run(text, options?)Send and return structured chat.output (always uses polling)
getChat(chatId?)Get the current or specified chat (chat_messages on the returned chat)
stopChat()Stop generation for the current chat (no-op if no active chat)
submitToolResult(toolId, resultOrAction)Submit result for a client tool (string or {action, form_data})
startStreaming(options?)Manually attach to /chats/{id}/stream for the current chat
disconnect()Stop active stream/poll connections
reset()Disconnect and clear chat state so the next message starts a new chat

Messages that arrive through a channel integration (Slack, Telegram, a wearable) can carry channel_context so the reply is routed back the same way. The API records it on the chat the first time it sees it, so later turns inherit it:

await agent.sendMessage(text, {
  channel_context: { channel_type: 'slack', channel_metadata: { channel: 'C123', thread_ts: '1700000000.000100' } },
});

API Reference

inference(config)

Creates a new inference client.

ParameterTypeRequiredDescription
config.apiKeystringYesYour inference.sh API key
config.baseUrlstringNoCustom API URL (default: https://api.inference.sh)
config.streambooleanNoUse NDJSON streaming (true, default) or status polling (false)
config.pollIntervalMsnumberNoPoll interval when stream: false (default: 2000)
config.proxyUrlstringNoProxy base URL for frontend apps (keeps API keys server-side)

client.tasks.run(params, options?)

Runs a task on inference.sh.

Parameters:

ParameterTypeRequiredDescription
params.appstringYesApp identifier (e.g., 'username/app-name')
params.inputobjectYesInput parameters for the app
params.setupobjectNoSetup parameters (affects worker warmth/scheduling)
params.infrastringNoInfrastructure: 'cloud' or 'private'
params.sessionstringNoSession ID or 'new' to start a new session
params.session_timeoutnumberNoSession timeout in seconds (1-3600, only with session: 'new')

Options:

OptionTypeDefaultDescription
waitbooleantrueWait for task completion
streambooleanclient defaultUse NDJSON streaming or status polling
pollIntervalMsnumberclient defaultPoll interval when stream: false
onUpdatefunction-Callback for task updates (full fetch on status change when polling)
onPartialUpdatefunction-Callback for partial NDJSON stream updates (task, fields)
maxReconnectsnumber5Max poll retries when stream: false

client.tasks.get(taskId)

Gets a task by ID.

client.tasks.cancel(taskId)

Cancels a running task.

client.files.upload(data, options?)

Uploads a file to inference.sh.

Parameters:

ParameterTypeDescription
datastring | BlobBase64 string, data URI, or Blob
options.filenamestringFilename
options.contentTypestringMIME type
options.publicbooleanMake file publicly accessible

client.agents.create(templateOrConfig) / client.agent(...)

Creates an agent instance from a template or ad-hoc configuration. client.agent(...) is an alias for client.agents.create(...).

sendMessage options: onMessage, onChat, onToolCall, files, stream, pollIntervalMs. Client tools with status awaiting_input are dispatched once per invocation ID via onToolCall.

Template mode:

const agent = client.agents.create('namespace/name@version');

Ad-hoc mode:

const agent = client.agents.create({
  core_app: { ref: 'infsh/claude-sonnet-4@abc123' },
  system_prompt: 'You are helpful.',
  tools: [...],
});

client.sessions

MethodHTTPDescription
get(sessionId)GET /sessions/{id}Session metadata (status, expires_at, call_count, …)
list()GET /sessionsAll sessions (empty array if none)
keepalive(sessionId)POST /sessions/{id}/keepaliveReset idle expiration
end(sessionId)DELETE /sessions/{id}End session and release worker

client.sockets

MethodHTTPDescription
open(taskOrId, handlers?, options?)—Dial a stream task's socket; returns a LiveSession
get(socketId)GET /sockets/{id}The socket and what is known of its life
forTask(taskId)POST /sockets/listThe task's socket, or null
access(socketId)POST /sockets/{id}/accessA fresh credential for the caller's end
delete(socketId)DELETE /sockets/{id}Delete the record

client.live(params, handlers?, options?) is tasks.run(params, { wait: false }) followed by sockets.open.

Task Status Constants

import {
  TaskStatusQueued,
  TaskStatusRunning,
  TaskStatusCompleted,
  TaskStatusFailed,
  TaskStatusCancelled
} from '@inferencesh/sdk';

if (task.status === TaskStatusCompleted) {
  console.log('Done!');
}

Credential Constants

CredentialDTO fields (provider, type, status) use typed string unions exported as constants:

import type { CredentialDTO } from '@inferencesh/sdk';
import {
  CredentialProviderGoogle,
  CredentialTypeOAuth,
  CredentialStatusConnected,
  CredentialStatusDisconnected,
  CredentialStatusExpired,
  CredentialStatusError,
  RequirementTypeCredential,
  isRequirementsNotMetException,
} from '@inferencesh/sdk';

function isGoogleConnected(credential: CredentialDTO): boolean {
  return (
    credential.provider === CredentialProviderGoogle &&
    credential.status === CredentialStatusConnected
  );
}

// HTTP 412 when an app requires a missing secret, credential, or scope
try {
  await client.run({ app: 'my-app', input: {} });
} catch (error) {
  if (isRequirementsNotMetException(error)) {
    for (const req of error.errors) {
      if (req.type === RequirementTypeCredential && req.action?.provider === CredentialProviderGoogle) {
        // User must connect Google — see https://inference.sh/docs/extend/credentials
      }
    }
  }
}
Constant groupValues
CredentialProvider*google, google-sa, slack, notion, github, x, microsoft, salesforce, discord, gcp, reddit
CredentialType*service_account, oauth, api_key, wif
CredentialStatus*connected, disconnected, expired, error

Instance Status Constants

Use when working with engine instance APIs (InstanceDTO.status):

import {
  InstanceStatusCreating,
  InstanceStatusPendingProvider,
  InstanceStatusPending,
  InstanceStatusActive,
  InstanceStatusError,
  InstanceStatusDeleting,
  InstanceStatusDeleted,
} from '@inferencesh/sdk';

Tool Parameter Types

When building AgentTool schemas manually (outside the tool builder), use ToolParamType* for JSON Schema type fields:

import {
  ToolParamTypeObject,
  ToolParamTypeString,
  ToolParamTypeInteger,
  ToolParamTypeNumber,
  ToolParamTypeBoolean,
  ToolParamTypeArray,
  ToolParamTypeNull,
} from '@inferencesh/sdk';

const schema = {
  type: ToolParamTypeObject,
  properties: {
    city: { type: ToolParamTypeString, description: 'City name' },
  },
  required: ['city'],
};

The fluent tool builder (string(), number(), object(), …) infers these types automatically.

TypeScript Support

This SDK is written in TypeScript and includes full type definitions. All types are exported:

import type {
  Task,
  ApiAppRunRequest,
  RunOptions,
  CredentialDTO,
  AgentTool,
} from '@inferencesh/sdk';

Requirements

  • Node.js 18.0.0 or higher
  • Modern browsers with fetch support

resources

  • documentation — getting started guides and api reference
  • blog — tutorials on ai agents, image generation, and more
  • app store — browse ai models
  • discord — community support
  • github — open source projects

license

MIT © inference.sh

版本与发布记录

@inferencesh/sdk 共发布 122 个版本,首次发布于 2025/12/01;过去 12 个月发布了 122 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.38.4latest2026/11/012 天前
0.38.32026/11/012 天前
0.38.22026/11/012 天前
0.38.12026/10/313 天前
0.38.02026/10/295 天前
0.37.02026/10/295 天前

关于 @inferencesh/sdk 的常见问题

@inferencesh/sdk 是什么?

用于inference.sh的官方JavaScript/TypeScript SDK -使用简单的API运行AI模型

如何安装 @inferencesh/sdk?

在项目目录运行 npm install @inferencesh/sdk,或使用 pnpm add / yarn add / bun add。

@inferencesh/sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@inferencesh/sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@inferencesh/sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@inferencesh/sdk 可以商用吗?

@inferencesh/sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@inferencesh/sdk 还在维护吗?

最新版本 0.38.4 发布于 2026/11/01(2 天前);至今共发布 122 个版本,其中过去 12 个月发布了 122 个;首次发布于 2025/12/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。