feat(chat-streaming): added a stream option to workflow execute route, updated SDKs, updated docs (#1565)

* feat(chat-stream): updated workflow id execute route to support streaming via API

* enable streaming via api

* added only text stream option

* cleanup deployed preview componnet

* updated selectedOutputIds to selectedOutput

* updated TS and Python SDKs with async, rate limits, usage, and streaming API routes

* stream non-streaming blocks when streaming is specified

* fix(chat-panel): add onBlockComplete handler to chat panel to stream back blocks as they complete

* update docs

* cleanup

* ack PR comments

* updated next config

* removed getAssetUrl in favor of local assets

* resolve merge conflicts

* remove extra logic to create sensitive result

* simplify internal auth

* remove vercel blob from CSP + next config
This commit is contained in:
Waleed
2025-10-07 15:10:37 -07:00
committed by GitHub
parent a63a7b0262
commit 872e034312
73 changed files with 3662 additions and 1618 deletions

View File

@@ -1,7 +1,7 @@
'use client'
import { useEffect, useRef } from 'react'
import { getVideoUrl } from '@/lib/utils'
import { getAssetUrl } from '@/lib/utils'
interface LightboxProps {
isOpen: boolean
@@ -60,7 +60,7 @@ export function Lightbox({ isOpen, onClose, src, alt, type }: LightboxProps) {
/>
) : (
<video
src={getVideoUrl(src)}
src={getAssetUrl(src)}
autoPlay
loop
muted

View File

@@ -1,7 +1,7 @@
'use client'
import { useState } from 'react'
import { getVideoUrl } from '@/lib/utils'
import { getAssetUrl } from '@/lib/utils'
import { Lightbox } from './lightbox'
interface VideoProps {
@@ -39,7 +39,7 @@ export function Video({
muted={muted}
playsInline={playsInline}
className={`${className} ${enableLightbox ? 'cursor-pointer transition-opacity hover:opacity-90' : ''}`}
src={getVideoUrl(src)}
src={getAssetUrl(src)}
onClick={handleVideoClick}
/>

View File

@@ -214,7 +214,7 @@ class SimStudioError(Exception):
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def run_workflow():
try:
@@ -252,7 +252,7 @@ Behandeln Sie verschiedene Fehlertypen, die während der Workflow-Ausführung au
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
@@ -284,7 +284,7 @@ from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
@@ -298,7 +298,7 @@ Führen Sie mehrere Workflows effizient aus:
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
@@ -352,8 +352,8 @@ Konfigurieren Sie den Client mit Umgebungsvariablen:
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIMSTUDIO_API_KEY"),
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
api_key=os.getenv("SIM_API_KEY"),
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
@@ -365,13 +365,13 @@ Konfigurieren Sie den Client mit Umgebungsvariablen:
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIMSTUDIO_API_KEY")
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIMSTUDIO_API_KEY environment variable is required")
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```

View File

@@ -230,7 +230,7 @@ class SimStudioError extends Error {
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
@@ -271,7 +271,7 @@ Behandeln Sie verschiedene Fehlertypen, die während der Workflow-Ausführung au
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
@@ -315,14 +315,14 @@ Konfigurieren Sie den Client mit Umgebungsvariablen:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL // optional
baseUrl: process.env.SIM_BASE_URL // optional
});
```
@@ -333,14 +333,14 @@ Konfigurieren Sie den Client mit Umgebungsvariablen:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL || 'https://sim.ai'
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
@@ -357,7 +357,7 @@ import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
@@ -399,7 +399,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
@@ -476,7 +476,7 @@ import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIMSTUDIO_API_KEY!
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
interface UseWorkflowResult {
@@ -588,7 +588,7 @@ import {
// Type-safe client initialization
const client: SimStudioClient = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
// Type-safe workflow execution

View File

@@ -10,7 +10,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
The official Python SDK for Sim allows you to execute workflows programmatically from your Python applications using the official Python SDK.
<Callout type="info">
The Python SDK supports Python 3.8+ and provides synchronous workflow execution. All workflow executions are currently synchronous.
The Python SDK supports Python 3.8+ with async execution support, automatic rate limiting with exponential backoff, and usage tracking.
</Callout>
## Installation
@@ -74,8 +74,13 @@ result = client.execute_workflow(
- `workflow_id` (str): The ID of the workflow to execute
- `input_data` (dict, optional): Input data to pass to the workflow
- `timeout` (float, optional): Timeout in seconds (default: 30.0)
- `stream` (bool, optional): Enable streaming responses (default: False)
- `selected_outputs` (list[str], optional): Block outputs to stream in `blockName.attribute` format (e.g., `["agent1.content"]`)
- `async_execution` (bool, optional): Execute asynchronously (default: False)
**Returns:** `WorkflowExecutionResult`
**Returns:** `WorkflowExecutionResult | AsyncExecutionResult`
When `async_execution=True`, returns immediately with a task ID for polling. Otherwise, waits for completion.
##### get_workflow_status()
@@ -107,28 +112,117 @@ if is_ready:
**Returns:** `bool`
##### execute_workflow_sync()
##### get_job_status()
<Callout type="info">
Currently, this method is identical to `execute_workflow()` since all executions are synchronous. This method is provided for future compatibility when asynchronous execution is added.
</Callout>
Execute a workflow (currently synchronous, same as `execute_workflow()`).
Get the status of an async job execution.
```python
result = client.execute_workflow_sync(
status = client.get_job_status("task-id-from-async-execution")
print("Status:", status["status"]) # 'queued', 'processing', 'completed', 'failed'
if status["status"] == "completed":
print("Output:", status["output"])
```
**Parameters:**
- `task_id` (str): The task ID returned from async execution
**Returns:** `Dict[str, Any]`
**Response fields:**
- `success` (bool): Whether the request was successful
- `taskId` (str): The task ID
- `status` (str): One of `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
- `metadata` (dict): Contains `startedAt`, `completedAt`, and `duration`
- `output` (any, optional): The workflow output (when completed)
- `error` (any, optional): Error details (when failed)
- `estimatedDuration` (int, optional): Estimated duration in milliseconds (when processing/queued)
##### execute_with_retry()
Execute a workflow with automatic retry on rate limit errors using exponential backoff.
```python
result = client.execute_with_retry(
"workflow-id",
input_data={"data": "some input"},
timeout=60.0
input_data={"message": "Hello"},
timeout=30.0,
max_retries=3, # Maximum number of retries
initial_delay=1.0, # Initial delay in seconds
max_delay=30.0, # Maximum delay in seconds
backoff_multiplier=2.0 # Exponential backoff multiplier
)
```
**Parameters:**
- `workflow_id` (str): The ID of the workflow to execute
- `input_data` (dict, optional): Input data to pass to the workflow
- `timeout` (float): Timeout for the initial request in seconds
- `timeout` (float, optional): Timeout in seconds
- `stream` (bool, optional): Enable streaming responses
- `selected_outputs` (list, optional): Block outputs to stream
- `async_execution` (bool, optional): Execute asynchronously
- `max_retries` (int, optional): Maximum number of retries (default: 3)
- `initial_delay` (float, optional): Initial delay in seconds (default: 1.0)
- `max_delay` (float, optional): Maximum delay in seconds (default: 30.0)
- `backoff_multiplier` (float, optional): Backoff multiplier (default: 2.0)
**Returns:** `WorkflowExecutionResult`
**Returns:** `WorkflowExecutionResult | AsyncExecutionResult`
The retry logic uses exponential backoff (1s → 2s → 4s → 8s...) with ±25% jitter to prevent thundering herd. If the API provides a `retry-after` header, it will be used instead.
##### get_rate_limit_info()
Get the current rate limit information from the last API response.
```python
rate_limit_info = client.get_rate_limit_info()
if rate_limit_info:
print("Limit:", rate_limit_info.limit)
print("Remaining:", rate_limit_info.remaining)
print("Reset:", datetime.fromtimestamp(rate_limit_info.reset))
```
**Returns:** `RateLimitInfo | None`
##### get_usage_limits()
Get current usage limits and quota information for your account.
```python
limits = client.get_usage_limits()
print("Sync requests remaining:", limits.rate_limit["sync"]["remaining"])
print("Async requests remaining:", limits.rate_limit["async"]["remaining"])
print("Current period cost:", limits.usage["currentPeriodCost"])
print("Plan:", limits.usage["plan"])
```
**Returns:** `UsageLimits`
**Response structure:**
```python
{
"success": bool,
"rateLimit": {
"sync": {
"isLimited": bool,
"limit": int,
"remaining": int,
"resetAt": str
},
"async": {
"isLimited": bool,
"limit": int,
"remaining": int,
"resetAt": str
},
"authType": str # 'api' or 'manual'
},
"usage": {
"currentPeriodCost": float,
"limit": float,
"plan": str # e.g., 'free', 'pro'
}
}
```
##### set_api_key()
@@ -170,6 +264,18 @@ class WorkflowExecutionResult:
total_duration: Optional[float] = None
```
### AsyncExecutionResult
```python
@dataclass
class AsyncExecutionResult:
success: bool
task_id: str
status: str # 'queued'
created_at: str
links: Dict[str, str] # e.g., {"status": "/api/jobs/{taskId}"}
```
### WorkflowStatus
```python
@@ -181,6 +287,27 @@ class WorkflowStatus:
needs_redeployment: bool = False
```
### RateLimitInfo
```python
@dataclass
class RateLimitInfo:
limit: int
remaining: int
reset: int
retry_after: Optional[int] = None
```
### UsageLimits
```python
@dataclass
class UsageLimits:
success: bool
rate_limit: Dict[str, Any]
usage: Dict[str, Any]
```
### SimStudioError
```python
@@ -191,6 +318,13 @@ class SimStudioError(Exception):
self.status = status
```
**Common error codes:**
- `UNAUTHORIZED`: Invalid API key
- `TIMEOUT`: Request timed out
- `RATE_LIMIT_EXCEEDED`: Rate limit exceeded
- `USAGE_LIMIT_EXCEEDED`: Usage limit exceeded
- `EXECUTION_ERROR`: Workflow execution failed
## Examples
### Basic Workflow Execution
@@ -214,7 +348,7 @@ class SimStudioError(Exception):
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY
def run_workflow():
try:
@@ -252,7 +386,7 @@ Handle different types of errors that may occur during workflow execution:
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY")
def execute_with_error_handling():
try:
@@ -284,7 +418,7 @@ from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
@@ -298,7 +432,7 @@ Execute multiple workflows efficiently:
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY")
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
@@ -339,6 +473,230 @@ for result in results:
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
```
### Async Workflow Execution
Execute workflows asynchronously for long-running tasks:
```python
import os
import time
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_async():
try:
# Start async execution
result = client.execute_workflow(
"workflow-id",
input_data={"data": "large dataset"},
async_execution=True # Execute asynchronously
)
# Check if result is an async execution
if hasattr(result, 'task_id'):
print(f"Task ID: {result.task_id}")
print(f"Status endpoint: {result.links['status']}")
# Poll for completion
status = client.get_job_status(result.task_id)
while status["status"] in ["queued", "processing"]:
print(f"Current status: {status['status']}")
time.sleep(2) # Wait 2 seconds
status = client.get_job_status(result.task_id)
if status["status"] == "completed":
print("Workflow completed!")
print(f"Output: {status['output']}")
print(f"Duration: {status['metadata']['duration']}")
else:
print(f"Workflow failed: {status['error']}")
except Exception as error:
print(f"Error: {error}")
execute_async()
```
### Rate Limiting and Retry
Handle rate limits automatically with exponential backoff:
```python
import os
from simstudio import SimStudioClient, SimStudioError
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_retry_handling():
try:
# Automatically retries on rate limit
result = client.execute_with_retry(
"workflow-id",
input_data={"message": "Process this"},
max_retries=5,
initial_delay=1.0,
max_delay=60.0,
backoff_multiplier=2.0
)
print(f"Success: {result}")
except SimStudioError as error:
if error.code == "RATE_LIMIT_EXCEEDED":
print("Rate limit exceeded after all retries")
# Check rate limit info
rate_limit_info = client.get_rate_limit_info()
if rate_limit_info:
from datetime import datetime
reset_time = datetime.fromtimestamp(rate_limit_info.reset)
print(f"Rate limit resets at: {reset_time}")
execute_with_retry_handling()
```
### Usage Monitoring
Monitor your account usage and limits:
```python
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def check_usage():
try:
limits = client.get_usage_limits()
print("=== Rate Limits ===")
print("Sync requests:")
print(f" Limit: {limits.rate_limit['sync']['limit']}")
print(f" Remaining: {limits.rate_limit['sync']['remaining']}")
print(f" Resets at: {limits.rate_limit['sync']['resetAt']}")
print(f" Is limited: {limits.rate_limit['sync']['isLimited']}")
print("\nAsync requests:")
print(f" Limit: {limits.rate_limit['async']['limit']}")
print(f" Remaining: {limits.rate_limit['async']['remaining']}")
print(f" Resets at: {limits.rate_limit['async']['resetAt']}")
print(f" Is limited: {limits.rate_limit['async']['isLimited']}")
print("\n=== Usage ===")
print(f"Current period cost: ${limits.usage['currentPeriodCost']:.2f}")
print(f"Limit: ${limits.usage['limit']:.2f}")
print(f"Plan: {limits.usage['plan']}")
percent_used = (limits.usage['currentPeriodCost'] / limits.usage['limit']) * 100
print(f"Usage: {percent_used:.1f}%")
if percent_used > 80:
print("⚠️ Warning: You are approaching your usage limit!")
except Exception as error:
print(f"Error checking usage: {error}")
check_usage()
```
### Streaming Workflow Execution
Execute workflows with real-time streaming responses:
```python
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY")
def execute_with_streaming():
"""Execute workflow with streaming enabled."""
try:
# Enable streaming for specific block outputs
result = client.execute_workflow(
"workflow-id",
input_data={"message": "Count to five"},
stream=True,
selected_outputs=["agent1.content"] # Use blockName.attribute format
)
print("Workflow result:", result)
except Exception as error:
print("Error:", error)
execute_with_streaming()
```
The streaming response follows the Server-Sent Events (SSE) format:
```
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]
```
**Flask Streaming Example:**
```python
from flask import Flask, Response, stream_with_context
import requests
import json
import os
app = Flask(__name__)
@app.route('/stream-workflow')
def stream_workflow():
"""Stream workflow execution to the client."""
def generate():
response = requests.post(
'https://sim.ai/api/workflows/WORKFLOW_ID/execute',
headers={
'Content-Type': 'application/json',
'X-API-Key': os.getenv('SIM_API_KEY
},
json={
'message': 'Generate a story',
'stream': True,
'selectedOutputs': ['agent1.content']
},
stream=True
)
for line in response.iter_lines():
if line:
decoded_line = line.decode('utf-8')
if decoded_line.startswith('data: '):
data = decoded_line[6:] # Remove 'data: ' prefix
if data == '[DONE]':
break
try:
parsed = json.loads(data)
if 'chunk' in parsed:
yield f"data: {json.dumps(parsed)}\n\n"
elif parsed.get('event') == 'done':
yield f"data: {json.dumps(parsed)}\n\n"
print("Execution complete:", parsed.get('metadata'))
except json.JSONDecodeError:
pass
return Response(
stream_with_context(generate()),
mimetype='text/event-stream'
)
if __name__ == '__main__':
app.run(debug=True)
```
### Environment Configuration
Configure the client using environment variables:
@@ -351,8 +709,8 @@ Configure the client using environment variables:
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIMSTUDIO_API_KEY"),
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
api_key=os.getenv("SIM_API_KEY")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
</Tab>
@@ -362,13 +720,13 @@ Configure the client using environment variables:
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIMSTUDIO_API_KEY")
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIMSTUDIO_API_KEY environment variable is required")
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
</Tab>

View File

@@ -7,10 +7,10 @@ import { Card, Cards } from 'fumadocs-ui/components/card'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
The official TypeScript/JavaScript SDK for Sim provides full type safety and supports both Node.js and browser environments, allowing you to execute workflows programmatically from your Node.js applications, web applications, and other JavaScript environments. All workflow executions are currently synchronous.
The official TypeScript/JavaScript SDK for Sim provides full type safety and supports both Node.js and browser environments, allowing you to execute workflows programmatically from your Node.js applications, web applications, and other JavaScript environments.
<Callout type="info">
The TypeScript SDK provides full type safety and supports both Node.js and browser environments. All workflow executions are currently synchronous.
The TypeScript SDK provides full type safety, async execution support, automatic rate limiting with exponential backoff, and usage tracking.
</Callout>
## Installation
@@ -89,8 +89,13 @@ const result = await client.executeWorkflow('workflow-id', {
- `options` (ExecutionOptions, optional):
- `input` (any): Input data to pass to the workflow
- `timeout` (number): Timeout in milliseconds (default: 30000)
- `stream` (boolean): Enable streaming responses (default: false)
- `selectedOutputs` (string[]): Block outputs to stream in `blockName.attribute` format (e.g., `["agent1.content"]`)
- `async` (boolean): Execute asynchronously (default: false)
**Returns:** `Promise<WorkflowExecutionResult>`
**Returns:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
When `async: true`, returns immediately with a task ID for polling. Otherwise, waits for completion.
##### getWorkflowStatus()
@@ -122,28 +127,116 @@ if (isReady) {
**Returns:** `Promise<boolean>`
##### executeWorkflowSync()
##### getJobStatus()
<Callout type="info">
Currently, this method is identical to `executeWorkflow()` since all executions are synchronous. This method is provided for future compatibility when asynchronous execution is added.
</Callout>
Execute a workflow (currently synchronous, same as `executeWorkflow()`).
Get the status of an async job execution.
```typescript
const result = await client.executeWorkflowSync('workflow-id', {
input: { data: 'some input' },
timeout: 60000
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);
}
```
**Parameters:**
- `taskId` (string): The task ID returned from async execution
**Returns:** `Promise<JobStatus>`
**Response fields:**
- `success` (boolean): Whether the request was successful
- `taskId` (string): The task ID
- `status` (string): One of `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
- `metadata` (object): Contains `startedAt`, `completedAt`, and `duration`
- `output` (any, optional): The workflow output (when completed)
- `error` (any, optional): Error details (when failed)
- `estimatedDuration` (number, optional): Estimated duration in milliseconds (when processing/queued)
##### executeWithRetry()
Execute a workflow with automatic retry on rate limit errors using exponential backoff.
```typescript
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
});
```
**Parameters:**
- `workflowId` (string): The ID of the workflow to execute
- `options` (ExecutionOptions, optional):
- `input` (any): Input data to pass to the workflow
- `timeout` (number): Timeout for the initial request in milliseconds
- `options` (ExecutionOptions, optional): Same as `executeWorkflow()`
- `retryOptions` (RetryOptions, optional):
- `maxRetries` (number): Maximum number of retries (default: 3)
- `initialDelay` (number): Initial delay in ms (default: 1000)
- `maxDelay` (number): Maximum delay in ms (default: 30000)
- `backoffMultiplier` (number): Backoff multiplier (default: 2)
**Returns:** `Promise<WorkflowExecutionResult>`
**Returns:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
The retry logic uses exponential backoff (1s → 2s → 4s → 8s...) with ±25% jitter to prevent thundering herd. If the API provides a `retry-after` header, it will be used instead.
##### getRateLimitInfo()
Get the current rate limit information from the last API response.
```typescript
const rateLimitInfo = client.getRateLimitInfo();
if (rateLimitInfo) {
console.log('Limit:', rateLimitInfo.limit);
console.log('Remaining:', rateLimitInfo.remaining);
console.log('Reset:', new Date(rateLimitInfo.reset * 1000));
}
```
**Returns:** `RateLimitInfo | null`
##### getUsageLimits()
Get current usage limits and quota information for your account.
```typescript
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);
```
**Returns:** `Promise<UsageLimits>`
**Response structure:**
```typescript
{
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'
}
}
```
##### setApiKey()
@@ -181,6 +274,20 @@ interface WorkflowExecutionResult {
}
```
### AsyncExecutionResult
```typescript
interface AsyncExecutionResult {
success: boolean;
taskId: string;
status: 'queued';
createdAt: string;
links: {
status: string; // e.g., "/api/jobs/{taskId}"
};
}
```
### WorkflowStatus
```typescript
@@ -192,6 +299,45 @@ interface WorkflowStatus {
}
```
### RateLimitInfo
```typescript
interface RateLimitInfo {
limit: number;
remaining: number;
reset: number;
retryAfter?: number;
}
```
### UsageLimits
```typescript
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;
};
}
```
### SimStudioError
```typescript
@@ -201,6 +347,13 @@ class SimStudioError extends Error {
}
```
**Common error codes:**
- `UNAUTHORIZED`: Invalid API key
- `TIMEOUT`: Request timed out
- `RATE_LIMIT_EXCEEDED`: Rate limit exceeded
- `USAGE_LIMIT_EXCEEDED`: Usage limit exceeded
- `EXECUTION_ERROR`: Workflow execution failed
## Examples
### Basic Workflow Execution
@@ -224,7 +377,7 @@ class SimStudioError extends Error {
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
@@ -265,7 +418,7 @@ Handle different types of errors that may occur during workflow execution:
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
@@ -308,14 +461,14 @@ Configure the client using environment variables:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL // optional
baseUrl: process.env.SIM_BASE_URL // optional
});
```
</Tab>
@@ -324,14 +477,14 @@ Configure the client using environment variables:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL || 'https://sim.ai'
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
</Tab>
@@ -347,7 +500,7 @@ import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
@@ -389,7 +542,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
@@ -466,7 +619,7 @@ import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
interface UseWorkflowResult {
@@ -522,7 +675,7 @@ function WorkflowComponent() {
<button onClick={handleExecute} disabled={loading}>
{loading ? 'Executing...' : 'Execute Workflow'}
</button>
{error && <div>Error: {error.message}</div>}
{result && (
<div>
@@ -535,6 +688,251 @@ function WorkflowComponent() {
}
```
### Async Workflow Execution
Execute workflows asynchronously for long-running tasks:
```typescript
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();
```
### Rate Limiting and Retry
Handle rate limits automatically with exponential backoff:
```typescript
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));
}
}
}
}
```
### Usage Monitoring
Monitor your account usage and limits:
```typescript
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();
```
### Streaming Workflow Execution
Execute workflows with real-time streaming responses:
```typescript
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);
}
}
```
The streaming response follows the Server-Sent Events (SSE) format:
```
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]
```
**React Streaming Example:**
```typescript
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>
);
}
```
## Getting Your API Key
<Steps>
@@ -578,7 +976,7 @@ import {
// Type-safe client initialization
const client: SimStudioClient = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
// Type-safe workflow execution

View File

@@ -38,6 +38,84 @@ curl -X POST \
Successful responses return the serialized execution result from the Executor. Errors surface validation, auth, or workflow failures.
## Streaming Responses
Enable real-time streaming to receive workflow output as it's generated, character-by-character. This is useful for displaying AI responses progressively to users.
### Request Parameters
Add these parameters to enable streaming:
- `stream` - Set to `true` to enable Server-Sent Events (SSE) streaming
- `selectedOutputs` - Array of block outputs to stream (e.g., `["agent1.content"]`)
### Block Output Format
Use the `blockName.attribute` format to specify which block outputs to stream:
- Format: `"blockName.attribute"` (e.g., If you want to stream the content of the Agent 1 block, you would use `"agent1.content"`)
- Block names are case-insensitive and spaces are ignored
### Example Request
```bash
curl -X POST \
https://sim.ai/api/workflows/WORKFLOW_ID/execute \
-H 'Content-Type: application/json' \
-H 'X-API-Key: YOUR_KEY' \
-d '{
"message": "Count to five",
"stream": true,
"selectedOutputs": ["agent1.content"]
}'
```
### Response Format
Streaming responses use Server-Sent Events (SSE) format:
```
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"}
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":", two"}
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":", three"}
data: {"event":"done","success":true,"output":{},"metadata":{"duration":610}}
data: [DONE]
```
Each event includes:
- **Streaming chunks**: `{"blockId": "...", "chunk": "text"}` - Real-time text as it's generated
- **Final event**: `{"event": "done", ...}` - Execution metadata and complete results
- **Terminator**: `[DONE]` - Signals end of stream
### Multiple Block Streaming
When `selectedOutputs` includes multiple blocks, each chunk indicates which block produced it:
```bash
curl -X POST \
https://sim.ai/api/workflows/WORKFLOW_ID/execute \
-H 'Content-Type: application/json' \
-H 'X-API-Key: YOUR_KEY' \
-d '{
"message": "Process this request",
"stream": true,
"selectedOutputs": ["agent1.content", "agent2.content"]
}'
```
The `blockId` field in each chunk lets you route output to the correct UI element:
```
data: {"blockId":"agent1-uuid","chunk":"Processing..."}
data: {"blockId":"agent2-uuid","chunk":"Analyzing..."}
data: {"blockId":"agent1-uuid","chunk":" complete"}
```
## Output Reference
| Reference | Description |

View File

@@ -214,7 +214,7 @@ class SimStudioError(Exception):
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def run_workflow():
try:
@@ -252,7 +252,7 @@ Maneja diferentes tipos de errores que pueden ocurrir durante la ejecución del
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
@@ -284,7 +284,7 @@ from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
@@ -298,7 +298,7 @@ Ejecuta múltiples flujos de trabajo de manera eficiente:
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
@@ -352,8 +352,8 @@ Configura el cliente usando variables de entorno:
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIMSTUDIO_API_KEY"),
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
api_key=os.getenv("SIM_API_KEY"),
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
@@ -365,13 +365,13 @@ Configura el cliente usando variables de entorno:
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIMSTUDIO_API_KEY")
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIMSTUDIO_API_KEY environment variable is required")
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```

