| title | TypeScript |
|---|
import { Callout } from 'fumadocs-ui/components/callout' import { Card, Cards } from 'fumadocs-ui/components/card' import { Step, Steps } from 'fumadocs-ui/components/steps' import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
El SDK oficial de TypeScript/JavaScript para Sim proporciona seguridad de tipos completa y es compatible tanto con entornos Node.js como con navegadores, lo que te permite ejecutar flujos de trabajo programáticamente desde tus aplicaciones Node.js, aplicaciones web y otros entornos JavaScript.
El SDK de TypeScript proporciona seguridad de tipos completa, soporte para ejecución asíncrona, limitación automática de tasa con retroceso exponencial y seguimiento de uso.Instala el SDK usando tu gestor de paquetes preferido:
<Tabs items={['npm', 'yarn', 'bun']}>
```bash
npm install simstudio-ts-sdk
```
```bash
yarn add simstudio-ts-sdk
```
```bash
bun add simstudio-ts-sdk
```
Aquí tienes un ejemplo sencillo para empezar:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Initialize the client
const client = new SimStudioClient({
apiKey: 'your-api-key-here',
baseUrl: 'https://sim.ai' // optional, defaults to https://sim.ai
});
// Execute a workflow
try {
const result = await client.executeWorkflow('workflow-id');
console.log('Workflow executed successfully:', result);
} catch (error) {
console.error('Workflow execution failed:', error);
}new SimStudioClient(config: SimStudioConfig)Configuración:
config.apiKey(string): Tu clave API de Simconfig.baseUrl(string, opcional): URL base para la API de Sim (por defecto eshttps://sim.ai)
Ejecuta un flujo de trabajo con datos de entrada opcionales.
const result = await client.executeWorkflow('workflow-id', {
input: { message: 'Hello, world!' },
timeout: 30000 // 30 seconds
});Parámetros:
workflowId(string): El ID del flujo de trabajo a ejecutaroptions(ExecutionOptions, opcional):input(any): Datos de entrada para pasar al flujo de trabajotimeout(number): Tiempo de espera en milisegundos (predeterminado: 30000)stream(boolean): Habilitar respuestas en streaming (predeterminado: false)selectedOutputs(string[]): Salidas de bloques para transmitir en formatoblockName.attribute(p. ej.,["agent1.content"])async(boolean): Ejecutar de forma asíncrona (predeterminado: false)
Devuelve: Promise<WorkflowExecutionResult | AsyncExecutionResult>
Cuando async: true, devuelve inmediatamente un ID de tarea para consultar. De lo contrario, espera hasta completarse.
Obtener el estado de un flujo de trabajo (estado de implementación, etc.).
const status = await client.getWorkflowStatus('workflow-id');
console.log('Is deployed:', status.isDeployed);Parámetros:
workflowId(string): El ID del flujo de trabajo
Devuelve: Promise<WorkflowStatus>
Validar que un flujo de trabajo está listo para su ejecución.
const isReady = await client.validateWorkflow('workflow-id');
if (isReady) {
// Workflow is deployed and ready
}Parámetros:
workflowId(string): El ID del flujo de trabajo
Devuelve: Promise<boolean>
Obtener el estado de una ejecución de trabajo asíncrono.
const status = await client.getJobStatus('task-id-from-async-execution');
console.log('Status:', status.status); // 'queued', 'processing', 'completed', 'failed'
if (status.status === 'completed') {
console.log('Output:', status.output);
}Parámetros:
taskId(string): El ID de tarea devuelto por la ejecución asíncrona
Devuelve: Promise<JobStatus>
Campos de respuesta:
success(boolean): Si la solicitud fue exitosataskId(string): El ID de la tareastatus(string): Uno de'queued','processing','completed','failed','cancelled'metadata(object): ContienestartedAt,completedAt, ydurationoutput(any, opcional): La salida del flujo de trabajo (cuando se completa)error(any, opcional): Detalles del error (cuando falla)estimatedDuration(number, opcional): Duración estimada en milisegundos (cuando está procesando/en cola)
Ejecutar un flujo de trabajo con reintento automático en errores de límite de tasa usando retroceso exponencial.
const result = await client.executeWithRetry('workflow-id', {
input: { message: 'Hello' },
timeout: 30000
}, {
maxRetries: 3, // Maximum number of retries
initialDelay: 1000, // Initial delay in ms (1 second)
maxDelay: 30000, // Maximum delay in ms (30 seconds)
backoffMultiplier: 2 // Exponential backoff multiplier
});Parámetros:
workflowId(string): El ID del flujo de trabajo a ejecutaroptions(ExecutionOptions, opcional): Igual queexecuteWorkflow()retryOptions(RetryOptions, opcional):maxRetries(number): Número máximo de reintentos (predeterminado: 3)initialDelay(number): Retraso inicial en ms (predeterminado: 1000)maxDelay(number): Retraso máximo en ms (predeterminado: 30000)backoffMultiplier(number): Multiplicador de retroceso (predeterminado: 2)
Devuelve: Promise<WorkflowExecutionResult | AsyncExecutionResult>
La lógica de reintento utiliza retroceso exponencial (1s → 2s → 4s → 8s...) con fluctuación de ±25% para evitar el efecto de manada. Si la API proporciona una cabecera retry-after, se utilizará en su lugar.
Obtiene la información actual del límite de tasa de la última respuesta de la API.
const rateLimitInfo = client.getRateLimitInfo();
if (rateLimitInfo) {
console.log('Limit:', rateLimitInfo.limit);
console.log('Remaining:', rateLimitInfo.remaining);
console.log('Reset:', new Date(rateLimitInfo.reset * 1000));
}Devuelve: RateLimitInfo | null
Obtiene los límites de uso actuales y la información de cuota para tu cuenta.
const limits = await client.getUsageLimits();
console.log('Sync requests remaining:', limits.rateLimit.sync.remaining);
console.log('Async requests remaining:', limits.rateLimit.async.remaining);
console.log('Current period cost:', limits.usage.currentPeriodCost);
console.log('Plan:', limits.usage.plan);Devuelve: Promise<UsageLimits>
Estructura de respuesta:
{
success: boolean
rateLimit: {
sync: {
isLimited: boolean
limit: number
remaining: number
resetAt: string
}
async: {
isLimited: boolean
limit: number
remaining: number
resetAt: string
}
authType: string // 'api' or 'manual'
}
usage: {
currentPeriodCost: number
limit: number
plan: string // e.g., 'free', 'pro'
}
}Actualiza la clave de API.
client.setApiKey('new-api-key');Actualiza la URL base.
client.setBaseUrl('https://my-custom-domain.com');interface WorkflowExecutionResult {
success: boolean;
output?: any;
error?: string;
logs?: any[];
metadata?: {
duration?: number;
executionId?: string;
[key: string]: any;
};
traceSpans?: any[];
totalDuration?: number;
}interface AsyncExecutionResult {
success: boolean;
taskId: string;
status: 'queued';
createdAt: string;
links: {
status: string; // e.g., "/api/jobs/{taskId}"
};
}interface WorkflowStatus {
isDeployed: boolean;
deployedAt?: string;
needsRedeployment: boolean;
}interface RateLimitInfo {
limit: number;
remaining: number;
reset: number;
retryAfter?: number;
}interface UsageLimits {
success: boolean;
rateLimit: {
sync: {
isLimited: boolean;
limit: number;
remaining: number;
resetAt: string;
};
async: {
isLimited: boolean;
limit: number;
remaining: number;
resetAt: string;
};
authType: string;
};
usage: {
currentPeriodCost: number;
limit: number;
plan: string;
};
}class SimStudioError extends Error {
code?: string;
status?: number;
}Códigos de error comunes:
UNAUTHORIZED: Clave API inválidaTIMEOUT: Tiempo de espera agotadoRATE_LIMIT_EXCEEDED: Límite de tasa excedidoUSAGE_LIMIT_EXCEEDED: Límite de uso excedidoEXECUTION_ERROR: Ejecución del flujo de trabajo fallida
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
try {
// Check if workflow is ready
const isReady = await client.validateWorkflow('my-workflow-id');
if (!isReady) {
throw new Error('Workflow is not deployed or ready');
}
// Execute the workflow
const result = await client.executeWorkflow('my-workflow-id', {
input: {
message: 'Process this data',
userId: '12345'
}
});
if (result.success) {
console.log('Output:', result.output);
console.log('Duration:', result.metadata?.duration);
} else {
console.error('Workflow failed:', result.error);
}
} catch (error) {
console.error('Error:', error);
}
}
runWorkflow();Maneja diferentes tipos de errores que pueden ocurrir durante la ejecución del flujo de trabajo:
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
try {
const result = await client.executeWorkflow('workflow-id');
return result;
} catch (error) {
if (error instanceof SimStudioError) {
switch (error.code) {
case 'UNAUTHORIZED':
console.error('Invalid API key');
break;
case 'TIMEOUT':
console.error('Workflow execution timed out');
break;
case 'USAGE_LIMIT_EXCEEDED':
console.error('Usage limit exceeded');
break;
case 'INVALID_JSON':
console.error('Invalid JSON in request body');
break;
default:
console.error('Workflow error:', error.message);
}
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}Configura el cliente usando variables de entorno:
<Tabs items={['Development', 'Production']}>
```typescript
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIM_BASE_URL // optional
});
```
```typescript
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
Integración con un servidor Express.js:
import express from 'express';
import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
app.post('/execute-workflow', async (req, res) => {
try {
const { workflowId, input } = req.body;
const result = await client.executeWorkflow(workflowId, {
input,
timeout: 60000
});
res.json({
success: true,
data: result
});
} catch (error) {
console.error('Workflow execution error:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Uso con rutas API de Next.js:
// pages/api/workflow.ts or app/api/workflow/route.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { workflowId, input } = req.body;
const result = await client.executeWorkflow(workflowId, {
input,
timeout: 30000
});
res.status(200).json(result);
} catch (error) {
console.error('Error executing workflow:', error);
res.status(500).json({
error: 'Failed to execute workflow'
});
}
}Uso en el navegador (con configuración CORS adecuada):
import { SimStudioClient } from 'simstudio-ts-sdk';
// Note: In production, use a proxy server to avoid exposing API keys
const client = new SimStudioClient({
apiKey: 'your-public-api-key', // Use with caution in browser
baseUrl: 'https://sim.ai'
});
async function executeClientSideWorkflow() {
try {
const result = await client.executeWorkflow('workflow-id', {
input: {
userInput: 'Hello from browser'
}
});
console.log('Workflow result:', result);
// Update UI with result
document.getElementById('result')!.textContent =
JSON.stringify(result.output, null, 2);
} catch (error) {
console.error('Error:', error);
}
}Los objetos File son detectados automáticamente y convertidos a formato base64. Inclúyelos en tu entrada bajo el nombre de campo que coincida con el formato de entrada del disparador API de tu flujo de trabajo.
El SDK convierte los objetos File a este formato:
{
type: 'file',
data: 'data:mime/type;base64,base64data',
name: 'filename',
mime: 'mime/type'
}Alternativamente, puedes proporcionar archivos manualmente usando el formato URL:
{
type: 'url',
data: 'https://example.com/file.pdf',
name: 'file.pdf',
mime: 'application/pdf'
}<Tabs items={['Browser', 'Node.js']}>
```typescript
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
// From file input
async function handleFileUpload(event: Event) {
const input = event.target as HTMLInputElement;
const files = Array.from(input.files || []);
// Include files under the field name from your API trigger's input format
const result = await client.executeWorkflow('workflow-id', {
input: {
documents: files, // Must match your workflow's "files" field name
instructions: 'Analyze these documents'
}
});
console.log('Result:', result);
}
```
```typescript
import { SimStudioClient } from 'simstudio-ts-sdk';
import fs from 'fs';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
// Read file and create File object
const fileBuffer = fs.readFileSync('./document.pdf');
const file = new File([fileBuffer], 'document.pdf', {
type: 'application/pdf'
});
// Include files under the field name from your API trigger's input format
const result = await client.executeWorkflow('workflow-id', {
input: {
documents: [file], // Must match your workflow's "files" field name
query: 'Summarize this document'
}
});
```
Crea un hook personalizado de React para la ejecución de flujos de trabajo:
import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
interface UseWorkflowResult {
result: WorkflowExecutionResult | null;
loading: boolean;
error: Error | null;
executeWorkflow: (workflowId: string, input?: any) => Promise<void>;
}
export function useWorkflow(): UseWorkflowResult {
const [result, setResult] = useState<WorkflowExecutionResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const executeWorkflow = useCallback(async (workflowId: string, input?: any) => {
setLoading(true);
setError(null);
setResult(null);
try {
const workflowResult = await client.executeWorkflow(workflowId, {
input,
timeout: 30000
});
setResult(workflowResult);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
}, []);
return {
result,
loading,
error,
executeWorkflow
};
}
// Usage in component
function WorkflowComponent() {
const { result, loading, error, executeWorkflow } = useWorkflow();
const handleExecute = () => {
executeWorkflow('my-workflow-id', {
message: 'Hello from React!'
});
};
return (
<div>
<button onClick={handleExecute} disabled={loading}>
{loading ? 'Executing...' : 'Execute Workflow'}
</button>
{error && <div>Error: {error.message}</div>}
{result && (
<div>
<h3>Result:</h3>
<pre>{JSON.stringify(result, null, 2)}</pre>
</div>
)}
</div>
);
}Ejecuta flujos de trabajo de forma asíncrona para tareas de larga duración:
import { SimStudioClient, AsyncExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeAsync() {
try {
// Start async execution
const result = await client.executeWorkflow('workflow-id', {
input: { data: 'large dataset' },
async: true // Execute asynchronously
});
// Check if result is an async execution
if ('taskId' in result) {
console.log('Task ID:', result.taskId);
console.log('Status endpoint:', result.links.status);
// Poll for completion
let status = await client.getJobStatus(result.taskId);
while (status.status === 'queued' || status.status === 'processing') {
console.log('Current status:', status.status);
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
status = await client.getJobStatus(result.taskId);
}
if (status.status === 'completed') {
console.log('Workflow completed!');
console.log('Output:', status.output);
console.log('Duration:', status.metadata.duration);
} else {
console.error('Workflow failed:', status.error);
}
}
} catch (error) {
console.error('Error:', error);
}
}
executeAsync();Maneja los límites de tasa automáticamente con retroceso exponencial:
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeWithRetryHandling() {
try {
// Automatically retries on rate limit
const result = await client.executeWithRetry('workflow-id', {
input: { message: 'Process this' }
}, {
maxRetries: 5,
initialDelay: 1000,
maxDelay: 60000,
backoffMultiplier: 2
});
console.log('Success:', result);
} catch (error) {
if (error instanceof SimStudioError && error.code === 'RATE_LIMIT_EXCEEDED') {
console.error('Rate limit exceeded after all retries');
// Check rate limit info
const rateLimitInfo = client.getRateLimitInfo();
if (rateLimitInfo) {
console.log('Rate limit resets at:', new Date(rateLimitInfo.reset * 1000));
}
}
}
}Monitorea el uso y los límites de tu cuenta:
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function checkUsage() {
try {
const limits = await client.getUsageLimits();
console.log('=== Rate Limits ===');
console.log('Sync requests:');
console.log(' Limit:', limits.rateLimit.sync.limit);
console.log(' Remaining:', limits.rateLimit.sync.remaining);
console.log(' Resets at:', limits.rateLimit.sync.resetAt);
console.log(' Is limited:', limits.rateLimit.sync.isLimited);
console.log('\nAsync requests:');
console.log(' Limit:', limits.rateLimit.async.limit);
console.log(' Remaining:', limits.rateLimit.async.remaining);
console.log(' Resets at:', limits.rateLimit.async.resetAt);
console.log(' Is limited:', limits.rateLimit.async.isLimited);
console.log('\n=== Usage ===');
console.log('Current period cost: $' + limits.usage.currentPeriodCost.toFixed(2));
console.log('Limit: $' + limits.usage.limit.toFixed(2));
console.log('Plan:', limits.usage.plan);
const percentUsed = (limits.usage.currentPeriodCost / limits.usage.limit) * 100;
console.log('Usage: ' + percentUsed.toFixed(1) + '%');
if (percentUsed > 80) {
console.warn('⚠️ Warning: You are approaching your usage limit!');
}
} catch (error) {
console.error('Error checking usage:', error);
}
}
checkUsage();Ejecuta flujos de trabajo con respuestas en streaming en tiempo real:
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIM_API_KEY!
});
async function executeWithStreaming() {
try {
// Enable streaming for specific block outputs
const result = await client.executeWorkflow('workflow-id', {
input: { message: 'Count to five' },
stream: true,
selectedOutputs: ['agent1.content'] // Use blockName.attribute format
});
console.log('Workflow result:', result);
} catch (error) {
console.error('Error:', error);
}
}La respuesta en streaming sigue el formato de eventos enviados por el servidor (SSE):
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"}
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":", two"}
data: {"event":"done","success":true,"output":{},"metadata":{"duration":610}}
data: [DONE]
Ejemplo de streaming en React:
import { useState, useEffect } from 'react';
function StreamingWorkflow() {
const [output, setOutput] = useState('');
const [loading, setLoading] = useState(false);
const executeStreaming = async () => {
setLoading(true);
setOutput('');
// IMPORTANT: Make this API call from your backend server, not the browser
// Never expose your API key in client-side code
const response = await fetch('https://sim.ai/api/workflows/WORKFLOW_ID/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.SIM_API_KEY! // Server-side environment variable only
},
body: JSON.stringify({
message: 'Generate a story',
stream: true,
selectedOutputs: ['agent1.content']
})
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (reader) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
setLoading(false);
break;
}
try {
const parsed = JSON.parse(data);
if (parsed.chunk) {
setOutput(prev => prev + parsed.chunk);
} else if (parsed.event === 'done') {
console.log('Execution complete:', parsed.metadata);
}
} catch (e) {
// Skip invalid JSON
}
}
}
}
};
return (
<div>
<button onClick={executeStreaming} disabled={loading}>
{loading ? 'Generating...' : 'Start Streaming'}
</button>
<div style={{ whiteSpace: 'pre-wrap' }}>{output}</div>
</div>
);
}- Node.js 16+
- TypeScript 5.0+ (para proyectos TypeScript)
Apache-2.0