View File

@@ -230,7 +230,7 @@ class SimStudioError extends Error {
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
@@ -271,7 +271,7 @@ Maneja diferentes tipos de errores que pueden ocurrir durante la ejecución del
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
@@ -315,14 +315,14 @@ Configura el cliente usando variables de entorno:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL // optional
baseUrl: process.env.SIM_BASE_URL // optional
});
```
@@ -333,14 +333,14 @@ Configura el cliente usando variables de entorno:
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL || 'https://sim.ai'
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
@@ -357,7 +357,7 @@ import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
@@ -399,7 +399,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
@@ -476,7 +476,7 @@ import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIMSTUDIO_API_KEY!
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
interface UseWorkflowResult {
@@ -588,7 +588,7 @@ import {
// Type-safe client initialization
const client: SimStudioClient = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
// Type-safe workflow execution

View File

@@ -214,7 +214,7 @@ class SimStudioError(Exception):
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def run_workflow():
try:
@@ -252,7 +252,7 @@ Gérez différents types d'erreurs qui peuvent survenir pendant l'exécution du
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
@@ -284,7 +284,7 @@ from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
@@ -298,7 +298,7 @@ Exécutez plusieurs flux de travail efficacement :
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
@@ -352,8 +352,8 @@ Configurez le client en utilisant des variables d'environnement :
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIMSTUDIO_API_KEY"),
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
api_key=os.getenv("SIM_API_KEY"),
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
@@ -365,13 +365,13 @@ Configurez le client en utilisant des variables d'environnement :
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIMSTUDIO_API_KEY")
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIMSTUDIO_API_KEY environment variable is required")
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```

View File

@@ -230,7 +230,7 @@ class SimStudioError extends Error {
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
@@ -271,7 +271,7 @@ Gérez différents types d'erreurs qui peuvent survenir pendant l'exécution du
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
@@ -315,14 +315,14 @@ Configurez le client en utilisant des variables d'environnement :
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL // optional
baseUrl: process.env.SIM_BASE_URL // optional
});
```
@@ -333,14 +333,14 @@ Configurez le client en utilisant des variables d'environnement :
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL || 'https://sim.ai'
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
@@ -357,7 +357,7 @@ import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
@@ -399,7 +399,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
@@ -476,7 +476,7 @@ import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIMSTUDIO_API_KEY!
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
interface UseWorkflowResult {
@@ -588,7 +588,7 @@ import {
// Type-safe client initialization
const client: SimStudioClient = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
// Type-safe workflow execution

View File

@@ -214,7 +214,7 @@ class SimStudioError(Exception):
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def run_workflow():
try:
@@ -252,7 +252,7 @@ run_workflow()
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
@@ -284,7 +284,7 @@ from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
@@ -298,7 +298,7 @@ with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
@@ -352,8 +352,8 @@ for result in results:
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIMSTUDIO_API_KEY"),
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
api_key=os.getenv("SIM_API_KEY"),
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
@@ -365,13 +365,13 @@ for result in results:
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIMSTUDIO_API_KEY")
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIMSTUDIO_API_KEY environment variable is required")
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```

View File

@@ -230,7 +230,7 @@ class SimStudioError extends Error {
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
@@ -271,7 +271,7 @@ runWorkflow();
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
@@ -315,14 +315,14 @@ async function executeWithErrorHandling() {
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL // optional
baseUrl: process.env.SIM_BASE_URL // optional
});
```
@@ -333,14 +333,14 @@ async function executeWithErrorHandling() {
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL || 'https://sim.ai'
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
@@ -357,7 +357,7 @@ import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
@@ -399,7 +399,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
@@ -476,7 +476,7 @@ import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIMSTUDIO_API_KEY!
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
interface UseWorkflowResult {
@@ -588,7 +588,7 @@ import {
// Type-safe client initialization
const client: SimStudioClient = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
// Type-safe workflow execution

View File

@@ -214,7 +214,7 @@ class SimStudioError(Exception):
import os
from simstudio import SimStudioClient
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def run_workflow():
try:
@@ -252,7 +252,7 @@ run_workflow()
from simstudio import SimStudioClient, SimStudioError
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling():
try:
@@ -284,7 +284,7 @@ from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
@@ -298,7 +298,7 @@ with SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY")) as client:
from simstudio import SimStudioClient
import os
client = SimStudioClient(api_key=os.getenv("SIMSTUDIO_API_KEY"))
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_workflows_batch(workflow_data_pairs):
"""Execute multiple workflows with different input data."""
@@ -352,8 +352,8 @@ for result in results:
# Development configuration
client = SimStudioClient(
api_key=os.getenv("SIMSTUDIO_API_KEY"),
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
api_key=os.getenv("SIM_API_KEY"),
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```
@@ -365,13 +365,13 @@ for result in results:
from simstudio import SimStudioClient
# Production configuration with error handling
api_key = os.getenv("SIMSTUDIO_API_KEY")
api_key = os.getenv("SIM_API_KEY")
if not api_key:
raise ValueError("SIMSTUDIO_API_KEY environment variable is required")
raise ValueError("SIM_API_KEY environment variable is required")
client = SimStudioClient(
api_key=api_key,
base_url=os.getenv("SIMSTUDIO_BASE_URL", "https://sim.ai")
base_url=os.getenv("SIM_BASE_URL", "https://sim.ai")
)
```

View File

@@ -230,7 +230,7 @@ class SimStudioError extends Error {
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function runWorkflow() {
@@ -271,7 +271,7 @@ runWorkflow();
import { SimStudioClient, SimStudioError } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
async function executeWithErrorHandling() {
@@ -315,14 +315,14 @@ async function executeWithErrorHandling() {
import { SimStudioClient } from 'simstudio-ts-sdk';
// Development configuration
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL // optional
baseUrl: process.env.SIM_BASE_URL // optional
});
```
@@ -333,14 +333,14 @@ async function executeWithErrorHandling() {
import { SimStudioClient } from 'simstudio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.SIMSTUDIO_API_KEY;
const apiKey = process.env.SIM_API_KEY;
if (!apiKey) {
throw new Error('SIMSTUDIO_API_KEY environment variable is required');
throw new Error('SIM_API_KEY environment variable is required');
}
const client = new SimStudioClient({
apiKey,
baseUrl: process.env.SIMSTUDIO_BASE_URL || 'https://sim.ai'
baseUrl: process.env.SIM_BASE_URL || 'https://sim.ai'
});
```
@@ -357,7 +357,7 @@ import { SimStudioClient } from 'simstudio-ts-sdk';
const app = express();
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
app.use(express.json());
@@ -399,7 +399,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
export default async function handler(
@@ -476,7 +476,7 @@ import { useState, useCallback } from 'react';
import { SimStudioClient, WorkflowExecutionResult } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: process.env.NEXT_PUBLIC_SIMSTUDIO_API_KEY!
apiKey: process.env.NEXT_PUBLIC_SIM_API_KEY!
});
interface UseWorkflowResult {
@@ -588,7 +588,7 @@ import {
// Type-safe client initialization
const client: SimStudioClient = new SimStudioClient({
apiKey: process.env.SIMSTUDIO_API_KEY!
apiKey: process.env.SIM_API_KEY!
});
// Type-safe workflow execution

View File

@@ -9,7 +9,7 @@ export function cn(...inputs: ClassValue[]) {
}
/**
* Get the full URL for an asset stored in Vercel Blob or local fallback
* Get the full URL for an asset stored in Vercel Blob
* - If CDN is configured (NEXT_PUBLIC_BLOB_BASE_URL), uses CDN URL
* - Otherwise falls back to local static assets served from root path
*/
@@ -20,12 +20,3 @@ export function getAssetUrl(filename: string) {
}
return `/${filename}`
}
/**
* Get the full URL for a video asset stored in Vercel Blob or local fallback
* - If CDN is configured (NEXT_PUBLIC_BLOB_BASE_URL), uses CDN URL
* - Otherwise falls back to local static assets served from root path
*/
export function getVideoUrl(filename: string) {
return getAssetUrl(filename)
}