mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-10 15:38:00 -05:00
Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb0fa1fd21 | ||
|
|
6f3df271fd | ||
|
|
7f82ed381a | ||
|
|
3dd36a8a35 | ||
|
|
09cccd5487 | ||
|
|
1773530325 | ||
|
|
2da7a6755c | ||
|
|
1e81cd6850 | ||
|
|
ec73e2e9ce | ||
|
|
4937d72d70 | ||
|
|
219a065a7c | ||
|
|
8f06aec68b | ||
|
|
1de6f09069 | ||
|
|
b10b2461a5 | ||
|
|
34fc8f84f5 | ||
|
|
ee77dea2d6 | ||
|
|
bba407b507 | ||
|
|
ab63978ce8 | ||
|
|
e697e50d4e | ||
|
|
41ec229431 | ||
|
|
c0f5ba75f1 | ||
|
|
5a943bca32 | ||
|
|
923595f57e | ||
|
|
241d9fd12d | ||
|
|
97a8778449 | ||
|
|
833e700b58 | ||
|
|
2d49892aaa | ||
|
|
8ce5a1b7c0 | ||
|
|
88d2e7b97b | ||
|
|
c04eb01aed | ||
|
|
5d887fdca7 | ||
|
|
1a0fdb32fe | ||
|
|
9d45b8df1e | ||
|
|
ae3a7f0865 | ||
|
|
25f5e31378 | ||
|
|
7bdf0e94d7 | ||
|
|
8e43774b5e | ||
|
|
715f42c1a6 | ||
|
|
8200e9a88f | ||
|
|
c6f6c9e2a5 | ||
|
|
2d7ba91c0e | ||
|
|
872e034312 | ||
|
|
a63a7b0262 | ||
|
|
991a020917 | ||
|
|
f03f395225 | ||
|
|
174f6a48a6 | ||
|
|
c2f0a95802 | ||
|
|
4dc4073452 | ||
|
|
d9b70087c4 | ||
|
|
07fd9c3a4a | ||
|
|
377b84e18c | ||
|
|
223ecda80e | ||
|
|
7dde01e74b | ||
|
|
b768ca845e | ||
|
|
86ed32ea10 | ||
|
|
0e838940f1 | ||
|
|
7cc9a23f99 | ||
|
|
c42d2a32f3 | ||
|
|
4da355d269 | ||
|
|
2175fd1106 | ||
|
|
10692b5e5a | ||
|
|
62298bf094 | ||
|
|
5f1518ffd9 | ||
|
|
cae0e85826 | ||
|
|
fa9c97816b | ||
|
|
4bc37db547 | ||
|
|
15138629cb | ||
|
|
ace83ebcae | ||
|
|
b33ae5bff9 | ||
|
|
dc6052578d | ||
|
|
4adbae03e7 | ||
|
|
3509ce8ce4 | ||
|
|
7aae108b87 | ||
|
|
980a6d8347 | ||
|
|
745eaff622 | ||
|
|
35d857ef2e | ||
|
|
6e63eafb79 | ||
|
|
896f7bb0a0 | ||
|
|
97f69a24e1 | ||
|
|
1a2c4040aa | ||
|
|
4ad9be0836 | ||
|
|
0bf2bce368 | ||
|
|
0d881ecc00 | ||
|
|
7e6a5dc7e2 | ||
|
|
c1a3500bde | ||
|
|
561b6f2778 | ||
|
|
cdfee16b8a | ||
|
|
9f6cb1becf | ||
|
|
dca8745c44 | ||
|
|
c35c8d1f31 | ||
|
|
87c00cec6d | ||
|
|
17edf0405b | ||
|
|
79461840c3 | ||
|
|
e76fc8c2da | ||
|
|
e9150a53e3 |
@@ -1,69 +0,0 @@
|
||||
# Sim Development Environment Bashrc
|
||||
# This gets sourced by post-create.sh
|
||||
|
||||
# Enhanced prompt with git branch info
|
||||
parse_git_branch() {
|
||||
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
|
||||
}
|
||||
|
||||
export PS1="\[\033[01;32m\]\u@simstudio\[\033[00m\]:\[\033[01;34m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\]\$ "
|
||||
|
||||
# Helpful aliases
|
||||
alias ll="ls -la"
|
||||
alias ..="cd .."
|
||||
alias ...="cd ../.."
|
||||
|
||||
# Database aliases
|
||||
alias pgc="PGPASSWORD=postgres psql -h db -U postgres -d simstudio"
|
||||
alias check-db="PGPASSWORD=postgres psql -h db -U postgres -c '\l'"
|
||||
|
||||
# Sim specific aliases
|
||||
alias logs="cd /workspace/apps/sim && tail -f logs/*.log 2>/dev/null || echo 'No log files found'"
|
||||
alias sim-start="cd /workspace && bun run dev"
|
||||
alias sim-migrate="cd /workspace/apps/sim && bunx drizzle-kit push"
|
||||
alias sim-generate="cd /workspace/apps/sim && bunx drizzle-kit generate"
|
||||
alias sim-rebuild="cd /workspace && bun run build && bun run start"
|
||||
alias docs-dev="cd /workspace/apps/docs && bun run dev"
|
||||
|
||||
# Turbo related commands
|
||||
alias turbo-build="cd /workspace && bunx turbo run build"
|
||||
alias turbo-dev="cd /workspace && bunx turbo run dev"
|
||||
alias turbo-test="cd /workspace && bunx turbo run test"
|
||||
|
||||
# Bun specific commands
|
||||
alias bun-update="cd /workspace && bun update"
|
||||
alias bun-add="cd /workspace && bun add"
|
||||
alias bun-pm="cd /workspace && bun pm"
|
||||
alias bun-canary="bun upgrade --canary"
|
||||
|
||||
# Default to workspace directory
|
||||
cd /workspace 2>/dev/null || true
|
||||
|
||||
# Welcome message - only show once per session
|
||||
if [ -z "$SIM_WELCOME_SHOWN" ]; then
|
||||
export SIM_WELCOME_SHOWN=1
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🚀 Welcome to Sim development environment!"
|
||||
echo ""
|
||||
echo "Available commands:"
|
||||
echo " sim-start - Start all apps in development mode"
|
||||
echo " sim-migrate - Push schema changes to the database for sim app"
|
||||
echo " sim-generate - Generate new migrations for sim app"
|
||||
echo " sim-rebuild - Build and start all apps"
|
||||
echo " docs-dev - Start only the docs app in development mode"
|
||||
echo ""
|
||||
echo "Turbo commands:"
|
||||
echo " turbo-build - Build all apps using Turborepo"
|
||||
echo " turbo-dev - Start development mode for all apps"
|
||||
echo " turbo-test - Run tests for all packages"
|
||||
echo ""
|
||||
echo "Bun commands:"
|
||||
echo " bun-update - Update dependencies"
|
||||
echo " bun-add - Add a new dependency"
|
||||
echo " bun-pm - Manage dependencies"
|
||||
echo " bun-canary - Upgrade to the latest canary version of Bun"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
fi
|
||||
@@ -1,38 +1,43 @@
|
||||
# Use the latest Bun canary image for development
|
||||
FROM oven/bun:canary
|
||||
|
||||
# Avoid warnings by switching to noninteractive
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
FROM oven/bun:1.2.22-alpine
|
||||
|
||||
# Install necessary packages for development
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
git curl wget jq sudo postgresql-client vim nano \
|
||||
bash-completion ca-certificates lsb-release gnupg \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
sudo \
|
||||
postgresql-client \
|
||||
vim \
|
||||
nano \
|
||||
bash \
|
||||
bash-completion \
|
||||
zsh \
|
||||
zsh-vcs \
|
||||
ca-certificates \
|
||||
shadow
|
||||
|
||||
# Create a non-root user
|
||||
# Create a non-root user with matching UID/GID
|
||||
ARG USERNAME=bun
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=$USER_UID
|
||||
|
||||
# Create user group if it doesn't exist
|
||||
RUN if ! getent group $USER_GID >/dev/null; then \
|
||||
addgroup -g $USER_GID $USERNAME; \
|
||||
fi
|
||||
|
||||
# Create user if it doesn't exist
|
||||
RUN if ! getent passwd $USER_UID >/dev/null; then \
|
||||
adduser -D -u $USER_UID -G $(getent group $USER_GID | cut -d: -f1) $USERNAME; \
|
||||
fi
|
||||
|
||||
# Add sudo support
|
||||
RUN echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USERNAME \
|
||||
&& chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
|
||||
# Install global packages for development
|
||||
RUN bun install -g turbo drizzle-kit typescript @types/node
|
||||
|
||||
# Install bun completions
|
||||
RUN bun completions > /etc/bash_completion.d/bun
|
||||
|
||||
# Set up shell environment
|
||||
RUN echo "export PATH=$PATH:/home/$USERNAME/.bun/bin" >> /etc/profile
|
||||
RUN echo "source /etc/profile" >> /etc/bash.bashrc
|
||||
|
||||
# Switch back to dialog for any ad-hoc use of apt-get
|
||||
ENV DEBIAN_FRONTEND=dialog
|
||||
RUN echo "export PATH=\$PATH:/home/$USERNAME/.bun/bin" >> /etc/profile
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
|
||||
@@ -1,78 +1,75 @@
|
||||
# Sim Development Container
|
||||
|
||||
This directory contains configuration files for Visual Studio Code Dev Containers / GitHub Codespaces. Dev containers provide a consistent, isolated development environment for this project.
|
||||
Development container configuration for VS Code Dev Containers and GitHub Codespaces.
|
||||
|
||||
## Contents
|
||||
|
||||
- `devcontainer.json` - The main configuration file that defines the development container settings
|
||||
- `Dockerfile` - Defines the container image and development environment
|
||||
- `docker-compose.yml` - Sets up the application and database containers
|
||||
- `post-create.sh` - Script that runs when the container is created
|
||||
- `.bashrc` - Custom shell configuration with helpful aliases
|
||||
|
||||
## Usage
|
||||
|
||||
### Prerequisites
|
||||
## Prerequisites
|
||||
|
||||
- Visual Studio Code
|
||||
- Docker installation:
|
||||
- Docker Desktop (Windows/macOS)
|
||||
- Docker Engine (Linux)
|
||||
- VS Code Remote - Containers extension
|
||||
- Docker Desktop or Podman Desktop
|
||||
- VS Code Dev Containers extension
|
||||
|
||||
### Getting Started
|
||||
## Getting Started
|
||||
|
||||
1. Open this project in Visual Studio Code
|
||||
2. When prompted, click "Reopen in Container"
|
||||
- Alternatively, press `F1` and select "Remote-Containers: Reopen in Container"
|
||||
1. Open this project in VS Code
|
||||
2. Click "Reopen in Container" when prompted (or press `F1` → "Dev Containers: Reopen in Container")
|
||||
3. Wait for the container to build and initialize
|
||||
4. The post-creation script will automatically:
|
||||
4. Start developing with `sim-start`
|
||||
|
||||
- Install dependencies
|
||||
- Set up environment variables
|
||||
- Run database migrations
|
||||
- Configure helpful aliases
|
||||
The setup script will automatically install dependencies and run migrations.
|
||||
|
||||
5. Start the application with `sim-start` (alias for `bun run dev`)
|
||||
## Development Commands
|
||||
|
||||
### Development Commands
|
||||
### Running Services
|
||||
|
||||
The development environment includes these helpful aliases:
|
||||
You have two options for running the development environment:
|
||||
|
||||
**Option 1: Run everything together (recommended for most development)**
|
||||
```bash
|
||||
sim-start # Runs both app and socket server using concurrently
|
||||
```
|
||||
|
||||
**Option 2: Run services separately (useful for debugging individual services)**
|
||||
- In the **app** container terminal: `sim-app` (starts Next.js app on port 3000)
|
||||
- In the **realtime** container terminal: `sim-sockets` (starts socket server on port 3002)
|
||||
|
||||
### Other Commands
|
||||
|
||||
- `sim-start` - Start the development server
|
||||
- `sim-migrate` - Push schema changes to the database
|
||||
- `sim-generate` - Generate new migrations
|
||||
- `sim-rebuild` - Build and start the production version
|
||||
- `pgc` - Connect to the PostgreSQL database
|
||||
- `check-db` - List all databases
|
||||
|
||||
### Using GitHub Codespaces
|
||||
|
||||
This project is also configured for GitHub Codespaces. To use it:
|
||||
|
||||
1. Go to the GitHub repository
|
||||
2. Click the "Code" button
|
||||
3. Select the "Codespaces" tab
|
||||
4. Click "Create codespace on main"
|
||||
|
||||
This will start a new Codespace with the development environment already set up.
|
||||
|
||||
## Customization
|
||||
|
||||
You can customize the development environment by:
|
||||
|
||||
- Modifying `devcontainer.json` to add VS Code extensions or settings
|
||||
- Updating the `Dockerfile` to install additional packages
|
||||
- Editing `docker-compose.yml` to add services or change configuration
|
||||
- Modifying `.bashrc` to add custom aliases or configurations
|
||||
- `build` - Build the application
|
||||
- `pgc` - Connect to PostgreSQL database
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter issues:
|
||||
**Build errors**: Rebuild the container with `F1` → "Dev Containers: Rebuild Container"
|
||||
|
||||
1. Rebuild the container: `F1` → "Remote-Containers: Rebuild Container"
|
||||
2. Check Docker logs for build errors
|
||||
3. Verify Docker Desktop is running
|
||||
4. Ensure all prerequisites are installed
|
||||
**Port conflicts**: Ensure ports 3000, 3002, and 5432 are available
|
||||
|
||||
For more information, see the [VS Code Remote Development documentation](https://code.visualstudio.com/docs/remote/containers).
|
||||
**Container runtime issues**: Verify Docker Desktop or Podman Desktop is running
|
||||
|
||||
## Technical Details
|
||||
|
||||
Services:
|
||||
- **App container** (8GB memory limit) - Main Next.js application
|
||||
- **Realtime container** (4GB memory limit) - Socket.io server for real-time features
|
||||
- **Database** - PostgreSQL with pgvector extension
|
||||
- **Migrations** - Runs automatically on container creation
|
||||
|
||||
You can develop with services running together or independently.
|
||||
|
||||
### Personalization
|
||||
|
||||
**Project commands** (`sim-start`, `sim-app`, etc.) are automatically available via `/workspace/.devcontainer/sim-commands.sh`.
|
||||
|
||||
**Personal shell customization** (aliases, prompts, etc.) should use VS Code's dotfiles feature:
|
||||
1. Create a dotfiles repository (e.g., `github.com/youruser/dotfiles`)
|
||||
2. Add your `.bashrc`, `.zshrc`, or other configs
|
||||
3. Configure in VS Code Settings:
|
||||
```json
|
||||
{
|
||||
"dotfiles.repository": "youruser/dotfiles",
|
||||
"dotfiles.installCommand": "install.sh"
|
||||
}
|
||||
```
|
||||
|
||||
This separates project-specific commands from personal preferences, following VS Code best practices.
|
||||
|
||||
@@ -13,13 +13,6 @@
|
||||
"source.fixAll.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit"
|
||||
},
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"bash": {
|
||||
"path": "/bin/bash",
|
||||
"args": ["--login"]
|
||||
}
|
||||
},
|
||||
"terminal.integrated.shellIntegration.enabled": true
|
||||
},
|
||||
"extensions": [
|
||||
@@ -36,18 +29,9 @@
|
||||
}
|
||||
},
|
||||
|
||||
"forwardPorts": [3000, 5432],
|
||||
"forwardPorts": [3000, 3002, 5432],
|
||||
|
||||
"postCreateCommand": "bash -c 'bash .devcontainer/post-create.sh || true'",
|
||||
|
||||
"postStartCommand": "bash -c 'if [ ! -f ~/.bashrc ] || ! grep -q \"sim-start\" ~/.bashrc; then cp .devcontainer/.bashrc ~/.bashrc; fi'",
|
||||
|
||||
"remoteUser": "bun",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/git:1": {},
|
||||
"ghcr.io/prulloac/devcontainer-features/bun:1": {
|
||||
"version": "latest"
|
||||
}
|
||||
}
|
||||
"remoteUser": "bun"
|
||||
}
|
||||
|
||||
@@ -7,53 +7,56 @@ services:
|
||||
- ..:/workspace:cached
|
||||
- bun-cache:/home/bun/.bun/cache:delegated
|
||||
command: sleep infinity
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 8G
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio
|
||||
- POSTGRES_URL=postgresql://postgres:postgres@db:5432/simstudio
|
||||
- BETTER_AUTH_URL=http://localhost:3000
|
||||
- NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your_auth_secret_here}
|
||||
- ENCRYPTION_KEY=${ENCRYPTION_KEY:-your_encryption_key_here}
|
||||
- COPILOT_API_KEY=${COPILOT_API_KEY}
|
||||
- SIM_AGENT_API_URL=${SIM_AGENT_API_URL}
|
||||
- OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
|
||||
- NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL:-http://localhost:3002}
|
||||
- BUN_INSTALL_CACHE_DIR=/home/bun/.bun/cache
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
realtime:
|
||||
condition: service_healthy
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "3001:3001"
|
||||
working_dir: /workspace
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000']
|
||||
interval: 90s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
realtime:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: .devcontainer/Dockerfile
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
- bun-cache:/home/bun/.bun/cache:delegated
|
||||
command: sleep infinity
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 4G
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio
|
||||
- BETTER_AUTH_URL=http://localhost:3000
|
||||
- NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your_auth_secret_here}
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3002:3002"
|
||||
working_dir: /workspace
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3002']
|
||||
interval: 90s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
migrations:
|
||||
build:
|
||||
|
||||
@@ -8,11 +8,43 @@ echo "🔧 Setting up Sim development environment..."
|
||||
# Change to the workspace root directory
|
||||
cd /workspace
|
||||
|
||||
# Setup .bashrc
|
||||
echo "📄 Setting up .bashrc with aliases..."
|
||||
cp /workspace/.devcontainer/.bashrc ~/.bashrc
|
||||
# Add to .profile to ensure .bashrc is sourced in non-interactive shells
|
||||
echo 'if [ -f ~/.bashrc ]; then . ~/.bashrc; fi' >> ~/.profile
|
||||
# Install global packages for development (done at runtime, not build time)
|
||||
echo "📦 Installing global development tools..."
|
||||
bun install -g turbo drizzle-kit typescript @types/node 2>/dev/null || {
|
||||
echo "⚠️ Some global packages may already be installed, continuing..."
|
||||
}
|
||||
|
||||
# Set up bun completions (with proper shell detection)
|
||||
echo "🔧 Setting up shell completions..."
|
||||
if [ -n "$SHELL" ] && [ -f "$SHELL" ]; then
|
||||
SHELL=/bin/bash bun completions 2>/dev/null | sudo tee /etc/bash_completion.d/bun > /dev/null || {
|
||||
echo "⚠️ Could not install bun completions, but continuing..."
|
||||
}
|
||||
fi
|
||||
|
||||
# Add project commands to shell profile
|
||||
echo "📄 Setting up project commands..."
|
||||
# Add sourcing of sim-commands.sh to user's shell config files if they exist
|
||||
for rcfile in ~/.bashrc ~/.zshrc; do
|
||||
if [ -f "$rcfile" ]; then
|
||||
# Check if already added
|
||||
if ! grep -q "sim-commands.sh" "$rcfile"; then
|
||||
echo "" >> "$rcfile"
|
||||
echo "# Sim project commands" >> "$rcfile"
|
||||
echo "if [ -f /workspace/.devcontainer/sim-commands.sh ]; then" >> "$rcfile"
|
||||
echo " source /workspace/.devcontainer/sim-commands.sh" >> "$rcfile"
|
||||
echo "fi" >> "$rcfile"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# If no rc files exist yet, create a minimal one
|
||||
if [ ! -f ~/.bashrc ] && [ ! -f ~/.zshrc ]; then
|
||||
echo "# Source Sim project commands" > ~/.bashrc
|
||||
echo "if [ -f /workspace/.devcontainer/sim-commands.sh ]; then" >> ~/.bashrc
|
||||
echo " source /workspace/.devcontainer/sim-commands.sh" >> ~/.bashrc
|
||||
echo "fi" >> ~/.bashrc
|
||||
fi
|
||||
|
||||
# Clean and reinstall dependencies to ensure platform compatibility
|
||||
echo "📦 Cleaning and reinstalling dependencies..."
|
||||
@@ -29,18 +61,12 @@ chmod 700 ~/.bun ~/.bun/cache
|
||||
|
||||
# Install dependencies with platform-specific binaries
|
||||
echo "Installing dependencies with Bun..."
|
||||
bun install || {
|
||||
echo "⚠️ bun install had issues but continuing setup..."
|
||||
}
|
||||
bun install
|
||||
|
||||
# Check for native dependencies
|
||||
echo "Checking for native dependencies compatibility..."
|
||||
NATIVE_DEPS=$(grep '"trustedDependencies"' apps/sim/package.json || echo "")
|
||||
if [ ! -z "$NATIVE_DEPS" ]; then
|
||||
echo "⚠️ Native dependencies detected. Ensuring compatibility with Bun..."
|
||||
for pkg in $(echo $NATIVE_DEPS | grep -oP '"[^"]*"' | tr -d '"' | grep -v "trustedDependencies"); do
|
||||
echo "Checking compatibility for $pkg..."
|
||||
done
|
||||
if grep -q '"trustedDependencies"' apps/sim/package.json 2>/dev/null; then
|
||||
echo "⚠️ Native dependencies detected. Bun will handle compatibility during install."
|
||||
fi
|
||||
|
||||
# Set up environment variables if .env doesn't exist for the sim app
|
||||
@@ -82,23 +108,6 @@ echo "Waiting for database to be ready..."
|
||||
fi
|
||||
) || echo "⚠️ Database setup had issues but continuing..."
|
||||
|
||||
# Add additional helpful aliases to .bashrc
|
||||
cat << EOF >> ~/.bashrc
|
||||
|
||||
# Additional Sim Development Aliases
|
||||
alias migrate="cd /workspace/apps/sim && DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio bunx drizzle-kit push"
|
||||
alias generate="cd /workspace/apps/sim && bunx drizzle-kit generate"
|
||||
alias dev="cd /workspace && bun run dev"
|
||||
alias build="cd /workspace && bun run build"
|
||||
alias start="cd /workspace && bun run dev"
|
||||
alias lint="cd /workspace/apps/sim && bun run lint"
|
||||
alias test="cd /workspace && bun run test"
|
||||
alias bun-update="cd /workspace && bun update"
|
||||
EOF
|
||||
|
||||
# Source the .bashrc to make aliases available immediately
|
||||
. ~/.bashrc
|
||||
|
||||
# Clear the welcome message flag to ensure it shows after setup
|
||||
unset SIM_WELCOME_SHOWN
|
||||
|
||||
|
||||
42
.devcontainer/sim-commands.sh
Executable file
42
.devcontainer/sim-commands.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# Sim Project Commands
|
||||
# Source this file to add project-specific commands to your shell
|
||||
# Add to your ~/.bashrc or ~/.zshrc: source /workspace/.devcontainer/sim-commands.sh
|
||||
|
||||
# Project-specific aliases for Sim development
|
||||
alias sim-start="cd /workspace && bun run dev:full"
|
||||
alias sim-app="cd /workspace && bun run dev"
|
||||
alias sim-sockets="cd /workspace && bun run dev:sockets"
|
||||
alias sim-migrate="cd /workspace/apps/sim && bunx drizzle-kit push"
|
||||
alias sim-generate="cd /workspace/apps/sim && bunx drizzle-kit generate"
|
||||
alias sim-rebuild="cd /workspace && bun run build && bun run start"
|
||||
alias docs-dev="cd /workspace/apps/docs && bun run dev"
|
||||
|
||||
# Database connection helpers
|
||||
alias pgc="PGPASSWORD=postgres psql -h db -U postgres -d simstudio"
|
||||
alias check-db="PGPASSWORD=postgres psql -h db -U postgres -c '\l'"
|
||||
|
||||
# Default to workspace directory
|
||||
cd /workspace 2>/dev/null || true
|
||||
|
||||
# Welcome message - show once per session
|
||||
if [ -z "$SIM_WELCOME_SHOWN" ]; then
|
||||
export SIM_WELCOME_SHOWN=1
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🚀 Sim Development Environment"
|
||||
echo ""
|
||||
echo "Project commands:"
|
||||
echo " sim-start - Start app + socket server"
|
||||
echo " sim-app - Start only main app"
|
||||
echo " sim-sockets - Start only socket server"
|
||||
echo " sim-migrate - Push schema changes"
|
||||
echo " sim-generate - Generate migrations"
|
||||
echo ""
|
||||
echo "Database:"
|
||||
echo " pgc - Connect to PostgreSQL"
|
||||
echo " check-db - List databases"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
fi
|
||||
201
.github/workflows/ci.yml
vendored
201
.github/workflows/ci.yml
vendored
@@ -16,43 +16,200 @@ jobs:
|
||||
uses: ./.github/workflows/test-build.yml
|
||||
secrets: inherit
|
||||
|
||||
# Build and push images (ECR for staging, ECR + GHCR for main)
|
||||
build-images:
|
||||
name: Build Images
|
||||
# Build AMD64 images and push to ECR immediately (+ GHCR for main)
|
||||
build-amd64:
|
||||
name: Build AMD64
|
||||
needs: test-build
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
|
||||
uses: ./.github/workflows/images.yml
|
||||
secrets: inherit
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- dockerfile: ./docker/app.Dockerfile
|
||||
ghcr_image: ghcr.io/simstudioai/simstudio
|
||||
ecr_repo_secret: ECR_APP
|
||||
- dockerfile: ./docker/db.Dockerfile
|
||||
ghcr_image: ghcr.io/simstudioai/migrations
|
||||
ecr_repo_secret: ECR_MIGRATIONS
|
||||
- dockerfile: ./docker/realtime.Dockerfile
|
||||
ghcr_image: ghcr.io/simstudioai/realtime
|
||||
ecr_repo_secret: ECR_REALTIME
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Deploy Trigger.dev (after builds complete)
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
|
||||
aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }}
|
||||
|
||||
- name: Login to Amazon ECR
|
||||
id: login-ecr
|
||||
uses: aws-actions/amazon-ecr-login@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GHCR
|
||||
if: github.ref == 'refs/heads/main'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: useblacksmith/setup-docker-builder@v1
|
||||
|
||||
- name: Generate tags
|
||||
id: meta
|
||||
run: |
|
||||
ECR_REGISTRY="${{ steps.login-ecr.outputs.registry }}"
|
||||
ECR_REPO="${{ secrets[matrix.ecr_repo_secret] }}"
|
||||
GHCR_IMAGE="${{ matrix.ghcr_image }}"
|
||||
|
||||
# ECR tags (always build for ECR)
|
||||
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||||
ECR_TAG="latest"
|
||||
else
|
||||
ECR_TAG="staging"
|
||||
fi
|
||||
ECR_IMAGE="${ECR_REGISTRY}/${ECR_REPO}:${ECR_TAG}"
|
||||
|
||||
# Build tags list
|
||||
TAGS="${ECR_IMAGE}"
|
||||
|
||||
# Add GHCR tags only for main branch
|
||||
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||||
GHCR_AMD64="${GHCR_IMAGE}:latest-amd64"
|
||||
GHCR_SHA="${GHCR_IMAGE}:${{ github.sha }}-amd64"
|
||||
TAGS="${TAGS},$GHCR_AMD64,$GHCR_SHA"
|
||||
fi
|
||||
|
||||
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push images
|
||||
uses: useblacksmith/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.dockerfile }}
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
provenance: false
|
||||
sbom: false
|
||||
|
||||
# Build ARM64 images for GHCR (main branch only, runs in parallel)
|
||||
build-ghcr-arm64:
|
||||
name: Build ARM64 (GHCR Only)
|
||||
needs: test-build
|
||||
runs-on: linux-arm64-8-core
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- dockerfile: ./docker/app.Dockerfile
|
||||
image: ghcr.io/simstudioai/simstudio
|
||||
- dockerfile: ./docker/db.Dockerfile
|
||||
image: ghcr.io/simstudioai/migrations
|
||||
- dockerfile: ./docker/realtime.Dockerfile
|
||||
image: ghcr.io/simstudioai/realtime
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: useblacksmith/setup-docker-builder@v1
|
||||
|
||||
- name: Generate ARM64 tags
|
||||
id: meta
|
||||
run: |
|
||||
IMAGE="${{ matrix.image }}"
|
||||
echo "tags=${IMAGE}:latest-arm64,${IMAGE}:${{ github.sha }}-arm64" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push ARM64 to GHCR
|
||||
uses: useblacksmith/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.dockerfile }}
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
provenance: false
|
||||
sbom: false
|
||||
|
||||
# Create GHCR multi-arch manifests (only for main, after both builds)
|
||||
create-ghcr-manifests:
|
||||
name: Create GHCR Manifests
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
needs: [build-amd64, build-ghcr-arm64]
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
permissions:
|
||||
packages: write
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- image: ghcr.io/simstudioai/simstudio
|
||||
- image: ghcr.io/simstudioai/migrations
|
||||
- image: ghcr.io/simstudioai/realtime
|
||||
|
||||
steps:
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifests
|
||||
run: |
|
||||
IMAGE_BASE="${{ matrix.image }}"
|
||||
|
||||
# Create latest manifest
|
||||
docker manifest create "${IMAGE_BASE}:latest" \
|
||||
"${IMAGE_BASE}:latest-amd64" \
|
||||
"${IMAGE_BASE}:latest-arm64"
|
||||
docker manifest push "${IMAGE_BASE}:latest"
|
||||
|
||||
# Create SHA manifest
|
||||
docker manifest create "${IMAGE_BASE}:${{ github.sha }}" \
|
||||
"${IMAGE_BASE}:${{ github.sha }}-amd64" \
|
||||
"${IMAGE_BASE}:${{ github.sha }}-arm64"
|
||||
docker manifest push "${IMAGE_BASE}:${{ github.sha }}"
|
||||
|
||||
# Deploy Trigger.dev (after ECR images are pushed, runs in parallel with process-docs)
|
||||
trigger-deploy:
|
||||
name: Deploy Trigger.dev
|
||||
needs: build-images
|
||||
needs: build-amd64
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
|
||||
uses: ./.github/workflows/trigger-deploy.yml
|
||||
secrets: inherit
|
||||
|
||||
# Run database migrations (depends on build completion and trigger deployment)
|
||||
migrations:
|
||||
name: Apply Database Migrations
|
||||
needs: [build-images, trigger-deploy]
|
||||
if: |
|
||||
always() &&
|
||||
github.event_name == 'push' &&
|
||||
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging') &&
|
||||
needs.build-images.result == 'success' &&
|
||||
needs.trigger-deploy.result == 'success'
|
||||
uses: ./.github/workflows/migrations.yml
|
||||
secrets: inherit
|
||||
|
||||
# Process docs embeddings if needed
|
||||
# Process docs embeddings (after ECR images are pushed, runs in parallel with trigger-deploy)
|
||||
process-docs:
|
||||
name: Process Docs
|
||||
needs: migrations
|
||||
needs: build-amd64
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
|
||||
uses: ./.github/workflows/docs-embeddings.yml
|
||||
secrets: inherit
|
||||
|
||||
4
.github/workflows/docs-embeddings.yml
vendored
4
.github/workflows/docs-embeddings.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
@@ -32,4 +32,4 @@ jobs:
|
||||
env:
|
||||
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
run: bun run scripts/process-docs-embeddings.ts --clear
|
||||
run: bun run scripts/process-docs.ts --clear
|
||||
|
||||
4
.github/workflows/i18n.yml
vendored
4
.github/workflows/i18n.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Run Lingo.dev translations
|
||||
env:
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
2
.github/workflows/migrations.yml
vendored
2
.github/workflows/migrations.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
2
.github/workflows/publish-cli.yml
vendored
2
.github/workflows/publish-cli.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Setup Node.js for npm publishing
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
2
.github/workflows/publish-python-sdk.yml
vendored
2
.github/workflows/publish-python-sdk.yml
vendored
@@ -84,6 +84,6 @@ jobs:
|
||||
```
|
||||
|
||||
### Documentation
|
||||
See the [README](https://github.com/simstudio/sim/tree/main/packages/python-sdk) for usage instructions.
|
||||
See the [README](https://github.com/simstudioai/sim/tree/main/packages/python-sdk) or the [docs](https://docs.sim.ai/sdks/python) for more information.
|
||||
draft: false
|
||||
prerelease: false
|
||||
7
.github/workflows/publish-ts-sdk.yml
vendored
7
.github/workflows/publish-ts-sdk.yml
vendored
@@ -16,16 +16,15 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Setup Node.js for npm publishing
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: '22'
|
||||
registry-url: 'https://registry.npmjs.org/'
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: packages/ts-sdk
|
||||
run: bun install
|
||||
|
||||
- name: Run tests
|
||||
@@ -80,6 +79,6 @@ jobs:
|
||||
```
|
||||
|
||||
### Documentation
|
||||
See the [README](https://github.com/simstudio/sim/tree/main/packages/ts-sdk) for usage instructions.
|
||||
See the [README](https://github.com/simstudioai/sim/tree/main/packages/ts-sdk) or the [docs](https://docs.sim.ai/sdks/typescript) for more information.
|
||||
draft: false
|
||||
prerelease: false
|
||||
2
.github/workflows/test-build.yml
vendored
2
.github/workflows/test-build.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
6
.github/workflows/trigger-deploy.yml
vendored
6
.github/workflows/trigger-deploy.yml
vendored
@@ -13,6 +13,7 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
env:
|
||||
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
|
||||
TRIGGER_PROJECT_ID: ${{ secrets.TRIGGER_PROJECT_ID }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -26,7 +27,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.2.22
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
@@ -39,4 +40,5 @@ jobs:
|
||||
- name: Deploy to Trigger.dev (Production)
|
||||
if: github.ref == 'refs/heads/main'
|
||||
working-directory: ./apps/sim
|
||||
run: npx --yes trigger.dev@4.0.4 deploy
|
||||
run: npx --yes trigger.dev@4.0.4 deploy
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { defineI18nUI } from 'fumadocs-ui/i18n'
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs'
|
||||
import { RootProvider } from 'fumadocs-ui/provider'
|
||||
import { RootProvider } from 'fumadocs-ui/provider/next'
|
||||
import { ExternalLink, GithubIcon } from 'lucide-react'
|
||||
import { Inter } from 'next/font/google'
|
||||
import Image from 'next/image'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
|
||||
@@ -175,56 +175,30 @@ Verwenden Sie einen `Memory`Block mit einer konsistenten `id` (zum Beispiel `cha
|
||||
- Lesen Sie den Gesprächsverlauf für den Kontext
|
||||
- Hängen Sie die Antwort des Agenten nach dessen Ausführung an
|
||||
|
||||
```yaml
|
||||
# 1) Add latest user message
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: user
|
||||
content: {{input}}
|
||||
|
||||
# 2) Load conversation history
|
||||
- Memory (operation: get)
|
||||
id: chat
|
||||
|
||||
# 3) Run the agent with prior messages available
|
||||
- Agent
|
||||
System Prompt: ...
|
||||
User Prompt: |
|
||||
Use the conversation so far:
|
||||
{{memory_get.memories}}
|
||||
Current user message: {{input}}
|
||||
|
||||
# 4) Store the agent reply
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: assistant
|
||||
content: {{agent.content}}
|
||||
```
|
||||
|
||||
Siehe die `Memory`Block-Referenz für Details: [/tools/memory](/tools/memory).
|
||||
Siehe die [`Memory`](/tools/memory) Blockreferenz für Details.
|
||||
|
||||
## Eingaben und Ausgaben
|
||||
|
||||
<Tabs items={['Configuration', 'Variables', 'Results']}>
|
||||
<Tabs items={['Konfiguration', 'Variablen', 'Ergebnisse']}>
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>System Prompt</strong>: Anweisungen, die das Verhalten und die Rolle des Agenten definieren
|
||||
<strong>System-Prompt</strong>: Anweisungen, die das Verhalten und die Rolle des Agenten definieren
|
||||
</li>
|
||||
<li>
|
||||
<strong>User Prompt</strong>: Eingabetext oder -daten zur Verarbeitung
|
||||
<strong>Benutzer-Prompt</strong>: Eingabetext oder zu verarbeitende Daten
|
||||
</li>
|
||||
<li>
|
||||
<strong>Model</strong>: KI-Modellauswahl (OpenAI, Anthropic, Google, etc.)
|
||||
<strong>Modell</strong>: KI-Modellauswahl (OpenAI, Anthropic, Google, usw.)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Temperature</strong>: Steuerung der Antwort-Zufälligkeit (0-2)
|
||||
<strong>Temperatur</strong>: Steuerung der Zufälligkeit der Antwort (0-2)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Tools</strong>: Array verfügbarer Tools für Funktionsaufrufe
|
||||
</li>
|
||||
<li>
|
||||
<strong>Response Format</strong>: JSON-Schema für strukturierte Ausgabe
|
||||
<strong>Antwortformat</strong>: JSON-Schema für strukturierte Ausgabe
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
@@ -261,7 +235,7 @@ Siehe die `Memory`Block-Referenz für Details: [/tools/memory](/tools/memory).
|
||||
|
||||
## Beispielanwendungsfälle
|
||||
|
||||
### Automatisierung des Kundendienstes
|
||||
### Automatisierung des Kundenservice
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Szenario: Bearbeitung von Kundenanfragen mit Datenbankzugriff</h4>
|
||||
@@ -269,9 +243,9 @@ Siehe die `Memory`Block-Referenz für Details: [/tools/memory](/tools/memory).
|
||||
<li>Benutzer reicht ein Support-Ticket über den API-Block ein</li>
|
||||
<li>Agent prüft Bestellungen/Abonnements in Postgres und durchsucht die Wissensdatenbank nach Anleitungen</li>
|
||||
<li>Falls eine Eskalation erforderlich ist, erstellt der Agent ein Linear-Ticket mit relevantem Kontext</li>
|
||||
<li>Agent verfasst eine klare E-Mail-Antwort</li>
|
||||
<li>Agent erstellt eine klare E-Mail-Antwort</li>
|
||||
<li>Gmail sendet die Antwort an den Kunden</li>
|
||||
<li>Konversation wird im Speicher gesichert, um den Verlauf für zukünftige Nachrichten zu erhalten</li>
|
||||
<li>Konversation wird im Memory gespeichert, um den Verlauf für zukünftige Nachrichten beizubehalten</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
@@ -287,20 +261,20 @@ Siehe die `Memory`Block-Referenz für Details: [/tools/memory](/tools/memory).
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### Werkzeuggestützter Rechercheassistent
|
||||
### Werkzeuggestützter Forschungsassistent
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Szenario: Rechercheassistent mit Websuche und Dokumentenzugriff</h4>
|
||||
<h4 className="font-medium">Szenario: Forschungsassistent mit Websuche und Dokumentenzugriff</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>Benutzeranfrage über Eingabe erhalten</li>
|
||||
<li>Agent durchsucht das Web mit dem Google Search-Tool</li>
|
||||
<li>Agent durchsucht das Web mit dem Google-Suchwerkzeug</li>
|
||||
<li>Agent greift auf Notion-Datenbank für interne Dokumente zu</li>
|
||||
<li>Agent erstellt umfassenden Recherchebericht</li>
|
||||
<li>Agent erstellt umfassenden Forschungsbericht</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
## Best Practices
|
||||
## Bewährte Praktiken
|
||||
|
||||
- **Sei spezifisch in System-Prompts**: Definiere die Rolle, den Tonfall und die Einschränkungen des Agenten klar. Je spezifischer deine Anweisungen sind, desto besser kann der Agent seinen vorgesehenen Zweck erfüllen.
|
||||
- **Sei spezifisch in System-Prompts**: Definiere die Rolle, den Ton und die Einschränkungen des Agenten klar. Je spezifischer deine Anweisungen sind, desto besser kann der Agent seinen vorgesehenen Zweck erfüllen.
|
||||
- **Wähle die richtige Temperatureinstellung**: Verwende niedrigere Temperatureinstellungen (0-0,3), wenn Genauigkeit wichtig ist, oder erhöhe die Temperatur (0,7-2,0) für kreativere oder abwechslungsreichere Antworten
|
||||
- **Nutze Tools effektiv**: Integriere Tools, die den Zweck des Agenten ergänzen und seine Fähigkeiten erweitern. Sei selektiv bei der Auswahl der Tools, um den Agenten nicht zu überfordern. Für Aufgaben mit wenig Überschneidung verwende einen anderen Agent-Block für die besten Ergebnisse.
|
||||
- **Nutze Werkzeuge effektiv**: Integriere Werkzeuge, die den Zweck des Agenten ergänzen und seine Fähigkeiten verbessern. Sei selektiv bei der Auswahl der Werkzeuge, um den Agenten nicht zu überfordern. Für Aufgaben mit wenig Überschneidung verwende einen anderen Agent-Block für die besten Ergebnisse.
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
Das offizielle Python SDK für Sim ermöglicht es Ihnen, Workflows programmatisch aus Ihren Python-Anwendungen mithilfe des offiziellen Python SDKs auszuführen.
|
||||
|
||||
<Callout type="info">
|
||||
Das Python SDK unterstützt Python 3.8+ und bietet synchrone Workflow-Ausführung. Alle Workflow-Ausführungen sind derzeit synchron.
|
||||
Das Python SDK unterstützt Python 3.8+ mit asynchroner Ausführungsunterstützung, automatischer Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung.
|
||||
</Callout>
|
||||
|
||||
## Installation
|
||||
@@ -74,12 +74,17 @@ result = client.execute_workflow(
|
||||
- `workflow_id` (str): Die ID des auszuführenden Workflows
|
||||
- `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden
|
||||
- `timeout` (float, optional): Timeout in Sekunden (Standard: 30.0)
|
||||
- `stream` (bool, optional): Streaming-Antworten aktivieren (Standard: False)
|
||||
- `selected_outputs` (list[str], optional): Block-Ausgaben, die im `blockName.attribute`Format gestreamt werden sollen (z.B. `["agent1.content"]`)
|
||||
- `async_execution` (bool, optional): Asynchron ausführen (Standard: False)
|
||||
|
||||
**Rückgabewert:** `WorkflowExecutionResult`
|
||||
**Rückgabe:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
Wenn `async_execution=True`, wird sofort mit einer Task-ID zum Abfragen zurückgegeben. Andernfalls wird auf den Abschluss gewartet.
|
||||
|
||||
##### get_workflow_status()
|
||||
|
||||
Ruft den Status eines Workflows ab (Deployment-Status usw.).
|
||||
Den Status eines Workflows abrufen (Bereitstellungsstatus usw.).
|
||||
|
||||
```python
|
||||
status = client.get_workflow_status("workflow-id")
|
||||
@@ -93,7 +98,7 @@ print("Is deployed:", status.is_deployed)
|
||||
|
||||
##### validate_workflow()
|
||||
|
||||
Überprüft, ob ein Workflow für die Ausführung bereit ist.
|
||||
Überprüfen, ob ein Workflow für die Ausführung bereit ist.
|
||||
|
||||
```python
|
||||
is_ready = client.validate_workflow("workflow-id")
|
||||
@@ -107,28 +112,118 @@ if is_ready:
|
||||
|
||||
**Rückgabe:** `bool`
|
||||
|
||||
##### execute_workflow_sync()
|
||||
##### get_job_status()
|
||||
|
||||
<Callout type="info">
|
||||
Derzeit ist diese Methode identisch mit `execute_workflow()`, da alle Ausführungen synchron sind. Diese Methode wird für zukünftige Kompatibilität bereitgestellt, wenn asynchrone Ausführung hinzugefügt wird.
|
||||
</Callout>
|
||||
|
||||
Führt einen Workflow aus (derzeit synchron, identisch mit `execute_workflow()`).
|
||||
Den Status einer asynchronen Job-Ausführung abrufen.
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**Parameter:**
|
||||
- `task_id` (str): Die Task-ID, die von der asynchronen Ausführung zurückgegeben wurde
|
||||
|
||||
**Rückgabe:** `Dict[str, Any]`
|
||||
|
||||
**Antwortfelder:**
|
||||
- `success` (bool): Ob die Anfrage erfolgreich war
|
||||
- `taskId` (str): Die Task-ID
|
||||
- `status` (str): Einer der Werte `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (dict): Enthält `startedAt`, `completedAt` und `duration`
|
||||
- `output` (any, optional): Die Workflow-Ausgabe (wenn abgeschlossen)
|
||||
- `error` (any, optional): Fehlerdetails (wenn fehlgeschlagen)
|
||||
- `estimatedDuration` (int, optional): Geschätzte Dauer in Millisekunden (wenn in Bearbeitung/in Warteschlange)
|
||||
|
||||
##### execute_with_retry()
|
||||
|
||||
Einen Workflow mit automatischer Wiederholung bei Ratenbegrenzungsfehlern unter Verwendung von exponentiellem Backoff ausführen.
|
||||
|
||||
```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
|
||||
)
|
||||
```
|
||||
|
||||
**Parameter:**
|
||||
- `workflow_id` (str): Die ID des auszuführenden Workflows
|
||||
- `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden
|
||||
- `timeout` (float): Timeout für die initiale Anfrage in Sekunden
|
||||
- `timeout` (float, optional): Timeout in Sekunden
|
||||
- `stream` (bool, optional): Streaming-Antworten aktivieren
|
||||
- `selected_outputs` (list, optional): Block-Ausgaben zum Streamen
|
||||
- `async_execution` (bool, optional): Asynchron ausführen
|
||||
- `max_retries` (int, optional): Maximale Anzahl von Wiederholungen (Standard: 3)
|
||||
- `initial_delay` (float, optional): Anfängliche Verzögerung in Sekunden (Standard: 1.0)
|
||||
- `max_delay` (float, optional): Maximale Verzögerung in Sekunden (Standard: 30.0)
|
||||
- `backoff_multiplier` (float, optional): Backoff-Multiplikator (Standard: 2.0)
|
||||
|
||||
**Rückgabe:** `WorkflowExecutionResult`
|
||||
**Rückgabewert:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
Die Wiederholungslogik verwendet exponentielles Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um den Thundering-Herd-Effekt zu vermeiden. Wenn die API einen `retry-after` Header bereitstellt, wird dieser stattdessen verwendet.
|
||||
|
||||
##### get_rate_limit_info()
|
||||
|
||||
Ruft die aktuellen Rate-Limit-Informationen aus der letzten API-Antwort ab.
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
**Rückgabewert:** `RateLimitInfo | None`
|
||||
|
||||
##### get_usage_limits()
|
||||
|
||||
Ruft aktuelle Nutzungslimits und Kontingentinformationen für dein Konto ab.
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**Rückgabewert:** `UsageLimits`
|
||||
|
||||
**Antwortstruktur:**
|
||||
|
||||
```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 +265,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 +288,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 +319,13 @@ class SimStudioError(Exception):
|
||||
self.status = status
|
||||
```
|
||||
|
||||
**Häufige Fehlercodes:**
|
||||
- `UNAUTHORIZED`: Ungültiger API-Schlüssel
|
||||
- `TIMEOUT`: Zeitüberschreitung bei der Anfrage
|
||||
- `RATE_LIMIT_EXCEEDED`: Ratengrenze überschritten
|
||||
- `USAGE_LIMIT_EXCEEDED`: Nutzungsgrenze überschritten
|
||||
- `EXECUTION_ERROR`: Workflow-Ausführung fehlgeschlagen
|
||||
|
||||
## Beispiele
|
||||
|
||||
### Grundlegende Workflow-Ausführung
|
||||
@@ -214,7 +349,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 +387,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:
|
||||
@@ -279,16 +414,7 @@ def execute_with_error_handling():
|
||||
|
||||
Verwenden Sie den Client als Kontextmanager, um die Ressourcenbereinigung automatisch zu handhaben:
|
||||
|
||||
```python
|
||||
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:
|
||||
result = client.execute_workflow("workflow-id")
|
||||
print("Result:", result)
|
||||
# Session is automatically closed here
|
||||
```
|
||||
---CODE-PLACEHOLDER-ef99d3dd509e04865d5b6b0e0e03d3f8---
|
||||
|
||||
### Batch-Workflow-Ausführung
|
||||
|
||||
@@ -298,7 +424,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."""
|
||||
@@ -339,9 +465,233 @@ for result in results:
|
||||
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
|
||||
```
|
||||
|
||||
### Asynchrone Workflow-Ausführung
|
||||
|
||||
Führen Sie Workflows asynchron für lang laufende Aufgaben aus:
|
||||
|
||||
```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 und Wiederholungsversuche
|
||||
|
||||
Behandle Rate-Limits automatisch mit exponentiellem 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()
|
||||
```
|
||||
|
||||
### Nutzungsüberwachung
|
||||
|
||||
Überwache deine Kontonutzung und -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-Ausführung
|
||||
|
||||
Führe Workflows mit Echtzeit-Streaming-Antworten aus:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
Die Streaming-Antwort folgt dem 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-Beispiel:**
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
### Umgebungskonfiguration
|
||||
|
||||
Konfigurieren Sie den Client mit Umgebungsvariablen:
|
||||
Konfiguriere den Client mit Umgebungsvariablen:
|
||||
|
||||
<Tabs items={['Development', 'Production']}>
|
||||
<Tab value="Development">
|
||||
@@ -352,8 +702,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 +715,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")
|
||||
)
|
||||
```
|
||||
|
||||
@@ -382,19 +732,19 @@ Konfigurieren Sie den Client mit Umgebungsvariablen:
|
||||
|
||||
<Steps>
|
||||
<Step title="Bei Sim anmelden">
|
||||
Navigieren Sie zu [Sim](https://sim.ai) und melden Sie sich bei Ihrem Konto an.
|
||||
Navigiere zu [Sim](https://sim.ai) und melde dich bei deinem Konto an.
|
||||
</Step>
|
||||
<Step title="Ihren Workflow öffnen">
|
||||
Navigieren Sie zu dem Workflow, den Sie programmatisch ausführen möchten.
|
||||
<Step title="Öffne deinen Workflow">
|
||||
Navigiere zu dem Workflow, den du programmatisch ausführen möchtest.
|
||||
</Step>
|
||||
<Step title="Ihren Workflow bereitstellen">
|
||||
Klicken Sie auf "Deploy", um Ihren Workflow bereitzustellen, falls dies noch nicht geschehen ist.
|
||||
<Step title="Deploye deinen Workflow">
|
||||
Klicke auf "Deploy", um deinen Workflow zu deployen, falls dies noch nicht geschehen ist.
|
||||
</Step>
|
||||
<Step title="API-Schlüssel erstellen oder auswählen">
|
||||
Wählen Sie während des Bereitstellungsprozesses einen API-Schlüssel aus oder erstellen Sie einen neuen.
|
||||
<Step title="Erstelle oder wähle einen API-Schlüssel">
|
||||
Wähle während des Deployment-Prozesses einen API-Schlüssel aus oder erstelle einen neuen.
|
||||
</Step>
|
||||
<Step title="API-Schlüssel kopieren">
|
||||
Kopieren Sie den API-Schlüssel zur Verwendung in Ihrer Python-Anwendung.
|
||||
<Step title="Kopiere den API-Schlüssel">
|
||||
Kopiere den API-Schlüssel zur Verwendung in deiner Python-Anwendung.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
Das offizielle TypeScript/JavaScript SDK für Sim bietet vollständige Typsicherheit und unterstützt sowohl Node.js- als auch Browser-Umgebungen, sodass Sie Workflows programmatisch aus Ihren Node.js-Anwendungen, Webanwendungen und anderen JavaScript-Umgebungen ausführen können. Alle Workflow-Ausführungen sind derzeit synchron.
|
||||
Das offizielle TypeScript/JavaScript SDK für Sim bietet vollständige Typsicherheit und unterstützt sowohl Node.js- als auch Browser-Umgebungen, sodass Sie Workflows programmatisch aus Ihren Node.js-Anwendungen, Webanwendungen und anderen JavaScript-Umgebungen ausführen können.
|
||||
|
||||
<Callout type="info">
|
||||
Das TypeScript SDK bietet vollständige Typsicherheit und unterstützt sowohl Node.js- als auch Browser-Umgebungen. Alle Workflow-Ausführungen sind derzeit synchron.
|
||||
Das TypeScript SDK bietet vollständige Typsicherheit, Unterstützung für asynchrone Ausführung, automatische Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung.
|
||||
</Callout>
|
||||
|
||||
## Installation
|
||||
@@ -95,8 +95,13 @@ const result = await client.executeWorkflow('workflow-id', {
|
||||
- `options` (ExecutionOptions, optional):
|
||||
- `input` (any): Eingabedaten, die an den Workflow übergeben werden
|
||||
- `timeout` (number): Timeout in Millisekunden (Standard: 30000)
|
||||
- `stream` (boolean): Streaming-Antworten aktivieren (Standard: false)
|
||||
- `selectedOutputs` (string[]): Block-Ausgaben, die im `blockName.attribute`Format gestreamt werden sollen (z.B. `["agent1.content"]`)
|
||||
- `async` (boolean): Asynchron ausführen (Standard: false)
|
||||
|
||||
**Rückgabewert:** `Promise<WorkflowExecutionResult>`
|
||||
**Rückgabe:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
Wenn `async: true`, wird sofort mit einer Task-ID zum Abfragen zurückgegeben. Andernfalls wird auf den Abschluss gewartet.
|
||||
|
||||
##### getWorkflowStatus()
|
||||
|
||||
@@ -110,7 +115,7 @@ console.log('Is deployed:', status.isDeployed);
|
||||
**Parameter:**
|
||||
- `workflowId` (string): Die ID des Workflows
|
||||
|
||||
**Rückgabewert:** `Promise<WorkflowStatus>`
|
||||
**Rückgabe:** `Promise<WorkflowStatus>`
|
||||
|
||||
##### validateWorkflow()
|
||||
|
||||
@@ -126,34 +131,123 @@ if (isReady) {
|
||||
**Parameter:**
|
||||
- `workflowId` (string): Die ID des Workflows
|
||||
|
||||
**Rückgabewert:** `Promise<boolean>`
|
||||
**Rückgabe:** `Promise<boolean>`
|
||||
|
||||
##### executeWorkflowSync()
|
||||
##### getJobStatus()
|
||||
|
||||
<Callout type="info">
|
||||
Derzeit ist diese Methode identisch mit `executeWorkflow()`, da alle Ausführungen synchron sind. Diese Methode wird für zukünftige Kompatibilität bereitgestellt, wenn asynchrone Ausführung hinzugefügt wird.
|
||||
</Callout>
|
||||
|
||||
Einen Workflow ausführen (derzeit synchron, identisch mit `executeWorkflow()`).
|
||||
Den Status einer asynchronen Job-Ausführung abrufen.
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
**Parameter:**
|
||||
- `taskId` (string): Die Task-ID, die von der asynchronen Ausführung zurückgegeben wurde
|
||||
|
||||
**Rückgabe:** `Promise<JobStatus>`
|
||||
|
||||
**Antwortfelder:**
|
||||
- `success` (boolean): Ob die Anfrage erfolgreich war
|
||||
- `taskId` (string): Die Task-ID
|
||||
- `status` (string): Einer der Werte `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (object): Enthält `startedAt`, `completedAt` und `duration`
|
||||
- `output` (any, optional): Die Workflow-Ausgabe (wenn abgeschlossen)
|
||||
- `error` (any, optional): Fehlerdetails (wenn fehlgeschlagen)
|
||||
- `estimatedDuration` (number, optional): Geschätzte Dauer in Millisekunden (wenn in Bearbeitung/in der Warteschlange)
|
||||
|
||||
##### executeWithRetry()
|
||||
|
||||
Führt einen Workflow mit automatischer Wiederholung bei Ratenlimitfehlern unter Verwendung von exponentiellem Backoff aus.
|
||||
|
||||
```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
|
||||
});
|
||||
```
|
||||
|
||||
**Parameter:**
|
||||
- `workflowId` (string): Die ID des auszuführenden Workflows
|
||||
- `options` (ExecutionOptions, optional):
|
||||
- `input` (any): Eingabedaten, die an den Workflow übergeben werden
|
||||
- `timeout` (number): Timeout für die initiale Anfrage in Millisekunden
|
||||
- `options` (ExecutionOptions, optional): Gleich wie `executeWorkflow()`
|
||||
- `retryOptions` (RetryOptions, optional):
|
||||
- `maxRetries` (number): Maximale Anzahl von Wiederholungen (Standard: 3)
|
||||
- `initialDelay` (number): Anfängliche Verzögerung in ms (Standard: 1000)
|
||||
- `maxDelay` (number): Maximale Verzögerung in ms (Standard: 30000)
|
||||
- `backoffMultiplier` (number): Backoff-Multiplikator (Standard: 2)
|
||||
|
||||
**Rückgabewert:** `Promise<WorkflowExecutionResult>`
|
||||
**Rückgabewert:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
Die Wiederholungslogik verwendet exponentiellen Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um den Thundering-Herd-Effekt zu vermeiden. Wenn die API einen `retry-after`Header bereitstellt, wird dieser stattdessen verwendet.
|
||||
|
||||
##### getRateLimitInfo()
|
||||
|
||||
Ruft die aktuellen Ratenlimit-Informationen aus der letzten API-Antwort ab.
|
||||
|
||||
```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));
|
||||
}
|
||||
```
|
||||
|
||||
**Rückgabewert:** `RateLimitInfo | null`
|
||||
|
||||
##### getUsageLimits()
|
||||
|
||||
Ruft aktuelle Nutzungslimits und Kontingentinformationen für Ihr Konto ab.
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
**Rückgabewert:** `Promise<UsageLimits>`
|
||||
|
||||
**Antwortstruktur:**
|
||||
|
||||
```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()
|
||||
|
||||
Den API-Schlüssel aktualisieren.
|
||||
Aktualisiert den API-Schlüssel.
|
||||
|
||||
```typescript
|
||||
client.setApiKey('new-api-key');
|
||||
@@ -161,7 +255,7 @@ client.setApiKey('new-api-key');
|
||||
|
||||
##### setBaseUrl()
|
||||
|
||||
Die Basis-URL aktualisieren.
|
||||
Aktualisiert die Basis-URL.
|
||||
|
||||
```typescript
|
||||
client.setBaseUrl('https://my-custom-domain.com');
|
||||
@@ -187,6 +281,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
|
||||
@@ -198,6 +306,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
|
||||
@@ -207,6 +354,13 @@ class SimStudioError extends Error {
|
||||
}
|
||||
```
|
||||
|
||||
**Häufige Fehlercodes:**
|
||||
- `UNAUTHORIZED`: Ungültiger API-Schlüssel
|
||||
- `TIMEOUT`: Zeitüberschreitung der Anfrage
|
||||
- `RATE_LIMIT_EXCEEDED`: Ratengrenze überschritten
|
||||
- `USAGE_LIMIT_EXCEEDED`: Nutzungsgrenze überschritten
|
||||
- `EXECUTION_ERROR`: Workflow-Ausführung fehlgeschlagen
|
||||
|
||||
## Beispiele
|
||||
|
||||
### Grundlegende Workflow-Ausführung
|
||||
@@ -230,7 +384,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 +425,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 +469,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 +487,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 +511,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 +553,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(
|
||||
@@ -467,16 +621,16 @@ document.getElementById('executeBtn')?.addEventListener('click', executeClientSi
|
||||
Bei der Verwendung des SDK im Browser sollten Sie darauf achten, keine sensiblen API-Schlüssel offenzulegen. Erwägen Sie die Verwendung eines Backend-Proxys oder öffentlicher API-Schlüssel mit eingeschränkten Berechtigungen.
|
||||
</Callout>
|
||||
|
||||
### React Hook Beispiel
|
||||
### React Hook-Beispiel
|
||||
|
||||
Erstellen Sie einen benutzerdefinierten React Hook für die Workflow-Ausführung:
|
||||
Erstellen eines benutzerdefinierten React-Hooks für die Workflow-Ausführung:
|
||||
|
||||
```typescript
|
||||
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 {
|
||||
@@ -532,7 +686,7 @@ function WorkflowComponent() {
|
||||
<button onClick={handleExecute} disabled={loading}>
|
||||
{loading ? 'Executing...' : 'Execute Workflow'}
|
||||
</button>
|
||||
|
||||
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
{result && (
|
||||
<div>
|
||||
@@ -545,38 +699,267 @@ function WorkflowComponent() {
|
||||
}
|
||||
```
|
||||
|
||||
## Ihren API-Schlüssel erhalten
|
||||
### Asynchrone Workflow-Ausführung
|
||||
|
||||
Führen Sie Workflows asynchron für lang laufende Aufgaben aus:
|
||||
|
||||
```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 und Wiederholungsversuche
|
||||
|
||||
Automatische Behandlung von Rate-Limits mit exponentiellem 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Nutzungsüberwachung
|
||||
|
||||
Überwachen Sie Ihre Kontonutzung und -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:
|
||||
|
||||
### 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 {
|
||||
// Streaming für bestimmte Block-Ausgaben aktivieren
|
||||
const result = await client.executeWorkflow('workflow-id', {
|
||||
input: { message: 'Count to five' },
|
||||
stream: true,
|
||||
selectedOutputs: ['agent1.content'] // Format blockName.attribute verwenden
|
||||
});
|
||||
|
||||
console.log('Workflow-Ergebnis:', result);
|
||||
} catch (error) {
|
||||
console.error('Fehler:', 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":", zwei"}
|
||||
|
||||
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('');
|
||||
|
||||
// WICHTIG: Führen Sie diesen API-Aufruf von Ihrem Backend-Server aus, nicht vom Browser
|
||||
// Setzen Sie niemals Ihren API-Schlüssel im Client-seitigen Code frei
|
||||
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! // Nur serverseitige Umgebungsvariable
|
||||
},
|
||||
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('Ausführung abgeschlossen:', parsed.metadata);
|
||||
}
|
||||
} catch (e) {
|
||||
// Ungültiges JSON überspringen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={executeStreaming} disabled={loading}>
|
||||
{loading ? 'Generiere...' : 'Streaming starten'}
|
||||
</button>
|
||||
<div style={{ whiteSpace: 'pre-wrap' }}>{output}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Your API Key
|
||||
|
||||
<Steps>
|
||||
<Step title="Bei Sim anmelden">
|
||||
Navigieren Sie zu [Sim](https://sim.ai) und melden Sie sich bei Ihrem Konto an.
|
||||
<Step title="Log in to Sim">
|
||||
Navigate to [Sim](https://sim.ai) and log in to your account.
|
||||
</Step>
|
||||
<Step title="Öffnen Sie Ihren Workflow">
|
||||
Navigieren Sie zu dem Workflow, den Sie programmatisch ausführen möchten.
|
||||
<Step title="Open your workflow">
|
||||
Navigate to the workflow you want to execute programmatically.
|
||||
</Step>
|
||||
<Step title="Deployen Sie Ihren Workflow">
|
||||
Klicken Sie auf "Deploy", um Ihren Workflow zu deployen, falls dies noch nicht geschehen ist.
|
||||
<Step title="Deploy your workflow">
|
||||
Click on "Deploy" to deploy your workflow if it hasn't been deployed yet.
|
||||
</Step>
|
||||
<Step title="Erstellen oder wählen Sie einen API-Schlüssel">
|
||||
Wählen Sie während des Deployment-Prozesses einen API-Schlüssel aus oder erstellen Sie einen neuen.
|
||||
<Step title="Create or select an API key">
|
||||
During the deployment process, select or create an API key.
|
||||
</Step>
|
||||
<Step title="Kopieren Sie den API-Schlüssel">
|
||||
Kopieren Sie den API-Schlüssel zur Verwendung in Ihrer TypeScript/JavaScript-Anwendung.
|
||||
<Step title="Copy the API key">
|
||||
Copy the API key to use in your TypeScript/JavaScript application.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning">
|
||||
Halten Sie Ihren API-Schlüssel sicher und committen Sie ihn niemals in die Versionskontrolle. Verwenden Sie Umgebungsvariablen oder sicheres Konfigurationsmanagement.
|
||||
Keep your API key secure and never commit it to version control. Use environment variables or secure configuration management.
|
||||
</Callout>
|
||||
|
||||
## Anforderungen
|
||||
## Requirements
|
||||
|
||||
- Node.js 16+
|
||||
- TypeScript 5.0+ (für TypeScript-Projekte)
|
||||
- TypeScript 5.0+ (for TypeScript projects)
|
||||
|
||||
## TypeScript-Unterstützung
|
||||
## TypeScript Support
|
||||
|
||||
Das SDK ist in TypeScript geschrieben und bietet vollständige Typsicherheit:
|
||||
The SDK is written in TypeScript and provides full type safety:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -586,22 +969,22 @@ import {
|
||||
SimStudioError
|
||||
} from 'simstudio-ts-sdk';
|
||||
|
||||
// Type-safe client initialization
|
||||
// Typsichere Client-Initialisierung
|
||||
const client: SimStudioClient = new SimStudioClient({
|
||||
apiKey: process.env.SIMSTUDIO_API_KEY!
|
||||
apiKey: process.env.SIM_API_KEY!
|
||||
});
|
||||
|
||||
// Type-safe workflow execution
|
||||
// Typsichere Workflow-Ausführung
|
||||
const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-id', {
|
||||
input: {
|
||||
message: 'Hello, TypeScript!'
|
||||
}
|
||||
});
|
||||
|
||||
// Type-safe status checking
|
||||
// Typsichere Statusprüfung
|
||||
const status: WorkflowStatus = await client.getWorkflowStatus('workflow-id');
|
||||
```
|
||||
|
||||
## Lizenz
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -24,15 +24,7 @@ Der API-Trigger stellt Ihren Workflow als sicheren HTTP-Endpunkt bereit. Senden
|
||||
|
||||
Fügen Sie für jeden Parameter ein Feld **Eingabeformat** hinzu. Die Ausgabeschlüssel zur Laufzeit spiegeln das Schema wider und sind auch unter `<api.input>` verfügbar.
|
||||
|
||||
```yaml
|
||||
- type: string
|
||||
name: userId
|
||||
value: demo-user # optional manual test value
|
||||
- type: number
|
||||
name: maxTokens
|
||||
```
|
||||
|
||||
Manuelle Ausführungen im Editor verwenden die Spalte `value`, damit Sie testen können, ohne eine Anfrage zu senden. Während der Ausführung füllt der Resolver sowohl `<api.userId>` als auch `<api.input.userId>`.
|
||||
Manuelle Ausführungen im Editor verwenden die Spalte `value`, damit Sie testen können, ohne eine Anfrage zu senden. Während der Ausführung füllt der Resolver sowohl `<api.userId>` als auch `<api.input.userId>` aus.
|
||||
|
||||
## Anfrage-Beispiel
|
||||
|
||||
@@ -46,15 +38,93 @@ curl -X POST \
|
||||
|
||||
Erfolgreiche Antworten geben das serialisierte Ausführungsergebnis vom Executor zurück. Fehler zeigen Validierungs-, Authentifizierungs- oder Workflow-Fehler an.
|
||||
|
||||
## Ausgabe-Referenz
|
||||
## Streaming-Antworten
|
||||
|
||||
Aktivieren Sie Echtzeit-Streaming, um Workflow-Ausgaben zu erhalten, während sie zeichen-für-zeichen generiert werden. Dies ist nützlich, um KI-Antworten progressiv für Benutzer anzuzeigen.
|
||||
|
||||
### Anfrageparameter
|
||||
|
||||
Fügen Sie diese Parameter hinzu, um Streaming zu aktivieren:
|
||||
|
||||
- `stream` - Auf `true` setzen, um Server-Sent Events (SSE) Streaming zu aktivieren
|
||||
- `selectedOutputs` - Array von Block-Ausgaben zum Streamen (z.B. `["agent1.content"]`)
|
||||
|
||||
### Block-Ausgabeformat
|
||||
|
||||
Verwenden Sie das `blockName.attribute` Format, um anzugeben, welche Block-Ausgaben gestreamt werden sollen:
|
||||
- Format: `"blockName.attribute"` (z.B. Wenn Sie den Inhalt des Agent 1-Blocks streamen möchten, würden Sie `"agent1.content"` verwenden)
|
||||
- Blocknamen sind nicht case-sensitive und Leerzeichen werden ignoriert
|
||||
|
||||
### Beispielanfrage
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Antwortformat
|
||||
|
||||
Streaming-Antworten verwenden das 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]
|
||||
```
|
||||
|
||||
Jedes Ereignis enthält:
|
||||
- **Streaming-Chunks**: `{"blockId": "...", "chunk": "text"}` - Echtzeit-Text während er generiert wird
|
||||
- **Abschlussereignis**: `{"event": "done", ...}` - Ausführungsmetadaten und vollständige Ergebnisse
|
||||
- **Terminator**: `[DONE]` - Signalisiert das Ende des Streams
|
||||
|
||||
### Streaming mehrerer Blöcke
|
||||
|
||||
Wenn `selectedOutputs` mehrere Blöcke enthält, zeigt jeder Chunk an, welcher Block ihn erzeugt hat:
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
Das Feld `blockId` in jedem Chunk ermöglicht es Ihnen, die Ausgabe zum richtigen UI-Element zu leiten:
|
||||
|
||||
```
|
||||
data: {"blockId":"agent1-uuid","chunk":"Processing..."}
|
||||
|
||||
data: {"blockId":"agent2-uuid","chunk":"Analyzing..."}
|
||||
|
||||
data: {"blockId":"agent1-uuid","chunk":" complete"}
|
||||
```
|
||||
|
||||
## Ausgabereferenz
|
||||
|
||||
| Referenz | Beschreibung |
|
||||
|-----------|-------------|
|
||||
| `<api.field>` | Im Eingabeformat definiertes Feld |
|
||||
| `<api.input>` | Gesamter strukturierter Anfragekörper |
|
||||
|
||||
Wenn kein Eingabeformat definiert ist, stellt der Executor das rohe JSON nur unter `<api.input>` bereit.
|
||||
Wenn kein Eingabeformat definiert ist, stellt der Executor das rohe JSON nur unter `<api.input>` zur Verfügung.
|
||||
|
||||
<Callout type="warning">
|
||||
Ein Workflow kann nur einen API-Trigger enthalten. Veröffentlichen Sie eine neue Bereitstellung nach Änderungen, damit der Endpunkt aktuell bleibt.
|
||||
Ein Workflow kann nur einen API-Trigger enthalten. Veröffentlichen Sie nach Änderungen eine neue Bereitstellung, damit der Endpunkt aktuell bleibt.
|
||||
</Callout>
|
||||
|
||||
@@ -10,7 +10,6 @@ type: object
|
||||
required:
|
||||
- type
|
||||
- name
|
||||
- inputs
|
||||
- connections
|
||||
properties:
|
||||
type:
|
||||
@@ -22,21 +21,23 @@ properties:
|
||||
description: Display name for this loop block
|
||||
inputs:
|
||||
type: object
|
||||
required:
|
||||
- loopType
|
||||
description: Optional. If omitted, defaults will be applied.
|
||||
properties:
|
||||
loopType:
|
||||
type: string
|
||||
enum: [for, forEach]
|
||||
description: Type of loop to execute
|
||||
default: for
|
||||
iterations:
|
||||
type: number
|
||||
description: Number of iterations (for 'for' loops)
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
collection:
|
||||
type: string
|
||||
description: Collection to iterate over (for 'forEach' loops)
|
||||
default: ""
|
||||
maxConcurrency:
|
||||
type: number
|
||||
description: Maximum concurrent executions
|
||||
@@ -45,13 +46,10 @@ properties:
|
||||
maximum: 10
|
||||
connections:
|
||||
type: object
|
||||
required:
|
||||
- loop
|
||||
properties:
|
||||
# Nested format (recommended)
|
||||
loop:
|
||||
type: object
|
||||
required:
|
||||
- start
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
@@ -59,26 +57,37 @@ properties:
|
||||
end:
|
||||
type: string
|
||||
description: Target block ID for loop completion (optional)
|
||||
# Direct handle format (alternative)
|
||||
loop-start-source:
|
||||
type: string | string[]
|
||||
description: Target block ID to execute inside the loop (direct format)
|
||||
loop-end-source:
|
||||
type: string | string[]
|
||||
description: Target block ID for loop completion (direct format, optional)
|
||||
error:
|
||||
type: string
|
||||
description: Target block ID for error handling
|
||||
note: Use either the nested 'loop' format OR the direct 'loop-start-source' format, not both
|
||||
```
|
||||
|
||||
## Verbindungskonfiguration
|
||||
|
||||
Loop-Blöcke verwenden ein spezielles Verbindungsformat mit einem `loop`Abschnitt:
|
||||
Loop-Blöcke unterstützen zwei Verbindungsformate:
|
||||
|
||||
### Direktes Handle-Format (Alternative)
|
||||
|
||||
```yaml
|
||||
connections:
|
||||
loop:
|
||||
start: <string> # Target block ID to execute inside the loop
|
||||
end: <string> # Target block ID after loop completion (optional)
|
||||
loop-start-source: <string> # Target block ID to execute inside the loop
|
||||
loop-end-source: <string> # Target block ID after loop completion (optional)
|
||||
error: <string> # Target block ID for error handling (optional)
|
||||
```
|
||||
|
||||
Beide Formate funktionieren identisch. Verwenden Sie das Format, das Ihnen besser gefällt.
|
||||
|
||||
## Konfiguration von untergeordneten Blöcken
|
||||
|
||||
Blöcke innerhalb einer Schleife müssen ihre `parentId` auf die Loop-Block-ID setzen:
|
||||
Blöcke innerhalb einer Schleife müssen ihre `parentId` auf die Loop-Block-ID gesetzt haben. Die Eigenschaft `extent` wird automatisch auf `'parent'` gesetzt und muss nicht angegeben werden:
|
||||
|
||||
```yaml
|
||||
loop-1:
|
||||
@@ -106,7 +115,7 @@ process-item:
|
||||
|
||||
## Beispiele
|
||||
|
||||
### For-Schleife (feste Iterationen)
|
||||
### For-Schleife (feste Anzahl von Iterationen)
|
||||
|
||||
```yaml
|
||||
countdown-loop:
|
||||
@@ -227,7 +236,7 @@ store-analysis:
|
||||
};
|
||||
```
|
||||
|
||||
### Schleife mit paralleler Verarbeitung
|
||||
### Schleife für parallele Verarbeitung
|
||||
|
||||
```yaml
|
||||
parallel-processing-loop:
|
||||
@@ -261,9 +270,62 @@ process-task:
|
||||
success: task-completed
|
||||
```
|
||||
|
||||
### Beispiel für direktes Handle-Format
|
||||
|
||||
Dieselbe Schleife kann mit dem direkten Handle-Format geschrieben werden:
|
||||
|
||||
```yaml
|
||||
my-loop:
|
||||
type: loop
|
||||
name: "Process Items"
|
||||
inputs:
|
||||
loopType: forEach
|
||||
collection: <start.items>
|
||||
connections:
|
||||
loop-start-source: process-item # Direct handle format
|
||||
loop-end-source: final-results # Direct handle format
|
||||
error: handle-error
|
||||
|
||||
process-item:
|
||||
type: agent
|
||||
name: "Process Item"
|
||||
parentId: my-loop
|
||||
inputs:
|
||||
systemPrompt: "Process this item"
|
||||
userPrompt: <loop.currentItem>
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
### Minimales Schleifenbeispiel (mit Standardwerten)
|
||||
|
||||
Sie können den Abschnitt `inputs` vollständig weglassen, dann werden Standardwerte angewendet:
|
||||
|
||||
```yaml
|
||||
simple-loop:
|
||||
type: loop
|
||||
name: "Simple Loop"
|
||||
# No inputs section - defaults to loopType: 'for', iterations: 5
|
||||
connections:
|
||||
loop-start-source: process-step
|
||||
loop-end-source: complete
|
||||
|
||||
process-step:
|
||||
type: agent
|
||||
name: "Process Step"
|
||||
parentId: simple-loop
|
||||
inputs:
|
||||
systemPrompt: "Execute step"
|
||||
userPrompt: "Step <loop.index>"
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
Diese Schleife führt standardmäßig 5 Iterationen aus.
|
||||
|
||||
## Schleifenvariablen
|
||||
|
||||
Innerhalb von untergeordneten Schleifenblöcken sind diese speziellen Variablen verfügbar:
|
||||
Innerhalb von Schleifenunterblöcken sind diese speziellen Variablen verfügbar:
|
||||
|
||||
```yaml
|
||||
# Available in all child blocks of the loop
|
||||
@@ -290,6 +352,6 @@ final-processor:
|
||||
- Verwenden Sie forEach für die Verarbeitung von Sammlungen, for-Schleifen für feste Iterationen
|
||||
- Erwägen Sie die Verwendung von maxConcurrency für I/O-gebundene Operationen
|
||||
- Integrieren Sie Fehlerbehandlung für eine robuste Schleifenausführung
|
||||
- Verwenden Sie aussagekräftige Namen für Schleifen-Unterblöcke
|
||||
- Verwenden Sie aussagekräftige Namen für Schleifenunterblöcke
|
||||
- Testen Sie zuerst mit kleinen Sammlungen
|
||||
- Überwachen Sie die Ausführungszeit bei großen Sammlungen
|
||||
- Überwachen Sie die Ausführungszeit für große Sammlungen
|
||||
251
apps/docs/content/docs/en/blocks/guardrails.mdx
Normal file
251
apps/docs/content/docs/en/blocks/guardrails.mdx
Normal file
@@ -0,0 +1,251 @@
|
||||
---
|
||||
title: Guardrails
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
import { Image } from '@/components/ui/image'
|
||||
import { Video } from '@/components/ui/video'
|
||||
|
||||
The Guardrails block validates and protects your AI workflows by checking content against multiple validation types. Ensure data quality, prevent hallucinations, detect PII, and enforce format requirements before content moves through your workflow.
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Image
|
||||
src="/static/blocks/guardrails.png"
|
||||
alt="Guardrails Block"
|
||||
width={500}
|
||||
height={350}
|
||||
className="my-6"
|
||||
/>
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
The Guardrails block enables you to:
|
||||
|
||||
<Steps>
|
||||
<Step>
|
||||
<strong>Validate JSON Structure</strong>: Ensure LLM outputs are valid JSON before parsing
|
||||
</Step>
|
||||
<Step>
|
||||
<strong>Match Regex Patterns</strong>: Verify content matches specific formats (emails, phone numbers, URLs, etc.)
|
||||
</Step>
|
||||
<Step>
|
||||
<strong>Detect Hallucinations</strong>: Use RAG + LLM scoring to validate AI outputs against knowledge base content
|
||||
</Step>
|
||||
<Step>
|
||||
<strong>Detect PII</strong>: Identify and optionally mask personally identifiable information across 40+ entity types
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Validation Types
|
||||
|
||||
### JSON Validation
|
||||
|
||||
Validates that content is properly formatted JSON. Perfect for ensuring structured LLM outputs can be safely parsed.
|
||||
|
||||
**Use Cases:**
|
||||
- Validate JSON responses from Agent blocks before parsing
|
||||
- Ensure API payloads are properly formatted
|
||||
- Check structured data integrity
|
||||
|
||||
**Output:**
|
||||
- `passed`: `true` if valid JSON, `false` otherwise
|
||||
- `error`: Error message if validation fails (e.g., "Invalid JSON: Unexpected token...")
|
||||
|
||||
### Regex Validation
|
||||
|
||||
Checks if content matches a specified regular expression pattern.
|
||||
|
||||
**Use Cases:**
|
||||
- Validate email addresses
|
||||
- Check phone number formats
|
||||
- Verify URLs or custom identifiers
|
||||
- Enforce specific text patterns
|
||||
|
||||
**Configuration:**
|
||||
- **Regex Pattern**: The regular expression to match against (e.g., `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` for emails)
|
||||
|
||||
**Output:**
|
||||
- `passed`: `true` if content matches pattern, `false` otherwise
|
||||
- `error`: Error message if validation fails
|
||||
|
||||
### Hallucination Detection
|
||||
|
||||
Uses Retrieval-Augmented Generation (RAG) with LLM scoring to detect when AI-generated content contradicts or isn't grounded in your knowledge base.
|
||||
|
||||
**How It Works:**
|
||||
1. Queries your knowledge base for relevant context
|
||||
2. Sends both the AI output and retrieved context to an LLM
|
||||
3. LLM assigns a confidence score (0-10 scale)
|
||||
- **0** = Full hallucination (completely ungrounded)
|
||||
- **10** = Fully grounded (completely supported by knowledge base)
|
||||
4. Validation passes if score ≥ threshold (default: 3)
|
||||
|
||||
**Configuration:**
|
||||
- **Knowledge Base**: Select from your existing knowledge bases
|
||||
- **Model**: Choose LLM for scoring (requires strong reasoning - GPT-4o, Claude 3.7 Sonnet recommended)
|
||||
- **API Key**: Authentication for selected LLM provider (auto-hidden for hosted/Ollama models)
|
||||
- **Confidence Threshold**: Minimum score to pass (0-10, default: 3)
|
||||
- **Top K** (Advanced): Number of knowledge base chunks to retrieve (default: 10)
|
||||
|
||||
**Output:**
|
||||
- `passed`: `true` if confidence score ≥ threshold
|
||||
- `score`: Confidence score (0-10)
|
||||
- `reasoning`: LLM's explanation for the score
|
||||
- `error`: Error message if validation fails
|
||||
|
||||
**Use Cases:**
|
||||
- Validate Agent responses against documentation
|
||||
- Ensure customer support answers are factually accurate
|
||||
- Verify generated content matches source material
|
||||
- Quality control for RAG applications
|
||||
|
||||
### PII Detection
|
||||
|
||||
Detects personally identifiable information using Microsoft Presidio. Supports 40+ entity types across multiple countries and languages.
|
||||
|
||||
<div className="mx-auto w-3/5 overflow-hidden rounded-lg">
|
||||
<Video src="guardrails.mp4" width={500} height={350} />
|
||||
</div>
|
||||
|
||||
**How It Works:**
|
||||
1. Scans content for PII entities using pattern matching and NLP
|
||||
2. Returns detected entities with locations and confidence scores
|
||||
3. Optionally masks detected PII in the output
|
||||
|
||||
**Configuration:**
|
||||
- **PII Types to Detect**: Select from grouped categories via modal selector
|
||||
- **Common**: Person name, Email, Phone, Credit card, IP address, etc.
|
||||
- **USA**: SSN, Driver's license, Passport, etc.
|
||||
- **UK**: NHS number, National insurance number
|
||||
- **Spain**: NIF, NIE, CIF
|
||||
- **Italy**: Fiscal code, Driver's license, VAT code
|
||||
- **Poland**: PESEL, NIP, REGON
|
||||
- **Singapore**: NRIC/FIN, UEN
|
||||
- **Australia**: ABN, ACN, TFN, Medicare
|
||||
- **India**: Aadhaar, PAN, Passport, Voter number
|
||||
- **Mode**:
|
||||
- **Detect**: Only identify PII (default)
|
||||
- **Mask**: Replace detected PII with masked values
|
||||
- **Language**: Detection language (default: English)
|
||||
|
||||
**Output:**
|
||||
- `passed`: `false` if any selected PII types are detected
|
||||
- `detectedEntities`: Array of detected PII with type, location, and confidence
|
||||
- `maskedText`: Content with PII masked (only if mode = "Mask")
|
||||
- `error`: Error message if validation fails
|
||||
|
||||
**Use Cases:**
|
||||
- Block content containing sensitive personal information
|
||||
- Mask PII before logging or storing data
|
||||
- Compliance with GDPR, HIPAA, and other privacy regulations
|
||||
- Sanitize user inputs before processing
|
||||
|
||||
## Configuration
|
||||
|
||||
### Content to Validate
|
||||
|
||||
The input content to validate. This typically comes from:
|
||||
- Agent block outputs: `<agent.content>`
|
||||
- Function block results: `<function.output>`
|
||||
- API responses: `<api.output>`
|
||||
- Any other block output
|
||||
|
||||
### Validation Type
|
||||
|
||||
Choose from four validation types:
|
||||
- **Valid JSON**: Check if content is properly formatted JSON
|
||||
- **Regex Match**: Verify content matches a regex pattern
|
||||
- **Hallucination Check**: Validate against knowledge base with LLM scoring
|
||||
- **PII Detection**: Detect and optionally mask personally identifiable information
|
||||
|
||||
## Outputs
|
||||
|
||||
All validation types return:
|
||||
|
||||
- **`<guardrails.passed>`**: Boolean indicating if validation passed
|
||||
- **`<guardrails.validationType>`**: The type of validation performed
|
||||
- **`<guardrails.input>`**: The original input that was validated
|
||||
- **`<guardrails.error>`**: Error message if validation failed (optional)
|
||||
|
||||
Additional outputs by type:
|
||||
|
||||
**Hallucination Check:**
|
||||
- **`<guardrails.score>`**: Confidence score (0-10)
|
||||
- **`<guardrails.reasoning>`**: LLM's explanation
|
||||
|
||||
**PII Detection:**
|
||||
- **`<guardrails.detectedEntities>`**: Array of detected PII entities
|
||||
- **`<guardrails.maskedText>`**: Content with PII masked (if mode = "Mask")
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
### Validate JSON Before Parsing
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Scenario: Ensure Agent output is valid JSON</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>Agent generates structured JSON response</li>
|
||||
<li>Guardrails validates JSON format</li>
|
||||
<li>Condition block checks `<guardrails.passed>`</li>
|
||||
<li>If passed → Parse and use data, If failed → Retry or handle error</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### Prevent Hallucinations
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Scenario: Validate customer support responses</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>Agent generates response to customer question</li>
|
||||
<li>Guardrails checks against support documentation knowledge base</li>
|
||||
<li>If confidence score ≥ 3 → Send response</li>
|
||||
<li>If confidence score \< 3 → Flag for human review</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### Block PII in User Inputs
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Scenario: Sanitize user-submitted content</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>User submits form with text content</li>
|
||||
<li>Guardrails detects PII (emails, phone numbers, SSN, etc.)</li>
|
||||
<li>If PII detected → Reject submission or mask sensitive data</li>
|
||||
<li>If no PII → Process normally</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto w-3/5 overflow-hidden rounded-lg">
|
||||
<Video src="guardrails-example.mp4" width={500} height={350} />
|
||||
</div>
|
||||
|
||||
### Validate Email Format
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Scenario: Check email address format</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>Agent extracts email from text</li>
|
||||
<li>Guardrails validates with regex pattern</li>
|
||||
<li>If valid → Use email for notification</li>
|
||||
<li>If invalid → Request correction</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Chain with Condition blocks**: Use `<guardrails.passed>` to branch workflow logic based on validation results
|
||||
- **Use JSON validation before parsing**: Always validate JSON structure before attempting to parse LLM outputs
|
||||
- **Choose appropriate PII types**: Only select the PII entity types relevant to your use case for better performance
|
||||
- **Set reasonable confidence thresholds**: For hallucination detection, adjust threshold based on your accuracy requirements (higher = stricter)
|
||||
- **Use strong models for hallucination detection**: GPT-4o or Claude 3.7 Sonnet provide more accurate confidence scoring
|
||||
- **Mask PII for logging**: Use "Mask" mode when you need to log or store content that may contain PII
|
||||
- **Test regex patterns**: Validate your regex patterns thoroughly before deploying to production
|
||||
- **Monitor validation failures**: Track `<guardrails.error>` messages to identify common validation issues
|
||||
|
||||
<Callout type="info">
|
||||
Guardrails validation happens synchronously in your workflow. For hallucination detection, choose faster models (like GPT-4o-mini) if latency is critical.
|
||||
</Callout>
|
||||
|
||||
@@ -166,6 +166,38 @@ Different subscription plans have different usage limits:
|
||||
| **Team** | $500 (pooled) | 50 sync, 100 async |
|
||||
| **Enterprise** | Custom | Custom |
|
||||
|
||||
## Billing Model
|
||||
|
||||
Sim uses a **base subscription + overage** billing model:
|
||||
|
||||
### How It Works
|
||||
|
||||
**Pro Plan ($20/month):**
|
||||
- Monthly subscription includes $20 of usage
|
||||
- Usage under $20 → No additional charges
|
||||
- Usage over $20 → Pay the overage at month end
|
||||
- Example: $35 usage = $20 (subscription) + $15 (overage)
|
||||
|
||||
**Team Plan ($40/seat/month):**
|
||||
- Pooled usage across all team members
|
||||
- Overage calculated from total team usage
|
||||
- Organization owner receives one bill
|
||||
|
||||
**Enterprise Plans:**
|
||||
- Fixed monthly price, no overages
|
||||
- Custom usage limits per agreement
|
||||
|
||||
### Threshold Billing
|
||||
|
||||
When unbilled overage reaches $50, Sim automatically bills the full unbilled amount.
|
||||
|
||||
**Example:**
|
||||
- Day 10: $70 overage → Bill $70 immediately
|
||||
- Day 15: Additional $35 usage ($105 total) → Already billed, no action
|
||||
- Day 20: Another $50 usage ($155 total, $85 unbilled) → Bill $85 immediately
|
||||
|
||||
This spreads large overage charges throughout the month instead of one large bill at period end.
|
||||
|
||||
## Cost Management Best Practices
|
||||
|
||||
1. **Monitor Regularly**: Check your usage dashboard frequently to avoid surprises
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(
|
||||
@@ -440,14 +593,91 @@ async function executeClientSideWorkflow() {
|
||||
});
|
||||
|
||||
console.log('Workflow result:', result);
|
||||
|
||||
|
||||
// Update UI with result
|
||||
document.getElementById('result')!.textContent =
|
||||
document.getElementById('result')!.textContent =
|
||||
JSON.stringify(result.output, null, 2);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### File Upload
|
||||
|
||||
File objects are automatically detected and converted to base64 format. Include them in your input under the field name matching your workflow's API trigger input format.
|
||||
|
||||
The SDK converts File objects to this format:
|
||||
```typescript
|
||||
{
|
||||
type: 'file',
|
||||
data: 'data:mime/type;base64,base64data',
|
||||
name: 'filename',
|
||||
mime: 'mime/type'
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can manually provide files using the URL format:
|
||||
```typescript
|
||||
{
|
||||
type: 'url',
|
||||
data: 'https://example.com/file.pdf',
|
||||
name: 'file.pdf',
|
||||
mime: 'application/pdf'
|
||||
}
|
||||
```
|
||||
|
||||
<Tabs items={['Browser', 'Node.js']}>
|
||||
<Tab value="Browser">
|
||||
```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);
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Node.js">
|
||||
```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'
|
||||
}
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
// Attach to button click
|
||||
document.getElementById('executeBtn')?.addEventListener('click', executeClientSideWorkflow);
|
||||
@@ -466,7 +696,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 +752,7 @@ function WorkflowComponent() {
|
||||
<button onClick={handleExecute} disabled={loading}>
|
||||
{loading ? 'Executing...' : 'Execute Workflow'}
|
||||
</button>
|
||||
|
||||
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
{result && (
|
||||
<div>
|
||||
@@ -535,6 +765,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 +1053,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
|
||||
@@ -594,4 +1069,4 @@ const status: WorkflowStatus = await client.getWorkflowStatus('workflow-id');
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
Apache-2.0
|
||||
|
||||
@@ -57,7 +57,7 @@ In Sim, the Airtable integration enables your agents to interact with your Airta
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrates Airtable into the workflow. Can create, get, list, or update Airtable records. Requires OAuth. Can be used in trigger mode to trigger a workflow when an update is made to an Airtable table.
|
||||
Integrates Airtable into the workflow. Can create, get, list, or update Airtable records. Can be used in trigger mode to trigger a workflow when an update is made to an Airtable table.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ In Sim, the BrowserUse integration allows your agents to interact with the web a
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Browser Use into the workflow. Can navigate the web and perform actions as if a real user was interacting with the browser. Requires API Key.
|
||||
Integrate Browser Use into the workflow. Can navigate the web and perform actions as if a real user was interacting with the browser.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ In Sim, the Clay integration allows your agents to push structured data into Cla
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Clay into the workflow. Can populate a table with data. Requires an API Key.
|
||||
Integrate Clay into the workflow. Can populate a table with data.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ In Sim, the Confluence integration enables your agents to access and leverage yo
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Confluence into the workflow. Can read and update a page. Requires OAuth.
|
||||
Integrate Confluence into the workflow. Can read and update a page.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Discord components in Sim use efficient lazy loading, only fetching data when ne
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Discord into the workflow. Can send and get messages, get server information, and get a user’s information. Requires bot API key.
|
||||
Integrate Discord into the workflow. Can send and get messages, get server information, and get a user’s information.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ In Sim, the ElevenLabs integration enables your agents to convert text to lifeli
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate ElevenLabs into the workflow. Can convert text to speech. Requires API key.
|
||||
Integrate ElevenLabs into the workflow. Can convert text to speech.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ In Sim, the Exa integration allows your agents to search the web for information
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Exa into the workflow. Can search, get contents, find similar links, answer a question, and perform research. Requires API Key.
|
||||
Integrate Exa into the workflow. Can search, get contents, find similar links, answer a question, and perform research.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ This allows your agents to gather information from websites, extract structured
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Firecrawl into the workflow. Can search, scrape, or crawl websites. Requires API Key.
|
||||
Integrate Firecrawl into the workflow. Can search, scrape, or crawl websites.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ In Sim, the GitHub integration enables your agents to interact directly with Git
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Github into the workflow. Can get get PR details, create PR comment, get repository info, and get latest commit. Requires github token API Key. Can be used in trigger mode to trigger a workflow when a PR is created, commented on, or a commit is pushed.
|
||||
Integrate Github into the workflow. Can get get PR details, create PR comment, get repository info, and get latest commit. Can be used in trigger mode to trigger a workflow when a PR is created, commented on, or a commit is pushed.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ In Sim, the Gmail integration enables your agents to send, read, and search emai
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Gmail into the workflow. Can send, read, and search emails. Requires OAuth. Can be used in trigger mode to trigger a workflow when a new email is received.
|
||||
Integrate Gmail into the workflow. Can send, read, and search emails. Can be used in trigger mode to trigger a workflow when a new email is received.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ In Sim, the Google Calendar integration enables your agents to programmatically
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Calendar into the workflow. Can create, read, update, and list calendar events. Requires OAuth.
|
||||
Integrate Google Calendar into the workflow. Can create, read, update, and list calendar events.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ In Sim, the Google Docs integration enables your agents to interact directly wit
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Docs into the workflow. Can read, write, and create documents. Requires OAuth.
|
||||
Integrate Google Docs into the workflow. Can read, write, and create documents.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ In Sim, the Google Drive integration enables your agents to interact directly wi
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Drive into the workflow. Can create, upload, and list files. Requires OAuth.
|
||||
Integrate Google Drive into the workflow. Can create, upload, and list files.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,9 +69,6 @@ Integrate Google Forms into your workflow. Provide a Form ID to list responses,
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| formId | string | Yes | The ID of the Google Form |
|
||||
| responseId | string | No | If provided, returns this specific response |
|
||||
| pageSize | number | No | Max responses to return (service may return fewer). Defaults to 5000 |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ In Sim, the Google Search integration enables your agents to search the web prog
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Search into the workflow. Can search the web. Requires API Key.
|
||||
Integrate Google Search into the workflow. Can search the web.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ In Sim, the Google Sheets integration enables your agents to interact directly w
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Sheets into the workflow. Can read, write, append, and update data. Requires OAuth.
|
||||
Integrate Google Sheets into the workflow. Can read, write, append, and update data.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ In Sim, the HuggingFace integration enables your agents to programmatically gene
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Hugging Face into the workflow. Can generate completions using the Hugging Face Inference API. Requires API Key.
|
||||
Integrate Hugging Face into the workflow. Can generate completions using the Hugging Face Inference API.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ In Sim, the Hunter.io integration enables your agents to programmatically search
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Hunter into the workflow. Can search domains, find email addresses, verify email addresses, discover companies, find companies, and count email addresses. Requires API Key.
|
||||
Integrate Hunter into the workflow. Can search domains, find email addresses, verify email addresses, discover companies, find companies, and count email addresses.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ In Sim, the DALL-E integration enables your agents to generate images programmat
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Image Generator into the workflow. Can generate images using DALL-E 3 or GPT Image. Requires API Key.
|
||||
Integrate Image Generator into the workflow. Can generate images using DALL-E 3 or GPT Image.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ This integration is particularly valuable for building agents that need to gathe
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Jina into the workflow. Extracts content from websites. Requires API Key.
|
||||
Integrate Jina into the workflow. Extracts content from websites.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ In Sim, the Jira integration allows your agents to seamlessly interact with your
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Jira into the workflow. Can read, write, and update issues. Requires OAuth.
|
||||
Integrate Jira into the workflow. Can read, write, and update issues.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
color="#5E6AD2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='currentColor'
|
||||
|
||||
|
||||
viewBox='0 0 100 100'
|
||||
>
|
||||
<path
|
||||
@@ -39,7 +42,7 @@ In Sim, the Linear integration allows your agents to seamlessly interact with yo
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Linear into the workflow. Can read and create issues. Requires OAuth.
|
||||
Integrate Linear into the workflow. Can read and create issues.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ To implement Linkup in your agent, simply add the tool to your agent's configura
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Linkup into the workflow. Can search the web. Requires API Key.
|
||||
Integrate Linkup into the workflow. Can search the web.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ In Sim, the Mem0 integration enables your agents to maintain persistent memory a
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Mem0 into the workflow. Can add, search, and retrieve memories. Requires API Key.
|
||||
Integrate Mem0 into the workflow. Can add, search, and retrieve memories.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"google_forms",
|
||||
"google_search",
|
||||
"google_sheets",
|
||||
"google_vault",
|
||||
"huggingface",
|
||||
"hunter",
|
||||
"image_generator",
|
||||
|
||||
@@ -94,7 +94,7 @@ In Sim, the Microsoft Excel integration provides seamless access to spreadsheet
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Microsoft Excel into the workflow. Can read, write, update, and add to table. Requires OAuth.
|
||||
Integrate Microsoft Excel into the workflow. Can read, write, update, and add to table.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ In Sim, the Microsoft Planner integration allows your agents to programmatically
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Microsoft Planner into the workflow. Can read and create tasks. Requires OAuth.
|
||||
Integrate Microsoft Planner into the workflow. Can read and create tasks.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ In Sim, the Microsoft Teams integration enables your agents to interact directly
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Microsoft Teams into the workflow. Can read and write chat messages, and read and write channel messages. Requires OAuth. Can be used in trigger mode to trigger a workflow when a message is sent to a chat or channel.
|
||||
Integrate Microsoft Teams into the workflow. Can read and write chat messages, and read and write channel messages. Can be used in trigger mode to trigger a workflow when a message is sent to a chat or channel.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ The Mistral Parse tool is particularly useful for scenarios where your agents ne
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Mistral Parse into the workflow. Can extract text from uploaded PDF documents, or from a URL. Requires API Key.
|
||||
Integrate Mistral Parse into the workflow. Can extract text from uploaded PDF documents, or from a URL.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ This integration bridges the gap between your AI workflows and your knowledge ba
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate with Notion into the workflow. Can read page, read database, create page, create database, append content, query database, and search workspace. Requires OAuth.
|
||||
Integrate with Notion into the workflow. Can read page, read database, create page, create database, append content, query database, and search workspace.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ In Sim, the OneDrive integration enables your agents to directly interact with y
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate OneDrive into the workflow. Can create, upload, and list files. Requires OAuth.
|
||||
Integrate OneDrive into the workflow. Can create, upload, and list files.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ In Sim, the OpenAI integration enables your agents to leverage these powerful AI
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Embeddings into the workflow. Can generate embeddings from text. Requires API Key.
|
||||
Integrate Embeddings into the workflow. Can generate embeddings from text.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ In Sim, the Microsoft Outlook integration enables your agents to interact direct
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Outlook into the workflow. Can read, draft, and send email messages. Requires OAuth. Can be used in trigger mode to trigger a workflow when a new email is received.
|
||||
Integrate Outlook into the workflow. Can read, draft, and send email messages. Can be used in trigger mode to trigger a workflow when a new email is received.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ In Sim, the Parallel AI integration empowers your agents to perform web searches
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Parallel AI into the workflow. Can search the web. Requires API Key.
|
||||
Integrate Parallel AI into the workflow. Can search the web.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ In Sim, the Perplexity integration enables your agents to leverage these powerfu
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Perplexity into the workflow. Can generate completions using Perplexity AI chat models. Requires API Key.
|
||||
Integrate Perplexity into the workflow. Can generate completions using Perplexity AI chat models.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ In Sim, the Pinecone integration enables your agents to leverage vector search c
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Pinecone into the workflow. Can generate embeddings, upsert text, search with text, fetch vectors, and search with vectors. Requires API Key.
|
||||
Integrate Pinecone into the workflow. Can generate embeddings, upsert text, search with text, fetch vectors, and search with vectors.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ This integration allows your agents to leverage powerful vector search and manag
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Qdrant into the workflow. Can upsert, search, and fetch points. Requires API Key.
|
||||
Integrate Qdrant into the workflow. Can upsert, search, and fetch points.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ These operations let your agents access and analyze Reddit content as part of yo
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Reddit into the workflow. Can get posts and comments from a subreddit. Requires OAuth.
|
||||
Integrate Reddit into the workflow. Can get posts and comments from a subreddit.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="schedule"
|
||||
color="#7B68EE"
|
||||
color="#6366F1"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ In Sim, the Serper integration enables your agents to leverage the power of web
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Serper into the workflow. Can search the web. Requires API Key.
|
||||
Integrate Serper into the workflow. Can search the web.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ This allows for powerful automation scenarios such as sending notifications, ale
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Slack into the workflow. Can send messages, create canvases, and read messages. Requires OAuth. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.
|
||||
Integrate Slack into the workflow. Can send messages, create canvases, and read messages. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ In Sim, the Stagehand integration enables your agents to extract structured data
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Stagehand into the workflow. Can extract structured data from webpages. Requires API Key.
|
||||
Integrate Stagehand into the workflow. Can extract structured data from webpages.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ In Sim, the Stagehand integration enables your agents to seamlessly interact wit
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Stagehand Agent into the workflow. Can navigate the web and perform tasks. Requires API Key.
|
||||
Integrate Stagehand Agent into the workflow. Can navigate the web and perform tasks.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ In Sim, the Vision integration enables your agents to analyze images with vision
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Vision into the workflow. Can analyze images with vision models. Requires API Key.
|
||||
Integrate Vision into the workflow. Can analyze images with vision models.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ In Sim, the Wealthbox integration enables your agents to seamlessly interact wit
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Wealthbox into the workflow. Can read and write notes, read and write contacts, and read and write tasks. Requires OAuth.
|
||||
Integrate Wealthbox into the workflow. Can read and write notes, read and write contacts, and read and write tasks.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ In Sim, the X integration enables sophisticated social media automation scenario
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate X into the workflow. Can post a new tweet, get tweet details, search tweets, and get user profile. Requires OAuth.
|
||||
Integrate X into the workflow. Can post a new tweet, get tweet details, search tweets, and get user profile.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ In Sim, the YouTube integration enables your agents to programmatically search a
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate YouTube into the workflow. Can search for videos. Requires API Key.
|
||||
Integrate YouTube into the workflow. Can search for videos.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,9 +22,17 @@ The API trigger exposes your workflow as a secure HTTP endpoint. Send JSON data
|
||||
/>
|
||||
</div>
|
||||
|
||||
Add an **Input Format** field for each parameter. Runtime output keys mirror the schema and are also available under `<api.input>`.
|
||||
Add an **Input Format** field for each parameter. Supported types:
|
||||
|
||||
Manual runs in the editor use the `value` column so you can test without sending a request. During execution the resolver populates both `<api.userId>` and `<api.input.userId>`.
|
||||
- **string** - Text values
|
||||
- **number** - Numeric values
|
||||
- **boolean** - True/false values
|
||||
- **json** - JSON objects
|
||||
- **files** - File uploads (access via `<api.fieldName[0].url>`, `<api.fieldName[0].name>`, etc.)
|
||||
|
||||
Runtime output keys mirror the schema and are available under `<api.input>`.
|
||||
|
||||
Manual runs in the editor use the `value` column so you can test without sending a request. During execution the resolver populates both `<api.fieldName>` and `<api.input.fieldName>`.
|
||||
|
||||
## Request Example
|
||||
|
||||
@@ -38,6 +46,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 |
|
||||
@@ -45,6 +131,53 @@ Successful responses return the serialized execution result from the Executor. E
|
||||
| `<api.field>` | Field defined in the Input Format |
|
||||
| `<api.input>` | Entire structured request body |
|
||||
|
||||
### File Upload Format
|
||||
|
||||
The API accepts files in two formats:
|
||||
|
||||
**1. Base64-encoded files** (recommended for SDKs):
|
||||
```json
|
||||
{
|
||||
"documents": [{
|
||||
"type": "file",
|
||||
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
|
||||
"name": "document.pdf",
|
||||
"mime": "application/pdf"
|
||||
}]
|
||||
}
|
||||
```
|
||||
- Maximum file size: 20MB per file
|
||||
- Files are uploaded to cloud storage and converted to UserFile objects with all properties
|
||||
|
||||
**2. Direct URL references**:
|
||||
```json
|
||||
{
|
||||
"documents": [{
|
||||
"type": "url",
|
||||
"data": "https://example.com/document.pdf",
|
||||
"name": "document.pdf",
|
||||
"mime": "application/pdf"
|
||||
}]
|
||||
}
|
||||
```
|
||||
- File is not uploaded, URL is passed through directly
|
||||
- Useful for referencing existing files
|
||||
|
||||
### File Properties
|
||||
|
||||
For files, access all properties:
|
||||
|
||||
| Property | Description | Type |
|
||||
|----------|-------------|------|
|
||||
| `<api.fieldName[0].url>` | Signed download URL | string |
|
||||
| `<api.fieldName[0].name>` | Original filename | string |
|
||||
| `<api.fieldName[0].size>` | File size in bytes | number |
|
||||
| `<api.fieldName[0].type>` | MIME type | string |
|
||||
| `<api.fieldName[0].uploadedAt>` | Upload timestamp (ISO 8601) | string |
|
||||
| `<api.fieldName[0].expiresAt>` | URL expiry timestamp (ISO 8601) | string |
|
||||
|
||||
For URL-referenced files, the same properties are available except `uploadedAt` and `expiresAt` since the file is not uploaded to our storage.
|
||||
|
||||
If no Input Format is defined, the executor exposes the raw JSON at `<api.input>` only.
|
||||
|
||||
<Callout type="warning">
|
||||
|
||||
@@ -24,13 +24,24 @@ The Chat trigger creates a conversational interface for your workflow. Deploy yo
|
||||
|
||||
The trigger writes three fields that downstream blocks can reference:
|
||||
|
||||
| Reference | Description |
|
||||
|-----------|-------------|
|
||||
| `<chat.input>` | Latest user message |
|
||||
| `<chat.conversationId>` | Conversation thread ID |
|
||||
| `<chat.files>` | Optional uploaded files |
|
||||
| Reference | Description | Type |
|
||||
|-----------|-------------|------|
|
||||
| `<chat.input>` | Latest user message | string |
|
||||
| `<chat.conversationId>` | Conversation thread ID | string |
|
||||
| `<chat.files>` | Optional uploaded files | files array |
|
||||
|
||||
Files include `name`, `mimeType`, and a signed download `url`.
|
||||
### File Properties
|
||||
|
||||
Access individual file properties using array indexing:
|
||||
|
||||
| Property | Description | Type |
|
||||
|----------|-------------|------|
|
||||
| `<chat.files[0].url>` | Signed download URL | string |
|
||||
| `<chat.files[0].name>` | Original filename | string |
|
||||
| `<chat.files[0].size>` | File size in bytes | number |
|
||||
| `<chat.files[0].type>` | MIME type | string |
|
||||
| `<chat.files[0].uploadedAt>` | Upload timestamp (ISO 8601) | string |
|
||||
| `<chat.files[0].expiresAt>` | URL expiry timestamp (ISO 8601) | string |
|
||||
|
||||
## Usage Notes
|
||||
|
||||
|
||||
@@ -175,33 +175,7 @@ Utiliza un bloque `Memory` con un `id` consistente (por ejemplo, `chat`) para pe
|
||||
- Lee el historial de conversación para contexto
|
||||
- Añade la respuesta del agente después de que se ejecute
|
||||
|
||||
```yaml
|
||||
# 1) Add latest user message
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: user
|
||||
content: {{input}}
|
||||
|
||||
# 2) Load conversation history
|
||||
- Memory (operation: get)
|
||||
id: chat
|
||||
|
||||
# 3) Run the agent with prior messages available
|
||||
- Agent
|
||||
System Prompt: ...
|
||||
User Prompt: |
|
||||
Use the conversation so far:
|
||||
{{memory_get.memories}}
|
||||
Current user message: {{input}}
|
||||
|
||||
# 4) Store the agent reply
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: assistant
|
||||
content: {{agent.content}}
|
||||
```
|
||||
|
||||
Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/tools/memory).
|
||||
Consulta la referencia del bloque [`Memory`](/tools/memory) para más detalles.
|
||||
|
||||
## Entradas y salidas
|
||||
|
||||
@@ -212,7 +186,7 @@ Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/
|
||||
<strong>Prompt del sistema</strong>: Instrucciones que definen el comportamiento y rol del agente
|
||||
</li>
|
||||
<li>
|
||||
<strong>Prompt del usuario</strong>: Texto de entrada o datos a procesar
|
||||
<strong>Prompt del usuario</strong>: Texto o datos de entrada para procesar
|
||||
</li>
|
||||
<li>
|
||||
<strong>Modelo</strong>: Selección del modelo de IA (OpenAI, Anthropic, Google, etc.)
|
||||
@@ -231,13 +205,13 @@ Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>agent.content</strong>: Texto de respuesta o datos estructurados del agente
|
||||
<strong>agent.content</strong>: Texto de respuesta del agente o datos estructurados
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.tokens</strong>: Objeto de estadísticas de uso de tokens
|
||||
<strong>agent.tokens</strong>: Objeto con estadísticas de uso de tokens
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.tool_calls</strong>: Array de detalles de ejecución de herramientas
|
||||
<strong>agent.tool_calls</strong>: Array con detalles de ejecución de herramientas
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.cost</strong>: Costo estimado de la llamada a la API (si está disponible)
|
||||
@@ -247,7 +221,7 @@ Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>Contenido</strong>: Salida de respuesta principal del agente
|
||||
<strong>Contenido</strong>: Salida principal de respuesta del agente
|
||||
</li>
|
||||
<li>
|
||||
<strong>Metadatos</strong>: Estadísticas de uso y detalles de ejecución
|
||||
@@ -267,15 +241,15 @@ Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/
|
||||
<h4 className="font-medium">Escenario: Gestionar consultas de clientes con acceso a base de datos</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>El usuario envía un ticket de soporte a través del bloque API</li>
|
||||
<li>El agente verifica pedidos/suscripciones en Postgres y busca en la base de conocimientos para obtener orientación</li>
|
||||
<li>El agente verifica pedidos/suscripciones en Postgres y busca en la base de conocimientos</li>
|
||||
<li>Si se necesita escalamiento, el agente crea una incidencia en Linear con el contexto relevante</li>
|
||||
<li>El agente redacta una respuesta clara por correo electrónico</li>
|
||||
<li>Gmail envía la respuesta al cliente</li>
|
||||
<li>La conversación se guarda en Memoria para mantener el historial para mensajes futuros</li>
|
||||
<li>La conversación se guarda en Memory para mantener el historial para mensajes futuros</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### Análisis de contenido multi-modelo
|
||||
### Análisis de contenido con múltiples modelos
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Escenario: Analizar contenido con diferentes modelos de IA</h4>
|
||||
@@ -287,13 +261,13 @@ Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### Asistente de investigación con herramientas
|
||||
### Asistente de investigación potenciado por herramientas
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Escenario: Asistente de investigación con búsqueda web y acceso a documentos</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>Consulta del usuario recibida a través de entrada</li>
|
||||
<li>El agente busca en la web usando la herramienta de Google Search</li>
|
||||
<li>El agente busca en la web utilizando la herramienta de Google Search</li>
|
||||
<li>El agente accede a la base de datos de Notion para documentos internos</li>
|
||||
<li>El agente compila un informe de investigación completo</li>
|
||||
</ol>
|
||||
@@ -301,6 +275,6 @@ Consulta la referencia del bloque `Memory` para más detalles: [/tools/memory](/
|
||||
|
||||
## Mejores prácticas
|
||||
|
||||
- **Sé específico en los prompts del sistema**: Define claramente el rol, tono y limitaciones del agente. Cuanto más específicas sean tus instrucciones, mejor podrá el agente cumplir con su propósito previsto.
|
||||
- **Sé específico en los prompts del sistema**: Define claramente el rol del agente, el tono y las limitaciones. Cuanto más específicas sean tus instrucciones, mejor podrá el agente cumplir con su propósito previsto.
|
||||
- **Elige la configuración de temperatura adecuada**: Usa configuraciones de temperatura más bajas (0-0.3) cuando la precisión es importante, o aumenta la temperatura (0.7-2.0) para respuestas más creativas o variadas
|
||||
- **Aprovecha las herramientas de manera efectiva**: Integra herramientas que complementen el propósito del agente y mejoren sus capacidades. Sé selectivo sobre qué herramientas proporcionas para evitar sobrecargar al agente. Para tareas con poca superposición, usa otro bloque de Agente para obtener los mejores resultados.
|
||||
- **Aprovecha las herramientas de manera efectiva**: Integra herramientas que complementen el propósito del agente y mejoren sus capacidades. Sé selectivo sobre qué herramientas proporcionas para evitar sobrecargar al agente. Para tareas con poco solapamiento, usa otro bloque de Agente para obtener los mejores resultados.
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
El SDK oficial de Python para Sim te permite ejecutar flujos de trabajo programáticamente desde tus aplicaciones Python utilizando el SDK oficial de Python.
|
||||
|
||||
<Callout type="info">
|
||||
El SDK de Python es compatible con Python 3.8+ y proporciona ejecución sincrónica de flujos de trabajo. Todas las ejecuciones de flujos de trabajo son actualmente sincrónicas.
|
||||
El SDK de Python es compatible con Python 3.8+ con soporte para ejecución asíncrona, limitación automática de velocidad con retroceso exponencial y seguimiento de uso.
|
||||
</Callout>
|
||||
|
||||
## Instalación
|
||||
@@ -74,12 +74,17 @@ result = client.execute_workflow(
|
||||
- `workflow_id` (str): El ID del flujo de trabajo a ejecutar
|
||||
- `input_data` (dict, opcional): Datos de entrada para pasar al flujo de trabajo
|
||||
- `timeout` (float, opcional): Tiempo de espera en segundos (predeterminado: 30.0)
|
||||
- `stream` (bool, opcional): Habilitar respuestas en streaming (predeterminado: False)
|
||||
- `selected_outputs` (list[str], opcional): Salidas de bloque para transmitir en formato `blockName.attribute` (p. ej., `["agent1.content"]`)
|
||||
- `async_execution` (bool, opcional): Ejecutar de forma asíncrona (predeterminado: False)
|
||||
|
||||
**Devuelve:** `WorkflowExecutionResult`
|
||||
**Devuelve:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
Cuando `async_execution=True`, devuelve inmediatamente un ID de tarea para sondeo. De lo contrario, espera a que se complete.
|
||||
|
||||
##### get_workflow_status()
|
||||
|
||||
Obtiene el estado de un flujo de trabajo (estado de implementación, etc.).
|
||||
Obtener el estado de un flujo de trabajo (estado de implementación, etc.).
|
||||
|
||||
```python
|
||||
status = client.get_workflow_status("workflow-id")
|
||||
@@ -93,7 +98,7 @@ print("Is deployed:", status.is_deployed)
|
||||
|
||||
##### validate_workflow()
|
||||
|
||||
Valida que un flujo de trabajo esté listo para su ejecución.
|
||||
Validar que un flujo de trabajo está listo para su ejecución.
|
||||
|
||||
```python
|
||||
is_ready = client.validate_workflow("workflow-id")
|
||||
@@ -107,28 +112,118 @@ if is_ready:
|
||||
|
||||
**Devuelve:** `bool`
|
||||
|
||||
##### execute_workflow_sync()
|
||||
##### get_job_status()
|
||||
|
||||
<Callout type="info">
|
||||
Actualmente, este método es idéntico a `execute_workflow()` ya que todas las ejecuciones son síncronas. Este método se proporciona para compatibilidad futura cuando se añada la ejecución asíncrona.
|
||||
</Callout>
|
||||
|
||||
Ejecuta un flujo de trabajo (actualmente síncrono, igual que `execute_workflow()`).
|
||||
Obtener el estado de una ejecución de trabajo asíncrono.
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**Parámetros:**
|
||||
- `task_id` (str): El ID de tarea devuelto de la ejecución asíncrona
|
||||
|
||||
**Devuelve:** `Dict[str, Any]`
|
||||
|
||||
**Campos de respuesta:**
|
||||
- `success` (bool): Si la solicitud fue exitosa
|
||||
- `taskId` (str): El ID de la tarea
|
||||
- `status` (str): Uno de `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (dict): Contiene `startedAt`, `completedAt`, y `duration`
|
||||
- `output` (any, opcional): La salida del flujo de trabajo (cuando se completa)
|
||||
- `error` (any, opcional): Detalles del error (cuando falla)
|
||||
- `estimatedDuration` (int, opcional): Duración estimada en milisegundos (cuando está procesando/en cola)
|
||||
|
||||
##### execute_with_retry()
|
||||
|
||||
Ejecutar un flujo de trabajo con reintento automático en errores de límite de velocidad usando retroceso exponencial.
|
||||
|
||||
```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
|
||||
)
|
||||
```
|
||||
|
||||
**Parámetros:**
|
||||
- `workflow_id` (str): El ID del flujo de trabajo a ejecutar
|
||||
- `input_data` (dict, opcional): Datos de entrada para pasar al flujo de trabajo
|
||||
- `timeout` (float): Tiempo de espera para la solicitud inicial en segundos
|
||||
- `timeout` (float, opcional): Tiempo de espera en segundos
|
||||
- `stream` (bool, opcional): Habilitar respuestas en streaming
|
||||
- `selected_outputs` (list, opcional): Salidas de bloque para transmitir
|
||||
- `async_execution` (bool, opcional): Ejecutar de forma asíncrona
|
||||
- `max_retries` (int, opcional): Número máximo de reintentos (predeterminado: 3)
|
||||
- `initial_delay` (float, opcional): Retraso inicial en segundos (predeterminado: 1.0)
|
||||
- `max_delay` (float, opcional): Retraso máximo en segundos (predeterminado: 30.0)
|
||||
- `backoff_multiplier` (float, opcional): Multiplicador de retroceso (predeterminado: 2.0)
|
||||
|
||||
**Devuelve:** `WorkflowExecutionResult`
|
||||
**Devuelve:** `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 un encabezado `retry-after`, se utilizará en su lugar.
|
||||
|
||||
##### get_rate_limit_info()
|
||||
|
||||
Obtiene la información actual del límite de tasa de la última respuesta de la API.
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
**Devuelve:** `RateLimitInfo | None`
|
||||
|
||||
##### get_usage_limits()
|
||||
|
||||
Obtiene los límites de uso actuales y la información de cuota para tu cuenta.
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**Devuelve:** `UsageLimits`
|
||||
|
||||
**Estructura de respuesta:**
|
||||
|
||||
```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 +265,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 +288,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 +319,13 @@ class SimStudioError(Exception):
|
||||
self.status = status
|
||||
```
|
||||
|
||||
**Códigos de error comunes:**
|
||||
- `UNAUTHORIZED`: Clave API inválida
|
||||
- `TIMEOUT`: Tiempo de espera agotado
|
||||
- `RATE_LIMIT_EXCEEDED`: Límite de tasa excedido
|
||||
- `USAGE_LIMIT_EXCEEDED`: Límite de uso excedido
|
||||
- `EXECUTION_ERROR`: Ejecución del flujo de trabajo fallida
|
||||
|
||||
## Ejemplos
|
||||
|
||||
### Ejecución básica de flujo de trabajo
|
||||
@@ -205,8 +340,8 @@ class SimStudioError(Exception):
|
||||
<Step title="Ejecutar el flujo de trabajo">
|
||||
Ejecuta el flujo de trabajo con tus datos de entrada.
|
||||
</Step>
|
||||
<Step title="Gestionar el resultado">
|
||||
Procesa el resultado de la ejecución y maneja cualquier error.
|
||||
<Step title="Manejar el resultado">
|
||||
Procesa el resultado de la ejecución y gestiona cualquier error.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -214,7 +349,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 +387,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:
|
||||
@@ -275,22 +410,22 @@ def execute_with_error_handling():
|
||||
raise
|
||||
```
|
||||
|
||||
### Uso del administrador de contexto
|
||||
### Uso del gestor de contexto
|
||||
|
||||
Usa el cliente como un administrador de contexto para manejar automáticamente la limpieza de recursos:
|
||||
Usa el cliente como un gestor de contexto para manejar automáticamente la limpieza de recursos:
|
||||
|
||||
```python
|
||||
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
|
||||
```
|
||||
|
||||
### Ejecución por lotes de flujos de trabajo
|
||||
### Ejecución de flujos de trabajo por lotes
|
||||
|
||||
Ejecuta múltiples flujos de trabajo de manera eficiente:
|
||||
|
||||
@@ -298,7 +433,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."""
|
||||
@@ -339,6 +474,230 @@ for result in results:
|
||||
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
|
||||
```
|
||||
|
||||
### Ejecución asíncrona de flujos de trabajo
|
||||
|
||||
Ejecuta flujos de trabajo de forma asíncrona para tareas de larga duración:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Límite de tasa y reintentos
|
||||
|
||||
Maneja los límites de tasa automáticamente con retroceso exponencial:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Monitoreo de uso
|
||||
|
||||
Monitorea el uso de tu cuenta y sus límites:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Ejecución de flujo de trabajo en streaming
|
||||
|
||||
Ejecuta flujos de trabajo con respuestas en tiempo real:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
La respuesta en streaming sigue el formato de Server-Sent Events (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 con Flask:**
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
### Configuración del entorno
|
||||
|
||||
Configura el cliente usando variables de entorno:
|
||||
@@ -352,8 +711,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 +724,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")
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
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 de forma programática desde tus aplicaciones Node.js, aplicaciones web y otros entornos JavaScript. Todas las ejecuciones de flujos de trabajo son actualmente síncronas.
|
||||
El SDK oficial de TypeScript/JavaScript para Sim proporciona seguridad de tipos completa y es compatible tanto con entornos Node.js como de navegador, lo que te permite ejecutar flujos de trabajo programáticamente desde tus aplicaciones Node.js, aplicaciones web y otros entornos JavaScript.
|
||||
|
||||
<Callout type="info">
|
||||
El SDK de TypeScript proporciona seguridad de tipos completa y es compatible tanto con entornos Node.js como con navegadores. Todas las ejecuciones de flujos de trabajo son actualmente síncronas.
|
||||
El SDK de TypeScript proporciona seguridad de tipos completa, soporte para ejecución asíncrona, limitación automática de velocidad con retroceso exponencial y seguimiento de uso.
|
||||
</Callout>
|
||||
|
||||
## Instalación
|
||||
@@ -95,8 +95,13 @@ const result = await client.executeWorkflow('workflow-id', {
|
||||
- `options` (ExecutionOptions, opcional):
|
||||
- `input` (any): Datos de entrada para pasar al flujo de trabajo
|
||||
- `timeout` (number): Tiempo de espera en milisegundos (predeterminado: 30000)
|
||||
- `stream` (boolean): Habilitar respuestas en streaming (predeterminado: false)
|
||||
- `selectedOutputs` (string[]): Bloquear salidas para transmitir en formato `blockName.attribute` (por ejemplo, `["agent1.content"]`)
|
||||
- `async` (boolean): Ejecutar de forma asíncrona (predeterminado: false)
|
||||
|
||||
**Devuelve:** `Promise<WorkflowExecutionResult>`
|
||||
**Devuelve:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
Cuando `async: true`, devuelve inmediatamente un ID de tarea para sondeo. De lo contrario, espera a que se complete.
|
||||
|
||||
##### getWorkflowStatus()
|
||||
|
||||
@@ -128,32 +133,121 @@ if (isReady) {
|
||||
|
||||
**Devuelve:** `Promise<boolean>`
|
||||
|
||||
##### executeWorkflowSync()
|
||||
##### getJobStatus()
|
||||
|
||||
<Callout type="info">
|
||||
Actualmente, este método es idéntico a `executeWorkflow()` ya que todas las ejecuciones son síncronas. Este método se proporciona para compatibilidad futura cuando se añada la ejecución asíncrona.
|
||||
</Callout>
|
||||
|
||||
Ejecutar un flujo de trabajo (actualmente síncrono, igual que `executeWorkflow()`).
|
||||
Obtener el estado de una ejecución de trabajo asíncrono.
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
**Parámetros:**
|
||||
- `taskId` (string): El ID de tarea devuelto de la ejecución asíncrona
|
||||
|
||||
**Devuelve:** `Promise<JobStatus>`
|
||||
|
||||
**Campos de respuesta:**
|
||||
- `success` (boolean): Si la solicitud fue exitosa
|
||||
- `taskId` (string): El ID de la tarea
|
||||
- `status` (string): Uno de `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (object): Contiene `startedAt`, `completedAt`, y `duration`
|
||||
- `output` (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)
|
||||
|
||||
##### executeWithRetry()
|
||||
|
||||
Ejecuta un flujo de trabajo con reintento automático en errores de límite de tasa utilizando retroceso exponencial.
|
||||
|
||||
```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
|
||||
});
|
||||
```
|
||||
|
||||
**Parámetros:**
|
||||
- `workflowId` (string): El ID del flujo de trabajo a ejecutar
|
||||
- `options` (ExecutionOptions, opcional):
|
||||
- `input` (any): Datos de entrada para pasar al flujo de trabajo
|
||||
- `timeout` (number): Tiempo de espera para la solicitud inicial en milisegundos
|
||||
- `options` (ExecutionOptions, opcional): Igual que `executeWorkflow()`
|
||||
- `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>`
|
||||
**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.
|
||||
|
||||
##### getRateLimitInfo()
|
||||
|
||||
Obtiene la información actual del límite de tasa de la última respuesta de la API.
|
||||
|
||||
```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));
|
||||
}
|
||||
```
|
||||
|
||||
**Devuelve:** `RateLimitInfo | null`
|
||||
|
||||
##### getUsageLimits()
|
||||
|
||||
Obtiene los límites de uso actuales y la información de cuota para tu cuenta.
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
**Devuelve:** `Promise<UsageLimits>`
|
||||
|
||||
**Estructura de respuesta:**
|
||||
|
||||
```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()
|
||||
|
||||
Actualizar la clave API.
|
||||
Actualiza la clave API.
|
||||
|
||||
```typescript
|
||||
client.setApiKey('new-api-key');
|
||||
@@ -161,7 +255,7 @@ client.setApiKey('new-api-key');
|
||||
|
||||
##### setBaseUrl()
|
||||
|
||||
Actualizar la URL base.
|
||||
Actualiza la URL base.
|
||||
|
||||
```typescript
|
||||
client.setBaseUrl('https://my-custom-domain.com');
|
||||
@@ -187,6 +281,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
|
||||
@@ -198,6 +306,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
|
||||
@@ -207,6 +354,13 @@ class SimStudioError extends Error {
|
||||
}
|
||||
```
|
||||
|
||||
**Códigos de error comunes:**
|
||||
- `UNAUTHORIZED`: Clave API inválida
|
||||
- `TIMEOUT`: Tiempo de espera agotado
|
||||
- `RATE_LIMIT_EXCEEDED`: Límite de tasa excedido
|
||||
- `USAGE_LIMIT_EXCEEDED`: Límite de uso excedido
|
||||
- `EXECUTION_ERROR`: Ejecución del flujo de trabajo fallida
|
||||
|
||||
## Ejemplos
|
||||
|
||||
### Ejecución básica de flujo de trabajo
|
||||
@@ -216,13 +370,13 @@ class SimStudioError extends Error {
|
||||
Configura el SimStudioClient con tu clave API.
|
||||
</Step>
|
||||
<Step title="Validar el flujo de trabajo">
|
||||
Comprueba si el flujo de trabajo está implementado y listo para su ejecución.
|
||||
Comprueba si el flujo de trabajo está desplegado y listo para su ejecución.
|
||||
</Step>
|
||||
<Step title="Ejecutar el flujo de trabajo">
|
||||
Ejecuta el flujo de trabajo con tus datos de entrada.
|
||||
</Step>
|
||||
<Step title="Gestionar el resultado">
|
||||
Procesa el resultado de la ejecución y maneja cualquier error.
|
||||
<Step title="Manejar el resultado">
|
||||
Procesa el resultado de la ejecución y gestiona cualquier error.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -230,7 +384,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 +425,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 +469,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 +487,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'
|
||||
});
|
||||
```
|
||||
|
||||
@@ -349,7 +503,7 @@ Configura el cliente usando variables de entorno:
|
||||
|
||||
### Integración con Express de Node.js
|
||||
|
||||
Integración con un servidor Express.js:
|
||||
Integra con un servidor Express.js:
|
||||
|
||||
```typescript
|
||||
import express from 'express';
|
||||
@@ -357,7 +511,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 +553,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(
|
||||
@@ -430,7 +584,7 @@ export default async function handler(
|
||||
|
||||
### Uso del navegador
|
||||
|
||||
Uso en el navegador (con la configuración CORS adecuada):
|
||||
Uso en el navegador (con configuración CORS adecuada):
|
||||
|
||||
```typescript
|
||||
import { SimStudioClient } from 'simstudio-ts-sdk';
|
||||
@@ -469,14 +623,14 @@ document.getElementById('executeBtn')?.addEventListener('click', executeClientSi
|
||||
|
||||
### Ejemplo de hook de React
|
||||
|
||||
Crea un hook personalizado de React para la ejecución del flujo de trabajo:
|
||||
Crea un hook personalizado de React para la ejecución de flujos de trabajo:
|
||||
|
||||
```typescript
|
||||
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 {
|
||||
@@ -532,7 +686,7 @@ function WorkflowComponent() {
|
||||
<button onClick={handleExecute} disabled={loading}>
|
||||
{loading ? 'Executing...' : 'Execute Workflow'}
|
||||
</button>
|
||||
|
||||
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
{result && (
|
||||
<div>
|
||||
@@ -545,38 +699,267 @@ function WorkflowComponent() {
|
||||
}
|
||||
```
|
||||
|
||||
## Obtener tu clave API
|
||||
### Ejecución asíncrona de flujos de trabajo
|
||||
|
||||
Ejecuta flujos de trabajo de forma asíncrona para tareas de larga duración:
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
### Límite de tasa y reintentos
|
||||
|
||||
Maneja límites de tasa automáticamente con retroceso exponencial:
|
||||
|
||||
```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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Monitoreo de uso
|
||||
|
||||
Monitorea el uso de tu cuenta y sus límites:
|
||||
|
||||
```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:
|
||||
|
||||
### 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 {
|
||||
// Habilita streaming para salidas de bloques específicos
|
||||
const result = await client.executeWorkflow('workflow-id', {
|
||||
input: { message: 'Count to five' },
|
||||
stream: true,
|
||||
selectedOutputs: ['agent1.content'] // Usa el formato blockName.attribute
|
||||
});
|
||||
|
||||
console.log('Resultado del flujo de trabajo:', 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":", dos"}
|
||||
|
||||
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 ? 'Generando...' : 'Iniciar streaming'}
|
||||
</button>
|
||||
<div style={{ whiteSpace: 'pre-wrap' }}>{output}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Your API Key
|
||||
|
||||
<Steps>
|
||||
<Step title="Inicia sesión en Sim">
|
||||
Navega a [Sim](https://sim.ai) e inicia sesión en tu cuenta.
|
||||
<Step title="Log in to Sim">
|
||||
Navigate to [Sim](https://sim.ai) and log in to your account.
|
||||
</Step>
|
||||
<Step title="Abre tu flujo de trabajo">
|
||||
Navega al flujo de trabajo que quieres ejecutar programáticamente.
|
||||
<Step title="Open your workflow">
|
||||
Navigate to the workflow you want to execute programmatically.
|
||||
</Step>
|
||||
<Step title="Despliega tu flujo de trabajo">
|
||||
Haz clic en "Deploy" para desplegar tu flujo de trabajo si aún no ha sido desplegado.
|
||||
<Step title="Deploy your workflow">
|
||||
Click on "Deploy" to deploy your workflow if it hasn't been deployed yet.
|
||||
</Step>
|
||||
<Step title="Crea o selecciona una clave API">
|
||||
Durante el proceso de despliegue, selecciona o crea una clave API.
|
||||
<Step title="Create or select an API key">
|
||||
During the deployment process, select or create an API key.
|
||||
</Step>
|
||||
<Step title="Copia la clave API">
|
||||
Copia la clave API para usarla en tu aplicación TypeScript/JavaScript.
|
||||
<Step title="Copy the API key">
|
||||
Copy the API key to use in your TypeScript/JavaScript application.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning">
|
||||
Mantén tu clave API segura y nunca la incluyas en el control de versiones. Usa variables de entorno o gestión de configuración segura.
|
||||
Keep your API key secure and never commit it to version control. Use environment variables or secure configuration management.
|
||||
</Callout>
|
||||
|
||||
## Requisitos
|
||||
## Requirements
|
||||
|
||||
- Node.js 16+
|
||||
- TypeScript 5.0+ (para proyectos TypeScript)
|
||||
- TypeScript 5.0+ (for TypeScript projects)
|
||||
|
||||
## Soporte para TypeScript
|
||||
## TypeScript Support
|
||||
|
||||
El SDK está escrito en TypeScript y proporciona seguridad de tipos completa:
|
||||
The SDK is written in TypeScript and provides full type safety:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -588,13 +971,13 @@ 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
|
||||
const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-id', {
|
||||
input: {
|
||||
message: 'Hello, TypeScript!'
|
||||
message: '¡Hola, TypeScript!'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -602,6 +985,7 @@ const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-i
|
||||
const status: WorkflowStatus = await client.getWorkflowStatus('workflow-id');
|
||||
```
|
||||
|
||||
## Licencia
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
|
||||
Apache-2.0
|
||||
|
||||
@@ -24,14 +24,6 @@ El disparador de API expone tu flujo de trabajo como un punto de conexión HTTP
|
||||
|
||||
Añade un campo de **Formato de entrada** para cada parámetro. Las claves de salida en tiempo de ejecución reflejan el esquema y también están disponibles bajo `<api.input>`.
|
||||
|
||||
```yaml
|
||||
- type: string
|
||||
name: userId
|
||||
value: demo-user # optional manual test value
|
||||
- type: number
|
||||
name: maxTokens
|
||||
```
|
||||
|
||||
Las ejecuciones manuales en el editor utilizan la columna `value` para que puedas realizar pruebas sin enviar una solicitud. Durante la ejecución, el resolutor completa tanto `<api.userId>` como `<api.input.userId>`.
|
||||
|
||||
## Ejemplo de solicitud
|
||||
@@ -44,17 +36,95 @@ curl -X POST \
|
||||
-d '{"userId":"demo-user","maxTokens":1024}'
|
||||
```
|
||||
|
||||
Las respuestas exitosas devuelven el resultado de ejecución serializado del Ejecutor. Los errores muestran fallos de validación, autenticación o del flujo de trabajo.
|
||||
Las respuestas exitosas devuelven el resultado de ejecución serializado del Ejecutor. Los errores muestran fallos de validación, autenticación o flujo de trabajo.
|
||||
|
||||
## Respuestas en streaming
|
||||
|
||||
Habilita el streaming en tiempo real para recibir la salida del flujo de trabajo a medida que se genera, carácter por carácter. Esto es útil para mostrar las respuestas de IA progresivamente a los usuarios.
|
||||
|
||||
### Parámetros de solicitud
|
||||
|
||||
Añade estos parámetros para habilitar el streaming:
|
||||
|
||||
- `stream` - Establece a `true` para habilitar el streaming de eventos enviados por el servidor (SSE)
|
||||
- `selectedOutputs` - Array de salidas de bloques para transmitir (p. ej., `["agent1.content"]`)
|
||||
|
||||
### Formato de salida de bloque
|
||||
|
||||
Usa el formato `blockName.attribute` para especificar qué salidas de bloques transmitir:
|
||||
- Formato: `"blockName.attribute"` (p. ej., si quieres transmitir el contenido del bloque Agente 1, usarías `"agent1.content"`)
|
||||
- Los nombres de los bloques no distinguen entre mayúsculas y minúsculas y se ignoran los espacios
|
||||
|
||||
### Ejemplo de solicitud
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Formato de respuesta
|
||||
|
||||
Las respuestas en streaming utilizan 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: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":", three"}
|
||||
|
||||
data: {"event":"done","success":true,"output":{},"metadata":{"duration":610}}
|
||||
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
Cada evento incluye:
|
||||
- **Fragmentos de streaming**: `{"blockId": "...", "chunk": "text"}` - Texto en tiempo real a medida que se genera
|
||||
- **Evento final**: `{"event": "done", ...}` - Metadatos de ejecución y resultados completos
|
||||
- **Terminador**: `[DONE]` - Señala el fin del stream
|
||||
|
||||
### Streaming de múltiples bloques
|
||||
|
||||
Cuando `selectedOutputs` incluye múltiples bloques, cada fragmento indica qué bloque lo produjo:
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
El campo `blockId` en cada fragmento te permite dirigir la salida al elemento de UI correcto:
|
||||
|
||||
```
|
||||
data: {"blockId":"agent1-uuid","chunk":"Processing..."}
|
||||
|
||||
data: {"blockId":"agent2-uuid","chunk":"Analyzing..."}
|
||||
|
||||
data: {"blockId":"agent1-uuid","chunk":" complete"}
|
||||
```
|
||||
|
||||
## Referencia de salida
|
||||
|
||||
| Referencia | Descripción |
|
||||
|-----------|-------------|
|
||||
| `<api.field>` | Campo definido en el formato de entrada |
|
||||
| `<api.input>` | Cuerpo completo estructurado de la solicitud |
|
||||
| `<api.input>` | Cuerpo de solicitud estructurado completo |
|
||||
|
||||
Si no se define un formato de entrada, el ejecutor expone el JSON sin procesar solo en `<api.input>`.
|
||||
|
||||
<Callout type="warning">
|
||||
Un flujo de trabajo puede contener solo un disparador de API. Publica una nueva implementación después de realizar cambios para que el punto de conexión se mantenga actualizado.
|
||||
Un flujo de trabajo puede contener solo un disparador de API. Publica una nueva implementación después de los cambios para que el endpoint se mantenga actualizado.
|
||||
</Callout>
|
||||
|
||||
@@ -10,7 +10,6 @@ type: object
|
||||
required:
|
||||
- type
|
||||
- name
|
||||
- inputs
|
||||
- connections
|
||||
properties:
|
||||
type:
|
||||
@@ -22,21 +21,23 @@ properties:
|
||||
description: Display name for this loop block
|
||||
inputs:
|
||||
type: object
|
||||
required:
|
||||
- loopType
|
||||
description: Optional. If omitted, defaults will be applied.
|
||||
properties:
|
||||
loopType:
|
||||
type: string
|
||||
enum: [for, forEach]
|
||||
description: Type of loop to execute
|
||||
default: for
|
||||
iterations:
|
||||
type: number
|
||||
description: Number of iterations (for 'for' loops)
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
collection:
|
||||
type: string
|
||||
description: Collection to iterate over (for 'forEach' loops)
|
||||
default: ""
|
||||
maxConcurrency:
|
||||
type: number
|
||||
description: Maximum concurrent executions
|
||||
@@ -45,13 +46,10 @@ properties:
|
||||
maximum: 10
|
||||
connections:
|
||||
type: object
|
||||
required:
|
||||
- loop
|
||||
properties:
|
||||
# Nested format (recommended)
|
||||
loop:
|
||||
type: object
|
||||
required:
|
||||
- start
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
@@ -59,26 +57,37 @@ properties:
|
||||
end:
|
||||
type: string
|
||||
description: Target block ID for loop completion (optional)
|
||||
# Direct handle format (alternative)
|
||||
loop-start-source:
|
||||
type: string | string[]
|
||||
description: Target block ID to execute inside the loop (direct format)
|
||||
loop-end-source:
|
||||
type: string | string[]
|
||||
description: Target block ID for loop completion (direct format, optional)
|
||||
error:
|
||||
type: string
|
||||
description: Target block ID for error handling
|
||||
note: Use either the nested 'loop' format OR the direct 'loop-start-source' format, not both
|
||||
```
|
||||
|
||||
## Configuración de conexión
|
||||
|
||||
Los bloques Loop utilizan un formato de conexión especial con una sección `loop`:
|
||||
Los bloques de bucle admiten dos formatos de conexión:
|
||||
|
||||
### Formato de manejador directo (alternativo)
|
||||
|
||||
```yaml
|
||||
connections:
|
||||
loop:
|
||||
start: <string> # Target block ID to execute inside the loop
|
||||
end: <string> # Target block ID after loop completion (optional)
|
||||
loop-start-source: <string> # Target block ID to execute inside the loop
|
||||
loop-end-source: <string> # Target block ID after loop completion (optional)
|
||||
error: <string> # Target block ID for error handling (optional)
|
||||
```
|
||||
|
||||
Ambos formatos funcionan de manera idéntica. Usa el que prefieras.
|
||||
|
||||
## Configuración de bloques secundarios
|
||||
|
||||
Los bloques dentro de un bucle deben tener su `parentId` configurado con el ID del bloque loop:
|
||||
Los bloques dentro de un bucle deben tener su `parentId` configurado con el ID del bloque de bucle. La propiedad `extent` se establece automáticamente como `'parent'` y no necesita ser especificada:
|
||||
|
||||
```yaml
|
||||
loop-1:
|
||||
@@ -261,6 +270,59 @@ process-task:
|
||||
success: task-completed
|
||||
```
|
||||
|
||||
### Ejemplo de formato de manejador directo
|
||||
|
||||
El mismo bucle puede escribirse usando el formato de manejador directo:
|
||||
|
||||
```yaml
|
||||
my-loop:
|
||||
type: loop
|
||||
name: "Process Items"
|
||||
inputs:
|
||||
loopType: forEach
|
||||
collection: <start.items>
|
||||
connections:
|
||||
loop-start-source: process-item # Direct handle format
|
||||
loop-end-source: final-results # Direct handle format
|
||||
error: handle-error
|
||||
|
||||
process-item:
|
||||
type: agent
|
||||
name: "Process Item"
|
||||
parentId: my-loop
|
||||
inputs:
|
||||
systemPrompt: "Process this item"
|
||||
userPrompt: <loop.currentItem>
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
### Ejemplo de bucle mínimo (usando valores predeterminados)
|
||||
|
||||
Puedes omitir completamente la sección `inputs`, y se aplicarán los valores predeterminados:
|
||||
|
||||
```yaml
|
||||
simple-loop:
|
||||
type: loop
|
||||
name: "Simple Loop"
|
||||
# No inputs section - defaults to loopType: 'for', iterations: 5
|
||||
connections:
|
||||
loop-start-source: process-step
|
||||
loop-end-source: complete
|
||||
|
||||
process-step:
|
||||
type: agent
|
||||
name: "Process Step"
|
||||
parentId: simple-loop
|
||||
inputs:
|
||||
systemPrompt: "Execute step"
|
||||
userPrompt: "Step <loop.index>"
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
Este bucle ejecutará 5 iteraciones por defecto.
|
||||
|
||||
## Variables de bucle
|
||||
|
||||
Dentro de los bloques secundarios del bucle, estas variables especiales están disponibles:
|
||||
@@ -286,10 +348,10 @@ final-processor:
|
||||
|
||||
## Mejores prácticas
|
||||
|
||||
- Establece límites razonables de iteración para evitar tiempos de ejecución prolongados
|
||||
- Establece límites de iteración razonables para evitar tiempos de ejecución largos
|
||||
- Usa forEach para procesar colecciones, bucles for para iteraciones fijas
|
||||
- Considera usar maxConcurrency para operaciones limitadas por E/S
|
||||
- Incluye manejo de errores para una ejecución robusta de bucles
|
||||
- Incluye manejo de errores para una ejecución robusta del bucle
|
||||
- Usa nombres descriptivos para los bloques secundarios del bucle
|
||||
- Prueba primero con colecciones pequeñas
|
||||
- Monitorea el tiempo de ejecución para colecciones grandes
|
||||
@@ -175,33 +175,7 @@ Utilisez un bloc `Memory` avec un `id` cohérent (par exemple, `chat`) pour cons
|
||||
- Lisez l'historique de conversation pour le contexte
|
||||
- Ajoutez la réponse de l'Agent après son exécution
|
||||
|
||||
```yaml
|
||||
# 1) Add latest user message
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: user
|
||||
content: {{input}}
|
||||
|
||||
# 2) Load conversation history
|
||||
- Memory (operation: get)
|
||||
id: chat
|
||||
|
||||
# 3) Run the agent with prior messages available
|
||||
- Agent
|
||||
System Prompt: ...
|
||||
User Prompt: |
|
||||
Use the conversation so far:
|
||||
{{memory_get.memories}}
|
||||
Current user message: {{input}}
|
||||
|
||||
# 4) Store the agent reply
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: assistant
|
||||
content: {{agent.content}}
|
||||
```
|
||||
|
||||
Consultez la référence du bloc `Memory` pour plus de détails : [/tools/memory](/tools/memory).
|
||||
Voir la référence du bloc [`Memory`](/tools/memory) pour plus de détails.
|
||||
|
||||
## Entrées et sorties
|
||||
|
||||
@@ -209,51 +183,51 @@ Consultez la référence du bloc `Memory` pour plus de détails : [/tools/memory
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>Prompt système</strong> : Instructions définissant le comportement et le rôle de l'agent
|
||||
<strong>Prompt système</strong> : instructions définissant le comportement et le rôle de l'agent
|
||||
</li>
|
||||
<li>
|
||||
<strong>Prompt utilisateur</strong> : Texte d'entrée ou données à traiter
|
||||
<strong>Prompt utilisateur</strong> : texte ou données d'entrée à traiter
|
||||
</li>
|
||||
<li>
|
||||
<strong>Modèle</strong> : Sélection du modèle d'IA (OpenAI, Anthropic, Google, etc.)
|
||||
<strong>Modèle</strong> : sélection du modèle d'IA (OpenAI, Anthropic, Google, etc.)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Température</strong> : Contrôle de l'aléatoire des réponses (0-2)
|
||||
<strong>Température</strong> : contrôle de l'aléatoire des réponses (0-2)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Outils</strong> : Tableau d'outils disponibles pour l'appel de fonctions
|
||||
<strong>Outils</strong> : tableau des outils disponibles pour l'appel de fonctions
|
||||
</li>
|
||||
<li>
|
||||
<strong>Format de réponse</strong> : Schéma JSON pour une sortie structurée
|
||||
<strong>Format de réponse</strong> : schéma JSON pour une sortie structurée
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>agent.content</strong> : Texte de réponse de l'agent ou données structurées
|
||||
<strong>agent.content</strong> : texte de réponse de l'agent ou données structurées
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.tokens</strong> : Objet de statistiques d'utilisation des tokens
|
||||
<strong>agent.tokens</strong> : objet de statistiques d'utilisation des tokens
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.tool_calls</strong> : Tableau des détails d'exécution des outils
|
||||
<strong>agent.tool_calls</strong> : tableau des détails d'exécution des outils
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.cost</strong> : Coût estimé de l'appel API (si disponible)
|
||||
<strong>agent.cost</strong> : coût estimé de l'appel API (si disponible)
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>Contenu</strong> : Sortie de réponse principale de l'agent
|
||||
<strong>Contenu</strong> : sortie de réponse principale de l'agent
|
||||
</li>
|
||||
<li>
|
||||
<strong>Métadonnées</strong> : Statistiques d'utilisation et détails d'exécution
|
||||
<strong>Métadonnées</strong> : statistiques d'utilisation et détails d'exécution
|
||||
</li>
|
||||
<li>
|
||||
<strong>Accès</strong> : Disponible dans les blocs après l'agent
|
||||
<strong>Accès</strong> : disponible dans les blocs après l'agent
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
@@ -268,7 +242,7 @@ Consultez la référence du bloc `Memory` pour plus de détails : [/tools/memory
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>L'utilisateur soumet un ticket de support via le bloc API</li>
|
||||
<li>L'agent vérifie les commandes/abonnements dans Postgres et recherche des conseils dans la base de connaissances</li>
|
||||
<li>Si une escalade est nécessaire, l'agent crée un ticket Linear avec le contexte pertinent</li>
|
||||
<li>Si une escalade est nécessaire, l'agent crée un problème Linear avec le contexte pertinent</li>
|
||||
<li>L'agent rédige une réponse par e-mail claire</li>
|
||||
<li>Gmail envoie la réponse au client</li>
|
||||
<li>La conversation est enregistrée dans Memory pour conserver l'historique des messages futurs</li>
|
||||
@@ -278,7 +252,7 @@ Consultez la référence du bloc `Memory` pour plus de détails : [/tools/memory
|
||||
### Analyse de contenu multi-modèles
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">Scénario : analyser du contenu avec différents modèles d'IA</h4>
|
||||
<h4 className="font-medium">Scénario : analyser le contenu avec différents modèles d'IA</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>Le bloc de fonction traite le document téléchargé</li>
|
||||
<li>L'agent avec GPT-4o effectue une analyse technique</li>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
Le SDK Python officiel pour Sim vous permet d'exécuter des workflows de manière programmatique à partir de vos applications Python en utilisant le SDK Python officiel.
|
||||
|
||||
<Callout type="info">
|
||||
Le SDK Python prend en charge Python 3.8+ et fournit une exécution synchrone des workflows. Toutes les exécutions de workflow sont actuellement synchrones.
|
||||
Le SDK Python prend en charge Python 3.8+ avec support d'exécution asynchrone, limitation automatique du débit avec backoff exponentiel, et suivi d'utilisation.
|
||||
</Callout>
|
||||
|
||||
## Installation
|
||||
@@ -71,11 +71,16 @@ result = client.execute_workflow(
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflow_id` (str) : L'ID du workflow à exécuter
|
||||
- `workflow_id` (str) : L'identifiant du workflow à exécuter
|
||||
- `input_data` (dict, facultatif) : Données d'entrée à transmettre au workflow
|
||||
- `timeout` (float, facultatif) : Délai d'attente en secondes (par défaut : 30.0)
|
||||
- `timeout` (float, facultatif) : Délai d'expiration en secondes (par défaut : 30.0)
|
||||
- `stream` (bool, facultatif) : Activer les réponses en streaming (par défaut : False)
|
||||
- `selected_outputs` (list[str], facultatif) : Sorties de blocs à diffuser au format `blockName.attribute` (par exemple, `["agent1.content"]`)
|
||||
- `async_execution` (bool, facultatif) : Exécuter de manière asynchrone (par défaut : False)
|
||||
|
||||
**Retourne :** `WorkflowExecutionResult`
|
||||
**Retourne :** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
Lorsque `async_execution=True`, retourne immédiatement un identifiant de tâche pour l'interrogation. Sinon, attend la fin de l'exécution.
|
||||
|
||||
##### get_workflow_status()
|
||||
|
||||
@@ -87,13 +92,13 @@ print("Is deployed:", status.is_deployed)
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflow_id` (str) : L'ID du workflow
|
||||
- `workflow_id` (str) : L'identifiant du workflow
|
||||
|
||||
**Retourne :** `WorkflowStatus`
|
||||
|
||||
##### validate_workflow()
|
||||
|
||||
Valide qu'un workflow est prêt pour l'exécution.
|
||||
Valider qu'un workflow est prêt pour l'exécution.
|
||||
|
||||
```python
|
||||
is_ready = client.validate_workflow("workflow-id")
|
||||
@@ -107,32 +112,122 @@ if is_ready:
|
||||
|
||||
**Retourne :** `bool`
|
||||
|
||||
##### execute_workflow_sync()
|
||||
##### get_job_status()
|
||||
|
||||
<Callout type="info">
|
||||
Actuellement, cette méthode est identique à `execute_workflow()` puisque toutes les exécutions sont synchrones. Cette méthode est fournie pour une compatibilité future lorsque l'exécution asynchrone sera ajoutée.
|
||||
</Callout>
|
||||
|
||||
Exécute un workflow (actuellement synchrone, identique à `execute_workflow()`).
|
||||
Obtenir le statut d'une exécution de tâche asynchrone.
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `task_id` (str) : L'identifiant de tâche retourné par l'exécution asynchrone
|
||||
|
||||
**Retourne :** `Dict[str, Any]`
|
||||
|
||||
**Champs de réponse :**
|
||||
- `success` (bool) : Si la requête a réussi
|
||||
- `taskId` (str) : L'identifiant de la tâche
|
||||
- `status` (str) : L'un des états suivants : `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (dict) : Contient `startedAt`, `completedAt`, et `duration`
|
||||
- `output` (any, facultatif) : La sortie du workflow (une fois terminé)
|
||||
- `error` (any, facultatif) : Détails de l'erreur (en cas d'échec)
|
||||
- `estimatedDuration` (int, facultatif) : Durée estimée en millisecondes (lors du traitement/mise en file d'attente)
|
||||
|
||||
##### execute_with_retry()
|
||||
|
||||
Exécuter un workflow avec réessai automatique en cas d'erreurs de limitation de débit, en utilisant un backoff exponentiel.
|
||||
|
||||
```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
|
||||
)
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflow_id` (str) : L'identifiant du workflow à exécuter
|
||||
- `input_data` (dict, facultatif) : Données d'entrée à transmettre au workflow
|
||||
- `timeout` (float) : Délai d'attente pour la requête initiale en secondes
|
||||
- `timeout` (float, facultatif) : Délai d'expiration en secondes
|
||||
- `stream` (bool, facultatif) : Activer les réponses en streaming
|
||||
- `selected_outputs` (list, facultatif) : Sorties de blocs à diffuser
|
||||
- `async_execution` (bool, facultatif) : Exécuter de manière asynchrone
|
||||
- `max_retries` (int, facultatif) : Nombre maximum de tentatives (par défaut : 3)
|
||||
- `initial_delay` (float, facultatif) : Délai initial en secondes (par défaut : 1.0)
|
||||
- `max_delay` (float, facultatif) : Délai maximum en secondes (par défaut : 30.0)
|
||||
- `backoff_multiplier` (float, facultatif) : Multiplicateur de backoff (par défaut : 2.0)
|
||||
|
||||
**Retourne :** `WorkflowExecutionResult`
|
||||
**Retourne :** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
La logique de nouvelle tentative utilise un backoff exponentiel (1s → 2s → 4s → 8s...) avec une variation aléatoire de ±25% pour éviter l'effet de horde. Si l'API fournit un en-tête `retry-after`, celui-ci sera utilisé à la place.
|
||||
|
||||
##### get_rate_limit_info()
|
||||
|
||||
Obtenir les informations actuelles sur les limites de débit à partir de la dernière réponse de l'API.
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
**Retourne :** `RateLimitInfo | None`
|
||||
|
||||
##### get_usage_limits()
|
||||
|
||||
Obtenir les limites d'utilisation actuelles et les informations de quota pour votre compte.
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**Retourne :** `UsageLimits`
|
||||
|
||||
**Structure de la réponse :**
|
||||
|
||||
```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()
|
||||
|
||||
Met à jour la clé API.
|
||||
Mettre à jour la clé API.
|
||||
|
||||
```python
|
||||
client.set_api_key("new-api-key")
|
||||
@@ -140,7 +235,7 @@ client.set_api_key("new-api-key")
|
||||
|
||||
##### set_base_url()
|
||||
|
||||
Met à jour l'URL de base.
|
||||
Mettre à jour l'URL de base.
|
||||
|
||||
```python
|
||||
client.set_base_url("https://my-custom-domain.com")
|
||||
@@ -148,7 +243,7 @@ client.set_base_url("https://my-custom-domain.com")
|
||||
|
||||
##### close()
|
||||
|
||||
Ferme la session HTTP sous-jacente.
|
||||
Fermer la session HTTP sous-jacente.
|
||||
|
||||
```python
|
||||
client.close()
|
||||
@@ -170,6 +265,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 +288,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,19 +319,26 @@ class SimStudioError(Exception):
|
||||
self.status = status
|
||||
```
|
||||
|
||||
**Codes d'erreur courants :**
|
||||
- `UNAUTHORIZED` : Clé API invalide
|
||||
- `TIMEOUT` : Délai d'attente de la requête dépassé
|
||||
- `RATE_LIMIT_EXCEEDED` : Limite de débit dépassée
|
||||
- `USAGE_LIMIT_EXCEEDED` : Limite d'utilisation dépassée
|
||||
- `EXECUTION_ERROR` : Échec de l'exécution du workflow
|
||||
|
||||
## Exemples
|
||||
|
||||
### Exécution de flux de travail basique
|
||||
### Exécution basique d'un workflow
|
||||
|
||||
<Steps>
|
||||
<Step title="Initialiser le client">
|
||||
Configurez le SimStudioClient avec votre clé API.
|
||||
</Step>
|
||||
<Step title="Valider le flux de travail">
|
||||
Vérifiez si le flux de travail est déployé et prêt pour l'exécution.
|
||||
<Step title="Valider le workflow">
|
||||
Vérifiez si le workflow est déployé et prêt pour l'exécution.
|
||||
</Step>
|
||||
<Step title="Exécuter le flux de travail">
|
||||
Lancez le flux de travail avec vos données d'entrée.
|
||||
<Step title="Exécuter le workflow">
|
||||
Lancez le workflow avec vos données d'entrée.
|
||||
</Step>
|
||||
<Step title="Gérer le résultat">
|
||||
Traitez le résultat de l'exécution et gérez les éventuelles erreurs.
|
||||
@@ -214,7 +349,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:
|
||||
@@ -246,13 +381,13 @@ run_workflow()
|
||||
|
||||
### Gestion des erreurs
|
||||
|
||||
Gérez différents types d'erreurs qui peuvent survenir pendant l'exécution du flux de travail :
|
||||
Gérez différents types d'erreurs qui peuvent survenir pendant l'exécution du workflow :
|
||||
|
||||
```python
|
||||
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,21 +419,21 @@ 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
|
||||
```
|
||||
|
||||
### Exécution de flux de travail par lots
|
||||
### Exécution de workflows par lots
|
||||
|
||||
Exécutez plusieurs flux de travail efficacement :
|
||||
Exécutez plusieurs workflows efficacement :
|
||||
|
||||
```python
|
||||
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 +474,230 @@ for result in results:
|
||||
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
|
||||
```
|
||||
|
||||
### Exécution asynchrone de workflow
|
||||
|
||||
Exécutez des workflows de manière asynchrone pour les tâches de longue durée :
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Limitation de débit et nouvelle tentative
|
||||
|
||||
Gérez les limites de débit automatiquement avec un retrait exponentiel :
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Surveillance de l'utilisation
|
||||
|
||||
Surveillez l'utilisation et les limites de votre compte :
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Exécution de workflow en streaming
|
||||
|
||||
Exécutez des workflows avec des réponses en streaming en temps réel :
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
La réponse en streaming suit le format Server-Sent Events (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]
|
||||
```
|
||||
|
||||
**Exemple de streaming avec Flask :**
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
### Configuration de l'environnement
|
||||
|
||||
Configurez le client en utilisant des variables d'environnement :
|
||||
@@ -352,8 +711,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,30 +724,30 @@ 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")
|
||||
)
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Obtenir votre clé API
|
||||
## Obtention de votre clé API
|
||||
|
||||
<Steps>
|
||||
<Step title="Connectez-vous à Sim">
|
||||
Accédez à [Sim](https://sim.ai) et connectez-vous à votre compte.
|
||||
</Step>
|
||||
<Step title="Ouvrez votre flux de travail">
|
||||
Naviguez vers le flux de travail que vous souhaitez exécuter par programmation.
|
||||
<Step title="Ouvrez votre workflow">
|
||||
Accédez au workflow que vous souhaitez exécuter par programmation.
|
||||
</Step>
|
||||
<Step title="Déployez votre flux de travail">
|
||||
Cliquez sur "Déployer" pour déployer votre flux de travail s'il n'a pas encore été déployé.
|
||||
<Step title="Déployez votre workflow">
|
||||
Cliquez sur "Déployer" pour déployer votre workflow s'il n'a pas encore été déployé.
|
||||
</Step>
|
||||
<Step title="Créez ou sélectionnez une clé API">
|
||||
Pendant le processus de déploiement, sélectionnez ou créez une clé API.
|
||||
|
||||
@@ -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'
|
||||
|
||||
Le SDK officiel TypeScript/JavaScript pour Sim offre une sécurité de type complète et prend en charge les environnements Node.js et navigateur, vous permettant d'exécuter des workflows de manière programmatique depuis vos applications Node.js, applications web et autres environnements JavaScript. Toutes les exécutions de workflow sont actuellement synchrones.
|
||||
Le SDK officiel TypeScript/JavaScript pour Sim offre une sécurité de type complète et prend en charge les environnements Node.js et navigateur, vous permettant d'exécuter des workflows par programmation depuis vos applications Node.js, applications web et autres environnements JavaScript.
|
||||
|
||||
<Callout type="info">
|
||||
Le SDK TypeScript offre une sécurité de type complète et prend en charge les environnements Node.js et navigateur. Toutes les exécutions de workflow sont actuellement synchrones.
|
||||
Le SDK TypeScript offre une sécurité de type complète, la prise en charge de l'exécution asynchrone, une limitation automatique du débit avec backoff exponentiel et le suivi d'utilisation.
|
||||
</Callout>
|
||||
|
||||
## Installation
|
||||
@@ -91,12 +91,17 @@ const result = await client.executeWorkflow('workflow-id', {
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflowId` (string) : L'identifiant du workflow à exécuter
|
||||
- `options` (ExecutionOptions, facultatif) :
|
||||
- `workflowId` (string) : L'ID du workflow à exécuter
|
||||
- `options` (ExecutionOptions, optionnel) :
|
||||
- `input` (any) : Données d'entrée à transmettre au workflow
|
||||
- `timeout` (number) : Délai d'expiration en millisecondes (par défaut : 30000)
|
||||
- `stream` (boolean) : Activer les réponses en streaming (par défaut : false)
|
||||
- `selectedOutputs` (string[]) : Bloquer les sorties à diffuser au format `blockName.attribute` (par exemple, `["agent1.content"]`)
|
||||
- `async` (boolean) : Exécuter de manière asynchrone (par défaut : false)
|
||||
|
||||
**Retourne :** `Promise<WorkflowExecutionResult>`
|
||||
**Retourne :** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
Lorsque `async: true`, retourne immédiatement avec un ID de tâche pour l'interrogation. Sinon, attend la fin de l'exécution.
|
||||
|
||||
##### getWorkflowStatus()
|
||||
|
||||
@@ -108,7 +113,7 @@ console.log('Is deployed:', status.isDeployed);
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflowId` (string) : L'identifiant du workflow
|
||||
- `workflowId` (string) : L'ID du workflow
|
||||
|
||||
**Retourne :** `Promise<WorkflowStatus>`
|
||||
|
||||
@@ -124,36 +129,125 @@ if (isReady) {
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflowId` (string) : L'identifiant du workflow
|
||||
- `workflowId` (string) : L'ID du workflow
|
||||
|
||||
**Retourne :** `Promise<boolean>`
|
||||
|
||||
##### executeWorkflowSync()
|
||||
##### getJobStatus()
|
||||
|
||||
<Callout type="info">
|
||||
Actuellement, cette méthode est identique à `executeWorkflow()` puisque toutes les exécutions sont synchrones. Cette méthode est fournie pour une compatibilité future lorsque l'exécution asynchrone sera ajoutée.
|
||||
</Callout>
|
||||
|
||||
Exécuter un workflow (actuellement synchrone, identique à `executeWorkflow()`).
|
||||
Obtenir le statut d'une exécution de tâche asynchrone.
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `taskId` (string) : L'ID de tâche retourné par l'exécution asynchrone
|
||||
|
||||
**Retourne :** `Promise<JobStatus>`
|
||||
|
||||
**Champs de réponse :**
|
||||
- `success` (boolean) : Indique si la requête a réussi
|
||||
- `taskId` (string) : L'ID de la tâche
|
||||
- `status` (string) : L'un des statuts suivants : `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (object) : Contient `startedAt`, `completedAt`, et `duration`
|
||||
- `output` (any, optionnel) : La sortie du workflow (une fois terminé)
|
||||
- `error` (any, optionnel) : Détails de l'erreur (en cas d'échec)
|
||||
- `estimatedDuration` (number, optionnel) : Durée estimée en millisecondes (lorsqu'en traitement/en file d'attente)
|
||||
|
||||
##### executeWithRetry()
|
||||
|
||||
Exécute un workflow avec une nouvelle tentative automatique en cas d'erreurs de limite de débit en utilisant un backoff exponentiel.
|
||||
|
||||
```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
|
||||
});
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- `workflowId` (string) : L'identifiant du workflow à exécuter
|
||||
- `options` (ExecutionOptions, facultatif) :
|
||||
- `input` (any) : Données d'entrée à transmettre au workflow
|
||||
- `timeout` (number) : Délai d'expiration pour la requête initiale en millisecondes
|
||||
- `options` (ExecutionOptions, facultatif) : Identique à `executeWorkflow()`
|
||||
- `retryOptions` (RetryOptions, facultatif) :
|
||||
- `maxRetries` (number) : Nombre maximum de tentatives (par défaut : 3)
|
||||
- `initialDelay` (number) : Délai initial en ms (par défaut : 1000)
|
||||
- `maxDelay` (number) : Délai maximum en ms (par défaut : 30000)
|
||||
- `backoffMultiplier` (number) : Multiplicateur de backoff (par défaut : 2)
|
||||
|
||||
**Retourne :** `Promise<WorkflowExecutionResult>`
|
||||
**Retourne :** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
La logique de nouvelle tentative utilise un backoff exponentiel (1s → 2s → 4s → 8s...) avec une variation aléatoire de ±25 % pour éviter l'effet de rafale. Si l'API fournit un en-tête `retry-after`, celui-ci sera utilisé à la place.
|
||||
|
||||
##### getRateLimitInfo()
|
||||
|
||||
Obtient les informations actuelles sur les limites de débit à partir de la dernière réponse de l'API.
|
||||
|
||||
```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));
|
||||
}
|
||||
```
|
||||
|
||||
**Retourne :** `RateLimitInfo | null`
|
||||
|
||||
##### getUsageLimits()
|
||||
|
||||
Obtient les limites d'utilisation actuelles et les informations de quota pour votre compte.
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
**Retourne :** `Promise<UsageLimits>`
|
||||
|
||||
**Structure de la réponse :**
|
||||
|
||||
```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()
|
||||
|
||||
Mettre à jour la clé API.
|
||||
Met à jour la clé API.
|
||||
|
||||
```typescript
|
||||
client.setApiKey('new-api-key');
|
||||
@@ -161,7 +255,7 @@ client.setApiKey('new-api-key');
|
||||
|
||||
##### setBaseUrl()
|
||||
|
||||
Mettre à jour l'URL de base.
|
||||
Met à jour l'URL de base.
|
||||
|
||||
```typescript
|
||||
client.setBaseUrl('https://my-custom-domain.com');
|
||||
@@ -187,6 +281,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
|
||||
@@ -198,6 +306,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
|
||||
@@ -207,9 +354,16 @@ class SimStudioError extends Error {
|
||||
}
|
||||
```
|
||||
|
||||
**Codes d'erreur courants :**
|
||||
- `UNAUTHORIZED` : Clé API invalide
|
||||
- `TIMEOUT` : Délai d'attente de la requête dépassé
|
||||
- `RATE_LIMIT_EXCEEDED` : Limite de débit dépassée
|
||||
- `USAGE_LIMIT_EXCEEDED` : Limite d'utilisation dépassée
|
||||
- `EXECUTION_ERROR` : Échec de l'exécution du workflow
|
||||
|
||||
## Exemples
|
||||
|
||||
### Exécution de workflow basique
|
||||
### Exécution basique d'un workflow
|
||||
|
||||
<Steps>
|
||||
<Step title="Initialiser le client">
|
||||
@@ -230,7 +384,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 +425,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 +469,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 +487,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 +511,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 +553,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(
|
||||
@@ -469,14 +623,14 @@ document.getElementById('executeBtn')?.addEventListener('click', executeClientSi
|
||||
|
||||
### Exemple de hook React
|
||||
|
||||
Créez un hook React personnalisé pour l'exécution du workflow :
|
||||
Créer un hook React personnalisé pour l'exécution de workflow :
|
||||
|
||||
```typescript
|
||||
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 {
|
||||
@@ -532,7 +686,7 @@ function WorkflowComponent() {
|
||||
<button onClick={handleExecute} disabled={loading}>
|
||||
{loading ? 'Executing...' : 'Execute Workflow'}
|
||||
</button>
|
||||
|
||||
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
{result && (
|
||||
<div>
|
||||
@@ -545,38 +699,267 @@ function WorkflowComponent() {
|
||||
}
|
||||
```
|
||||
|
||||
## Obtenir votre clé API
|
||||
### Exécution asynchrone de workflow
|
||||
|
||||
Exécuter des workflows de manière asynchrone pour les tâches de longue durée :
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
### Limitation de débit et nouvelle tentative
|
||||
|
||||
Gérer automatiquement les limites de débit avec backoff exponentiel :
|
||||
|
||||
```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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Surveillance d'utilisation
|
||||
|
||||
Surveiller l'utilisation et les limites de votre compte :
|
||||
|
||||
```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:
|
||||
|
||||
### 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 {
|
||||
// Activer le streaming pour des sorties de blocs spécifiques
|
||||
const result = await client.executeWorkflow('workflow-id', {
|
||||
input: { message: 'Compter jusqu'à cinq' },
|
||||
stream: true,
|
||||
selectedOutputs: ['agent1.content'] // Utiliser le format blockName.attribute
|
||||
});
|
||||
|
||||
console.log('Résultat du workflow :', result);
|
||||
} catch (error) {
|
||||
console.error('Erreur :', 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":", deux"}
|
||||
|
||||
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 ? 'Génération en cours...' : 'Démarrer le streaming'}
|
||||
</button>
|
||||
<div style={{ whiteSpace: 'pre-wrap' }}>{output}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Your API Key
|
||||
|
||||
<Steps>
|
||||
<Step title="Connectez-vous à Sim">
|
||||
Accédez à [Sim](https://sim.ai) et connectez-vous à votre compte.
|
||||
<Step title="Log in to Sim">
|
||||
Navigate to [Sim](https://sim.ai) and log in to your account.
|
||||
</Step>
|
||||
<Step title="Ouvrez votre workflow">
|
||||
Accédez au workflow que vous souhaitez exécuter par programmation.
|
||||
<Step title="Open your workflow">
|
||||
Navigate to the workflow you want to execute programmatically.
|
||||
</Step>
|
||||
<Step title="Déployez votre workflow">
|
||||
Cliquez sur « Déployer » pour déployer votre workflow s'il n'a pas encore été déployé.
|
||||
<Step title="Deploy your workflow">
|
||||
Click on "Deploy" to deploy your workflow if it hasn't been deployed yet.
|
||||
</Step>
|
||||
<Step title="Créez ou sélectionnez une clé API">
|
||||
Pendant le processus de déploiement, sélectionnez ou créez une clé API.
|
||||
<Step title="Create or select an API key">
|
||||
During the deployment process, select or create an API key.
|
||||
</Step>
|
||||
<Step title="Copiez la clé API">
|
||||
Copiez la clé API à utiliser dans votre application TypeScript/JavaScript.
|
||||
<Step title="Copy the API key">
|
||||
Copy the API key to use in your TypeScript/JavaScript application.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning">
|
||||
Gardez votre clé API en sécurité et ne la soumettez jamais au contrôle de version. Utilisez des variables d'environnement ou une gestion de configuration sécurisée.
|
||||
Keep your API key secure and never commit it to version control. Use environment variables or secure configuration management.
|
||||
</Callout>
|
||||
|
||||
## Prérequis
|
||||
## Requirements
|
||||
|
||||
- Node.js 16+
|
||||
- TypeScript 5.0+ (pour les projets TypeScript)
|
||||
- TypeScript 5.0+ (for TypeScript projects)
|
||||
|
||||
## Support TypeScript
|
||||
## TypeScript Support
|
||||
|
||||
Le SDK est écrit en TypeScript et offre une sécurité de type complète :
|
||||
The SDK is written in TypeScript and provides full type safety:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -588,13 +971,13 @@ 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
|
||||
const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-id', {
|
||||
input: {
|
||||
message: 'Hello, TypeScript!'
|
||||
message: 'Bonjour, TypeScript !'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -602,6 +985,7 @@ const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-i
|
||||
const status: WorkflowStatus = await client.getWorkflowStatus('workflow-id');
|
||||
```
|
||||
|
||||
## Licence
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
|
||||
Apache-2.0
|
||||
|
||||
@@ -24,14 +24,6 @@ Le déclencheur d'API expose votre flux de travail en tant que point de terminai
|
||||
|
||||
Ajoutez un champ **Format d'entrée** pour chaque paramètre. Les clés de sortie d'exécution reflètent le schéma et sont également disponibles sous `<api.input>`.
|
||||
|
||||
```yaml
|
||||
- type: string
|
||||
name: userId
|
||||
value: demo-user # optional manual test value
|
||||
- type: number
|
||||
name: maxTokens
|
||||
```
|
||||
|
||||
Les exécutions manuelles dans l'éditeur utilisent la colonne `value` pour que vous puissiez tester sans envoyer de requête. Pendant l'exécution, le résolveur remplit à la fois `<api.userId>` et `<api.input.userId>`.
|
||||
|
||||
## Exemple de requête
|
||||
@@ -44,17 +36,95 @@ curl -X POST \
|
||||
-d '{"userId":"demo-user","maxTokens":1024}'
|
||||
```
|
||||
|
||||
Les réponses réussies renvoient le résultat d'exécution sérialisé de l'Exécuteur. Les erreurs révèlent des problèmes de validation, d'authentification ou d'échec du flux de travail.
|
||||
Les réponses réussies renvoient le résultat d'exécution sérialisé de l'exécuteur. Les erreurs révèlent des problèmes de validation, d'authentification ou d'échec du workflow.
|
||||
|
||||
## Référence de sortie
|
||||
## Réponses en streaming
|
||||
|
||||
Activez le streaming en temps réel pour recevoir les résultats du workflow au fur et à mesure qu'ils sont générés, caractère par caractère. Cela est utile pour afficher progressivement les réponses de l'IA aux utilisateurs.
|
||||
|
||||
### Paramètres de requête
|
||||
|
||||
Ajoutez ces paramètres pour activer le streaming :
|
||||
|
||||
- `stream` - Définissez à `true` pour activer le streaming Server-Sent Events (SSE)
|
||||
- `selectedOutputs` - Tableau des sorties de blocs à diffuser en streaming (par exemple, `["agent1.content"]`)
|
||||
|
||||
### Format de sortie de bloc
|
||||
|
||||
Utilisez le format `blockName.attribute` pour spécifier quelles sorties de blocs diffuser en streaming :
|
||||
- Format : `"blockName.attribute"` (par exemple, si vous souhaitez diffuser en streaming le contenu du bloc Agent 1, vous utiliseriez `"agent1.content"`)
|
||||
- Les noms de blocs ne sont pas sensibles à la casse et les espaces sont ignorés
|
||||
|
||||
### Exemple de requête
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Format de réponse
|
||||
|
||||
Les réponses en streaming utilisent le format Server-Sent Events (SSE) :
|
||||
|
||||
```
|
||||
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]
|
||||
```
|
||||
|
||||
Chaque événement comprend :
|
||||
- **Fragments en streaming** : `{"blockId": "...", "chunk": "text"}` - Texte en temps réel au fur et à mesure qu'il est généré
|
||||
- **Événement final** : `{"event": "done", ...}` - Métadonnées d'exécution et résultats complets
|
||||
- **Terminateur** : `[DONE]` - Signale la fin du flux
|
||||
|
||||
### Streaming de plusieurs blocs
|
||||
|
||||
Lorsque `selectedOutputs` inclut plusieurs blocs, chaque fragment indique quel bloc l'a produit :
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
Le champ `blockId` dans chaque fragment vous permet d'acheminer la sortie vers l'élément d'interface utilisateur approprié :
|
||||
|
||||
```
|
||||
data: {"blockId":"agent1-uuid","chunk":"Processing..."}
|
||||
|
||||
data: {"blockId":"agent2-uuid","chunk":"Analyzing..."}
|
||||
|
||||
data: {"blockId":"agent1-uuid","chunk":" complete"}
|
||||
```
|
||||
|
||||
## Référence des sorties
|
||||
|
||||
| Référence | Description |
|
||||
|-----------|-------------|
|
||||
| `<api.field>` | Champ défini dans le Format d'entrée |
|
||||
| `<api.input>` | Corps de la requête structuré complet |
|
||||
| `<api.field>` | Champ défini dans le format d'entrée |
|
||||
| `<api.input>` | Corps de requête structuré complet |
|
||||
|
||||
Si aucun Format d'entrée n'est défini, l'exécuteur expose le JSON brut uniquement à `<api.input>`.
|
||||
Si aucun format d'entrée n'est défini, l'exécuteur expose uniquement le JSON brut à `<api.input>`.
|
||||
|
||||
<Callout type="warning">
|
||||
Un flux de travail ne peut contenir qu'un seul déclencheur d'API. Publiez un nouveau déploiement après les modifications pour que le point de terminaison reste à jour.
|
||||
Un workflow ne peut contenir qu'un seul déclencheur API. Publiez un nouveau déploiement après les modifications pour que le point de terminaison reste à jour.
|
||||
</Callout>
|
||||
|
||||
@@ -10,7 +10,6 @@ type: object
|
||||
required:
|
||||
- type
|
||||
- name
|
||||
- inputs
|
||||
- connections
|
||||
properties:
|
||||
type:
|
||||
@@ -22,21 +21,23 @@ properties:
|
||||
description: Display name for this loop block
|
||||
inputs:
|
||||
type: object
|
||||
required:
|
||||
- loopType
|
||||
description: Optional. If omitted, defaults will be applied.
|
||||
properties:
|
||||
loopType:
|
||||
type: string
|
||||
enum: [for, forEach]
|
||||
description: Type of loop to execute
|
||||
default: for
|
||||
iterations:
|
||||
type: number
|
||||
description: Number of iterations (for 'for' loops)
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
collection:
|
||||
type: string
|
||||
description: Collection to iterate over (for 'forEach' loops)
|
||||
default: ""
|
||||
maxConcurrency:
|
||||
type: number
|
||||
description: Maximum concurrent executions
|
||||
@@ -45,13 +46,10 @@ properties:
|
||||
maximum: 10
|
||||
connections:
|
||||
type: object
|
||||
required:
|
||||
- loop
|
||||
properties:
|
||||
# Nested format (recommended)
|
||||
loop:
|
||||
type: object
|
||||
required:
|
||||
- start
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
@@ -59,26 +57,37 @@ properties:
|
||||
end:
|
||||
type: string
|
||||
description: Target block ID for loop completion (optional)
|
||||
# Direct handle format (alternative)
|
||||
loop-start-source:
|
||||
type: string | string[]
|
||||
description: Target block ID to execute inside the loop (direct format)
|
||||
loop-end-source:
|
||||
type: string | string[]
|
||||
description: Target block ID for loop completion (direct format, optional)
|
||||
error:
|
||||
type: string
|
||||
description: Target block ID for error handling
|
||||
note: Use either the nested 'loop' format OR the direct 'loop-start-source' format, not both
|
||||
```
|
||||
|
||||
## Configuration de connexion
|
||||
|
||||
Les blocs Loop utilisent un format de connexion spécial avec une section `loop` :
|
||||
Les blocs de boucle prennent en charge deux formats de connexion :
|
||||
|
||||
### Format de gestion directe (alternative)
|
||||
|
||||
```yaml
|
||||
connections:
|
||||
loop:
|
||||
start: <string> # Target block ID to execute inside the loop
|
||||
end: <string> # Target block ID after loop completion (optional)
|
||||
loop-start-source: <string> # Target block ID to execute inside the loop
|
||||
loop-end-source: <string> # Target block ID after loop completion (optional)
|
||||
error: <string> # Target block ID for error handling (optional)
|
||||
```
|
||||
|
||||
Les deux formats fonctionnent de manière identique. Utilisez celui que vous préférez.
|
||||
|
||||
## Configuration des blocs enfants
|
||||
|
||||
Les blocs à l'intérieur d'une boucle doivent avoir leur `parentId` défini sur l'ID du bloc de boucle :
|
||||
Les blocs à l'intérieur d'une boucle doivent avoir leur `parentId` défini sur l'ID du bloc de boucle. La propriété `extent` est automatiquement définie sur `'parent'` et n'a pas besoin d'être spécifiée :
|
||||
|
||||
```yaml
|
||||
loop-1:
|
||||
@@ -261,9 +270,62 @@ process-task:
|
||||
success: task-completed
|
||||
```
|
||||
|
||||
### Exemple de format de gestion directe
|
||||
|
||||
La même boucle peut être écrite en utilisant le format de gestion directe :
|
||||
|
||||
```yaml
|
||||
my-loop:
|
||||
type: loop
|
||||
name: "Process Items"
|
||||
inputs:
|
||||
loopType: forEach
|
||||
collection: <start.items>
|
||||
connections:
|
||||
loop-start-source: process-item # Direct handle format
|
||||
loop-end-source: final-results # Direct handle format
|
||||
error: handle-error
|
||||
|
||||
process-item:
|
||||
type: agent
|
||||
name: "Process Item"
|
||||
parentId: my-loop
|
||||
inputs:
|
||||
systemPrompt: "Process this item"
|
||||
userPrompt: <loop.currentItem>
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
### Exemple de boucle minimale (utilisant les valeurs par défaut)
|
||||
|
||||
Vous pouvez omettre entièrement la section `inputs`, et les valeurs par défaut seront appliquées :
|
||||
|
||||
```yaml
|
||||
simple-loop:
|
||||
type: loop
|
||||
name: "Simple Loop"
|
||||
# No inputs section - defaults to loopType: 'for', iterations: 5
|
||||
connections:
|
||||
loop-start-source: process-step
|
||||
loop-end-source: complete
|
||||
|
||||
process-step:
|
||||
type: agent
|
||||
name: "Process Step"
|
||||
parentId: simple-loop
|
||||
inputs:
|
||||
systemPrompt: "Execute step"
|
||||
userPrompt: "Step <loop.index>"
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
Cette boucle exécutera 5 itérations par défaut.
|
||||
|
||||
## Variables de boucle
|
||||
|
||||
À l'intérieur des blocs enfants de la boucle, ces variables spéciales sont disponibles :
|
||||
À l'intérieur des blocs enfants de boucle, ces variables spéciales sont disponibles :
|
||||
|
||||
```yaml
|
||||
# Available in all child blocks of the loop
|
||||
|
||||
@@ -172,33 +172,7 @@ When responding to questions about investments, include risk disclaimers.
|
||||
- コンテキストのために会話履歴を読み取る
|
||||
- エージェントの実行後に返信を追加
|
||||
|
||||
```yaml
|
||||
# 1) Add latest user message
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: user
|
||||
content: {{input}}
|
||||
|
||||
# 2) Load conversation history
|
||||
- Memory (operation: get)
|
||||
id: chat
|
||||
|
||||
# 3) Run the agent with prior messages available
|
||||
- Agent
|
||||
System Prompt: ...
|
||||
User Prompt: |
|
||||
Use the conversation so far:
|
||||
{{memory_get.memories}}
|
||||
Current user message: {{input}}
|
||||
|
||||
# 4) Store the agent reply
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: assistant
|
||||
content: {{agent.content}}
|
||||
```
|
||||
|
||||
詳細については`Memory`ブロックリファレンスを参照してください: [/tools/memory](/tools/memory)。
|
||||
詳細については[`Memory`](/tools/memory)ブロックリファレンスを参照してください。
|
||||
|
||||
## 入力と出力
|
||||
|
||||
@@ -209,7 +183,7 @@ When responding to questions about investments, include risk disclaimers.
|
||||
<strong>システムプロンプト</strong>: エージェントの動作と役割を定義する指示
|
||||
</li>
|
||||
<li>
|
||||
<strong>ユーザープロンプト</strong>: 処理する入力テキストまたはデータ
|
||||
<strong>ユーザープロンプト</strong>: 処理するテキストまたはデータ入力
|
||||
</li>
|
||||
<li>
|
||||
<strong>モデル</strong>: AIモデルの選択(OpenAI、Anthropic、Google など)
|
||||
@@ -221,14 +195,14 @@ When responding to questions about investments, include risk disclaimers.
|
||||
<strong>ツール</strong>: 関数呼び出し用の利用可能なツールの配列
|
||||
</li>
|
||||
<li>
|
||||
<strong>レスポンス形式</strong>: 構造化出力用のJSONスキーマ
|
||||
<strong>レスポンス形式</strong>: 構造化された出力のためのJSONスキーマ
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>agent.content</strong>: エージェントのレスポンステキストまたは構造化データ
|
||||
<strong>agent.content</strong>: エージェントの応答テキストまたは構造化データ
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.tokens</strong>: トークン使用統計オブジェクト
|
||||
@@ -237,14 +211,14 @@ When responding to questions about investments, include risk disclaimers.
|
||||
<strong>agent.tool_calls</strong>: ツール実行詳細の配列
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.cost</strong>: 推定APIコールコスト(利用可能な場合)
|
||||
<strong>agent.cost</strong>: 推定APIコール費用(利用可能な場合)
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>コンテンツ</strong>: エージェントからの主要なレスポンス出力
|
||||
<strong>コンテンツ</strong>: エージェントからの主要な応答出力
|
||||
</li>
|
||||
<li>
|
||||
<strong>メタデータ</strong>: 使用統計と実行詳細
|
||||
@@ -261,43 +235,43 @@ When responding to questions about investments, include risk disclaimers.
|
||||
### カスタマーサポートの自動化
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">シナリオ:データベースアクセスによる顧客問い合わせ対応</h4>
|
||||
<h4 className="font-medium">シナリオ: データベースアクセスによる顧客問い合わせ対応</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>ユーザーがAPIブロックを通じてサポートチケットを送信</li>
|
||||
<li>ユーザーがAPIブロック経由でサポートチケットを送信</li>
|
||||
<li>エージェントがPostgresで注文/サブスクリプションを確認し、ナレッジベースでガイダンスを検索</li>
|
||||
<li>エスカレーションが必要な場合、エージェントは関連コンテキストを含むLinearの課題を作成</li>
|
||||
<li>エージェントが明確なメール返信を作成</li>
|
||||
<li>エージェントが明確な返信メールを作成</li>
|
||||
<li>Gmailが顧客に返信を送信</li>
|
||||
<li>将来のメッセージのために会話履歴を維持するため、会話がメモリに保存される</li>
|
||||
<li>将来のメッセージのために履歴を維持するため、会話がメモリに保存される</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### マルチモデルコンテンツ分析
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">シナリオ:異なるAIモデルでコンテンツを分析</h4>
|
||||
<h4 className="font-medium">シナリオ: 異なるAIモデルでコンテンツを分析</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>ファンクションブロックがアップロードされた文書を処理</li>
|
||||
<li>関数ブロックがアップロードされた文書を処理</li>
|
||||
<li>GPT-4oを搭載したエージェントが技術的分析を実行</li>
|
||||
<li>Claudeを搭載したエージェントが感情やトーンを分析</li>
|
||||
<li>ファンクションブロックが最終レポート用に結果を統合</li>
|
||||
<li>Claudeを搭載したエージェントが感情とトーンを分析</li>
|
||||
<li>関数ブロックが最終レポート用に結果を統合</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
### ツール活用型リサーチアシスタント
|
||||
### ツール搭載型リサーチアシスタント
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">シナリオ:ウェブ検索と文書アクセス機能を持つリサーチアシスタント</h4>
|
||||
<h4 className="font-medium">シナリオ:ウェブ検索とドキュメントアクセス機能を持つリサーチアシスタント</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>入力を通じてユーザークエリを受信</li>
|
||||
<li>入力からユーザークエリを受信</li>
|
||||
<li>エージェントがGoogle検索ツールを使用してウェブを検索</li>
|
||||
<li>エージェントが社内文書用のNotionデータベースにアクセス</li>
|
||||
<li>エージェントが包括的な調査レポートをまとめる</li>
|
||||
<li>エージェントが包括的な調査レポートを作成</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
## ベストプラクティス
|
||||
|
||||
- **システムプロンプトを具体的に**: エージェントの役割、トーン、制限を明確に定義してください。指示が具体的であればあるほど、エージェントは目的を果たすことができます。
|
||||
- **適切な温度設定を選択**: 精度が重要な場合は低い温度設定(0〜0.3)を使用し、よりクリエイティブまたは多様な応答には温度を上げる(0.7〜2.0)
|
||||
- **ツールを効果的に活用**: エージェントの目的を補完し、その能力を強化するツールを統合してください。エージェントに負担をかけないよう、提供するツールを選択的にしてください。重複の少いタスクには、最良の結果を得るために別のエージェントブロックを使用してください。
|
||||
- **システムプロンプトで具体的に指示する**:エージェントの役割、トーン、制限を明確に定義してください。指示が具体的であればあるほど、エージェントは目的を果たすことができます。
|
||||
- **適切な温度設定を選択する**:精度が重要な場合は低い温度設定(0〜0.3)を使用し、よりクリエイティブまたは多様な応答を得るには温度を上げる(0.7〜2.0)
|
||||
- **ツールを効果的に活用する**:エージェントの目的を補完し、その能力を強化するツールを統合してください。エージェントに負担をかけないよう、提供するツールを選択的にしてください。重複の少いタスクには、最良の結果を得るために別のエージェントブロックを使用してください。
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
Simの公式Python SDKを使用すると、公式Python SDKを使用してPythonアプリケーションからプログラムでワークフローを実行できます。
|
||||
|
||||
<Callout type="info">
|
||||
Python SDKはPython 3.8以上をサポートし、同期的なワークフロー実行を提供します。現在、すべてのワークフロー実行は同期的です。
|
||||
Python SDKはPython 3.8以上をサポートし、非同期実行、指数バックオフによる自動レート制限、使用状況追跡機能を提供します。
|
||||
</Callout>
|
||||
|
||||
## インストール
|
||||
@@ -70,12 +70,17 @@ result = client.execute_workflow(
|
||||
)
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
**パラメータ:**
|
||||
- `workflow_id` (str): 実行するワークフローのID
|
||||
- `input_data` (dict, オプション): ワークフローに渡す入力データ
|
||||
- `timeout` (float, オプション): タイムアウト(秒)(デフォルト:30.0)
|
||||
- `timeout` (float, オプション): タイムアウト(秒)(デフォルト: 30.0)
|
||||
- `stream` (bool, オプション): ストリーミングレスポンスを有効にする(デフォルト: False)
|
||||
- `selected_outputs` (list[str], オプション): `blockName.attribute`形式でストリーミングするブロック出力(例: `["agent1.content"]`)
|
||||
- `async_execution` (bool, オプション): 非同期実行(デフォルト: False)
|
||||
|
||||
**戻り値:** `WorkflowExecutionResult`
|
||||
**戻り値:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
`async_execution=True`の場合、ポーリング用のタスクIDをすぐに返します。それ以外の場合は、完了を待ちます。
|
||||
|
||||
##### get_workflow_status()
|
||||
|
||||
@@ -86,7 +91,7 @@ status = client.get_workflow_status("workflow-id")
|
||||
print("Is deployed:", status.is_deployed)
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
**パラメータ:**
|
||||
- `workflow_id` (str): ワークフローのID
|
||||
|
||||
**戻り値:** `WorkflowStatus`
|
||||
@@ -107,28 +112,118 @@ if is_ready:
|
||||
|
||||
**戻り値:** `bool`
|
||||
|
||||
##### execute_workflow_sync()
|
||||
##### get_job_status()
|
||||
|
||||
<Callout type="info">
|
||||
現在、このメソッドは `execute_workflow()` と同一です。すべての実行は同期的に行われるためです。このメソッドは、将来的に非同期実行が追加された際の互換性のために提供されています。
|
||||
</Callout>
|
||||
|
||||
ワークフローを実行します(現在は同期的、`execute_workflow()` と同じ)。
|
||||
非同期ジョブ実行のステータスを取得します。
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `task_id` (str): 非同期実行から返されたタスクID
|
||||
|
||||
**戻り値:** `Dict[str, Any]`
|
||||
|
||||
**レスポンスフィールド:**
|
||||
- `success` (bool): リクエストが成功したかどうか
|
||||
- `taskId` (str): タスクID
|
||||
- `status` (str): 次のいずれか: `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (dict): `startedAt`, `completedAt`, `duration`を含む
|
||||
- `output` (any, オプション): ワークフロー出力(完了時)
|
||||
- `error` (any, オプション): エラー詳細(失敗時)
|
||||
- `estimatedDuration` (int, オプション): 推定所要時間(ミリ秒)(処理中/キュー時)
|
||||
|
||||
##### execute_with_retry()
|
||||
|
||||
指数バックオフを使用してレート制限エラーで自動的に再試行するワークフロー実行。
|
||||
|
||||
```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
|
||||
)
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `workflow_id` (str): 実行するワークフローのID
|
||||
- `input_data` (dict, optional): ワークフローに渡す入力データ
|
||||
- `timeout` (float): 初期リクエストのタイムアウト(秒)
|
||||
- `input_data` (dict, オプション): ワークフローに渡す入力データ
|
||||
- `timeout` (float, オプション): タイムアウト(秒)
|
||||
- `stream` (bool, オプション): ストリーミングレスポンスを有効にする
|
||||
- `selected_outputs` (list, オプション): ストリーミングするブロック出力
|
||||
- `async_execution` (bool, オプション): 非同期実行
|
||||
- `max_retries` (int, オプション): 最大再試行回数(デフォルト: 3)
|
||||
- `initial_delay` (float, オプション): 初期遅延(秒)(デフォルト: 1.0)
|
||||
- `max_delay` (float, オプション): 最大遅延(秒)(デフォルト: 30.0)
|
||||
- `backoff_multiplier` (float, オプション): バックオフ乗数(デフォルト: 2.0)
|
||||
|
||||
**戻り値:** `WorkflowExecutionResult`
|
||||
**戻り値:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
リトライロジックは、サンダリングハード問題を防ぐために±25%のジッターを伴う指数バックオフ(1秒→2秒→4秒→8秒...)を使用します。APIが `retry-after` ヘッダーを提供する場合、代わりにそれが使用されます。
|
||||
|
||||
##### get_rate_limit_info()
|
||||
|
||||
最後のAPIレスポンスから現在のレート制限情報を取得します。
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
**戻り値:** `RateLimitInfo | None`
|
||||
|
||||
##### get_usage_limits()
|
||||
|
||||
アカウントの現在の使用制限とクォータ情報を取得します。
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**戻り値:** `UsageLimits`
|
||||
|
||||
**レスポンス構造:**
|
||||
|
||||
```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 +265,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 +288,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 +319,13 @@ class SimStudioError(Exception):
|
||||
self.status = status
|
||||
```
|
||||
|
||||
**一般的なエラーコード:**
|
||||
- `UNAUTHORIZED`: 無効なAPIキー
|
||||
- `TIMEOUT`: リクエストがタイムアウトしました
|
||||
- `RATE_LIMIT_EXCEEDED`: レート制限を超えました
|
||||
- `USAGE_LIMIT_EXCEEDED`: 使用制限を超えました
|
||||
- `EXECUTION_ERROR`: ワークフローの実行に失敗しました
|
||||
|
||||
## 例
|
||||
|
||||
### 基本的なワークフロー実行
|
||||
@@ -214,7 +349,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 +387,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:
|
||||
@@ -277,14 +412,14 @@ def execute_with_error_handling():
|
||||
|
||||
### コンテキストマネージャーの使用
|
||||
|
||||
リソースのクリーンアップを自動的に処理するためにコンテキストマネージャーとしてクライアントを使用します:
|
||||
リソースのクリーンアップを自動的に処理するためにクライアントをコンテキストマネージャーとして使用します:
|
||||
|
||||
```python
|
||||
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 +433,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."""
|
||||
@@ -339,6 +474,230 @@ for result in results:
|
||||
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
|
||||
```
|
||||
|
||||
### 非同期ワークフロー実行
|
||||
|
||||
長時間実行されるタスクのためにワークフローを非同期で実行します:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### レート制限とリトライ
|
||||
|
||||
指数バックオフを使用して自動的にレート制限を処理します:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### 使用状況モニタリング
|
||||
|
||||
アカウントの使用状況と制限をモニタリングします:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### ワークフローの実行ストリーミング
|
||||
|
||||
リアルタイムのストリーミングレスポンスでワークフローを実行します:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
ストリーミングレスポンスはServer-Sent Events(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]
|
||||
```
|
||||
|
||||
**Flaskストリーミングの例:**
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
### 環境設定
|
||||
|
||||
環境変数を使用してクライアントを設定します:
|
||||
@@ -352,8 +711,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 +724,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")
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
公式TypeScript/JavaScript SDKはSimのために完全な型安全性を提供し、Node.jsとブラウザ環境の両方をサポートしています。これにより、Node.jsアプリケーション、Webアプリケーション、その他のJavaScript環境からプログラムでワークフローを実行することができます。現在、すべてのワークフロー実行は同期的に行われます。
|
||||
Sim用の公式TypeScript/JavaScript SDKは、完全な型安全性を提供し、Node.jsとブラウザ環境の両方をサポートしています。これにより、Node.jsアプリケーション、Webアプリケーション、その他のJavaScript環境からプログラムによってワークフローを実行することができます。
|
||||
|
||||
<Callout type="info">
|
||||
TypeScript SDKは完全な型安全性を提供し、Node.jsとブラウザ環境の両方をサポートしています。現在、すべてのワークフロー実行は同期的に行われます。
|
||||
TypeScript SDKは、完全な型安全性、非同期実行サポート、指数バックオフによる自動レート制限、使用状況追跡を提供します。
|
||||
</Callout>
|
||||
|
||||
## インストール
|
||||
@@ -95,8 +95,13 @@ const result = await client.executeWorkflow('workflow-id', {
|
||||
- `options` (ExecutionOptions, オプション):
|
||||
- `input` (any): ワークフローに渡す入力データ
|
||||
- `timeout` (number): タイムアウト(ミリ秒)(デフォルト: 30000)
|
||||
- `stream` (boolean): ストリーミングレスポンスを有効にする(デフォルト: false)
|
||||
- `selectedOutputs` (string[]): `blockName.attribute`形式でストリーミングするブロック出力(例: `["agent1.content"]`)
|
||||
- `async` (boolean): 非同期実行(デフォルト: false)
|
||||
|
||||
**戻り値:** `Promise<WorkflowExecutionResult>`
|
||||
**戻り値:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
`async: true`の場合、ポーリング用のタスクIDをすぐに返します。それ以外の場合は、完了を待ちます。
|
||||
|
||||
##### getWorkflowStatus()
|
||||
|
||||
@@ -114,7 +119,7 @@ console.log('Is deployed:', status.isDeployed);
|
||||
|
||||
##### validateWorkflow()
|
||||
|
||||
ワークフローが実行準備ができているか検証します。
|
||||
ワークフローが実行準備ができているかを検証します。
|
||||
|
||||
```typescript
|
||||
const isReady = await client.validateWorkflow('workflow-id');
|
||||
@@ -128,28 +133,117 @@ if (isReady) {
|
||||
|
||||
**戻り値:** `Promise<boolean>`
|
||||
|
||||
##### executeWorkflowSync()
|
||||
##### getJobStatus()
|
||||
|
||||
<Callout type="info">
|
||||
現在、このメソッドは `executeWorkflow()` と同一です。すべての実行は同期的に行われるためです。このメソッドは、将来的に非同期実行が追加された際の互換性のために提供されています。
|
||||
</Callout>
|
||||
|
||||
ワークフローを実行します(現在は同期的、`executeWorkflow()` と同じ)。
|
||||
非同期ジョブ実行のステータスを取得します。
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `taskId` (string): 非同期実行から返されたタスクID
|
||||
|
||||
**戻り値:** `Promise<JobStatus>`
|
||||
|
||||
**レスポンスフィールド:**
|
||||
- `success` (boolean): リクエストが成功したかどうか
|
||||
- `taskId` (string): タスクID
|
||||
- `status` (string): 次のいずれか `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (object): `startedAt`, `completedAt`, および `duration` を含む
|
||||
- `output` (any, オプション): ワークフロー出力(完了時)
|
||||
- `error` (any, オプション): エラー詳細(失敗時)
|
||||
- `estimatedDuration` (number, オプション): 推定所要時間(ミリ秒)(処理中/キュー時)
|
||||
|
||||
##### executeWithRetry()
|
||||
|
||||
レート制限エラー時に指数バックオフを使用して自動的に再試行するワークフロー実行。
|
||||
|
||||
```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
|
||||
});
|
||||
```
|
||||
|
||||
**パラメータ:**
|
||||
- `workflowId` (string): 実行するワークフローのID
|
||||
- `options` (ExecutionOptions, オプション):
|
||||
- `input` (any): ワークフローに渡す入力データ
|
||||
- `timeout` (number): 初期リクエストのタイムアウト(ミリ秒)
|
||||
- `options` (ExecutionOptions, オプション): `executeWorkflow()`と同じ
|
||||
- `retryOptions` (RetryOptions, オプション):
|
||||
- `maxRetries` (number): 最大再試行回数(デフォルト: 3)
|
||||
- `initialDelay` (number): 初期遅延(ミリ秒)(デフォルト: 1000)
|
||||
- `maxDelay` (number): 最大遅延(ミリ秒)(デフォルト: 30000)
|
||||
- `backoffMultiplier` (number): バックオフ乗数(デフォルト: 2)
|
||||
|
||||
**戻り値:** `Promise<WorkflowExecutionResult>`
|
||||
**戻り値:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
再試行ロジックは、サンダリングハード問題を防ぐために±25%のジッターを含む指数バックオフ(1秒→2秒→4秒→8秒...)を使用します。APIが`retry-after`ヘッダーを提供する場合、代わりにそれが使用されます。
|
||||
|
||||
##### getRateLimitInfo()
|
||||
|
||||
最後のAPIレスポンスから現在のレート制限情報を取得します。
|
||||
|
||||
```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));
|
||||
}
|
||||
```
|
||||
|
||||
**戻り値:** `RateLimitInfo | null`
|
||||
|
||||
##### getUsageLimits()
|
||||
|
||||
アカウントの現在の使用制限とクォータ情報を取得します。
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
**戻り値:** `Promise<UsageLimits>`
|
||||
|
||||
**レスポンス構造:**
|
||||
|
||||
```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()
|
||||
|
||||
@@ -167,7 +261,7 @@ client.setApiKey('new-api-key');
|
||||
client.setBaseUrl('https://my-custom-domain.com');
|
||||
```
|
||||
|
||||
## 型
|
||||
## 型定義
|
||||
|
||||
### WorkflowExecutionResult
|
||||
|
||||
@@ -187,6 +281,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
|
||||
@@ -198,6 +306,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
|
||||
@@ -207,9 +354,16 @@ class SimStudioError extends Error {
|
||||
}
|
||||
```
|
||||
|
||||
**一般的なエラーコード:**
|
||||
- `UNAUTHORIZED`: 無効なAPIキー
|
||||
- `TIMEOUT`: リクエストがタイムアウトしました
|
||||
- `RATE_LIMIT_EXCEEDED`: レート制限を超えました
|
||||
- `USAGE_LIMIT_EXCEEDED`: 使用制限を超えました
|
||||
- `EXECUTION_ERROR`: ワークフローの実行に失敗しました
|
||||
|
||||
## 例
|
||||
|
||||
### 基本的なワークフローの実行
|
||||
### 基本的なワークフロー実行
|
||||
|
||||
<Steps>
|
||||
<Step title="クライアントの初期化">
|
||||
@@ -230,7 +384,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 +425,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 +469,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,21 +487,21 @@ 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'
|
||||
});
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Node.js Expressとの統合
|
||||
### Node.js Express統合
|
||||
|
||||
Express.jsサーバーとの統合:
|
||||
|
||||
@@ -357,7 +511,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 +553,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(
|
||||
@@ -430,7 +584,7 @@ export default async function handler(
|
||||
|
||||
### ブラウザでの使用
|
||||
|
||||
ブラウザで使用する場合(適切なCORS設定が必要):
|
||||
ブラウザでの使用(適切なCORS設定が必要):
|
||||
|
||||
```typescript
|
||||
import { SimStudioClient } from 'simstudio-ts-sdk';
|
||||
@@ -464,7 +618,7 @@ document.getElementById('executeBtn')?.addEventListener('click', executeClientSi
|
||||
```
|
||||
|
||||
<Callout type="warning">
|
||||
ブラウザでSDKを使用する際は、機密性の高いAPIキーを公開しないよう注意してください。バックエンドプロキシや権限が制限された公開APIキーの使用を検討してください。
|
||||
ブラウザでSDKを使用する場合、機密性の高いAPIキーを公開しないよう注意してください。バックエンドプロキシや権限が制限された公開APIキーの使用を検討してください。
|
||||
</Callout>
|
||||
|
||||
### Reactフックの例
|
||||
@@ -476,7 +630,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 {
|
||||
@@ -532,7 +686,7 @@ function WorkflowComponent() {
|
||||
<button onClick={handleExecute} disabled={loading}>
|
||||
{loading ? 'Executing...' : 'Execute Workflow'}
|
||||
</button>
|
||||
|
||||
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
{result && (
|
||||
<div>
|
||||
@@ -545,38 +699,267 @@ function WorkflowComponent() {
|
||||
}
|
||||
```
|
||||
|
||||
## APIキーの取得方法
|
||||
### 非同期ワークフロー実行
|
||||
|
||||
長時間実行タスク向けに非同期でワークフローを実行:
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
### レート制限とリトライ
|
||||
|
||||
指数バックオフによるレート制限の自動処理:
|
||||
|
||||
```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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用状況モニタリング
|
||||
|
||||
アカウントの使用状況と制限のモニタリング:
|
||||
|
||||
```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:
|
||||
|
||||
### 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 {
|
||||
// 特定のブロック出力のストリーミングを有効化
|
||||
const result = await client.executeWorkflow('workflow-id', {
|
||||
input: { message: 'Count to five' },
|
||||
stream: true,
|
||||
selectedOutputs: ['agent1.content'] // blockName.attribute形式を使用
|
||||
});
|
||||
|
||||
console.log('ワークフロー結果:', result);
|
||||
} catch (error) {
|
||||
console.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>
|
||||
<Step title="Simにログイン">
|
||||
[Sim](https://sim.ai)に移動してアカウントにログインします。
|
||||
<Step title="Log in to Sim">
|
||||
Navigate to [Sim](https://sim.ai) and log in to your account.
|
||||
</Step>
|
||||
<Step title="ワークフローを開く">
|
||||
プログラムで実行したいワークフローに移動します。
|
||||
<Step title="Open your workflow">
|
||||
Navigate to the workflow you want to execute programmatically.
|
||||
</Step>
|
||||
<Step title="ワークフローをデプロイ">
|
||||
まだデプロイされていない場合は、「デプロイ」をクリックしてワークフローをデプロイします。
|
||||
<Step title="Deploy your workflow">
|
||||
Click on "Deploy" to deploy your workflow if it hasn't been deployed yet.
|
||||
</Step>
|
||||
<Step title="APIキーを作成または選択">
|
||||
デプロイ処理中に、APIキーを選択または作成します。
|
||||
<Step title="Create or select an API key">
|
||||
During the deployment process, select or create an API key.
|
||||
</Step>
|
||||
<Step title="APIキーをコピー">
|
||||
TypeScript/JavaScriptアプリケーションで使用するAPIキーをコピーします。
|
||||
<Step title="Copy the API key">
|
||||
Copy the API key to use in your TypeScript/JavaScript application.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning">
|
||||
APIキーは安全に保管し、バージョン管理システムにコミットしないでください。環境変数や安全な設定管理を使用してください。
|
||||
Keep your API key secure and never commit it to version control. Use environment variables or secure configuration management.
|
||||
</Callout>
|
||||
|
||||
## 要件
|
||||
## Requirements
|
||||
|
||||
- Node.js 16以上
|
||||
- TypeScript 5.0以上(TypeScriptプロジェクトの場合)
|
||||
- Node.js 16+
|
||||
- TypeScript 5.0+ (for TypeScript projects)
|
||||
|
||||
## TypeScriptサポート
|
||||
## TypeScript Support
|
||||
|
||||
このSDKはTypeScriptで書かれており、完全な型安全性を提供します:
|
||||
The SDK is written in TypeScript and provides full type safety:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -588,7 +971,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
|
||||
@@ -602,6 +985,7 @@ const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-i
|
||||
const status: WorkflowStatus = await client.getWorkflowStatus('workflow-id');
|
||||
```
|
||||
|
||||
## ライセンス
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
|
||||
Apache-2.0
|
||||
|
||||
@@ -24,15 +24,7 @@ APIトリガーは、ワークフローを安全なHTTPエンドポイントと
|
||||
|
||||
各パラメータに**入力フォーマット**フィールドを追加します。実行時の出力キーはスキーマを反映し、`<api.input>`でも利用できます。
|
||||
|
||||
```yaml
|
||||
- type: string
|
||||
name: userId
|
||||
value: demo-user # optional manual test value
|
||||
- type: number
|
||||
name: maxTokens
|
||||
```
|
||||
|
||||
エディタでの手動実行では、リクエストを送信せずにテストできるように`value`列を使用します。実行中、リゾルバは`<api.userId>`と`<api.input.userId>`の両方に値を設定します。
|
||||
エディタでの手動実行は `value` 列を使用するため、リクエストを送信せずにテストできます。実行中、リゾルバーは `<api.userId>` と `<api.input.userId>` の両方に値を設定します。
|
||||
|
||||
## リクエスト例
|
||||
|
||||
@@ -44,7 +36,85 @@ curl -X POST \
|
||||
-d '{"userId":"demo-user","maxTokens":1024}'
|
||||
```
|
||||
|
||||
成功したレスポンスはエグゼキュータからのシリアル化された実行結果を返します。エラーは検証、認証、またはワークフローの失敗を表示します。
|
||||
成功したレスポンスはエグゼキュータからシリアル化された実行結果を返します。エラーは検証、認証、またはワークフローの失敗を表示します。
|
||||
|
||||
## ストリーミングレスポンス
|
||||
|
||||
リアルタイムストリーミングを有効にすると、ワークフローの出力が生成されるたびに文字単位で受信できます。これはAIの応答をユーザーに段階的に表示するのに役立ちます。
|
||||
|
||||
### リクエストパラメータ
|
||||
|
||||
ストリーミングを有効にするには、これらのパラメータを追加してください:
|
||||
|
||||
- `stream` - Server-Sent Events (SSE)ストリーミングを有効にするには `true` に設定します
|
||||
- `selectedOutputs` - ストリーミングするブロック出力の配列(例:`["agent1.content"]`)
|
||||
|
||||
### ブロック出力フォーマット
|
||||
|
||||
`blockName.attribute` フォーマットを使用して、ストリーミングするブロック出力を指定します:
|
||||
- フォーマット:`"blockName.attribute"`(例:Agent 1ブロックの内容をストリーミングしたい場合は、`"agent1.content"` を使用します)
|
||||
- ブロック名は大文字小文字を区別せず、スペースは無視されます
|
||||
|
||||
### リクエスト例
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
### レスポンスフォーマット
|
||||
|
||||
ストリーミングレスポンスはServer-Sent Events (SSE)フォーマットを使用します:
|
||||
|
||||
```
|
||||
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]
|
||||
```
|
||||
|
||||
各イベントには以下が含まれます:
|
||||
- **ストリーミングチャンク**:`{"blockId": "...", "chunk": "text"}` - 生成されるリアルタイムテキスト
|
||||
- **最終イベント**:`{"event": "done", ...}` - 実行メタデータと完全な結果
|
||||
- **ターミネーター**:`[DONE]` - ストリーム終了を示す信号
|
||||
|
||||
### 複数ブロックのストリーミング
|
||||
|
||||
`selectedOutputs` に複数のブロックが含まれる場合、各チャンクはどのブロックから生成されたかを示します:
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
各チャンクの `blockId` フィールドを使用して、出力を正しいUI要素にルーティングできます:
|
||||
|
||||
```
|
||||
data: {"blockId":"agent1-uuid","chunk":"Processing..."}
|
||||
|
||||
data: {"blockId":"agent2-uuid","chunk":"Analyzing..."}
|
||||
|
||||
data: {"blockId":"agent1-uuid","chunk":" complete"}
|
||||
```
|
||||
|
||||
## 出力リファレンス
|
||||
|
||||
@@ -53,8 +123,8 @@ curl -X POST \
|
||||
| `<api.field>` | 入力フォーマットで定義されたフィールド |
|
||||
| `<api.input>` | 構造化されたリクエスト本文全体 |
|
||||
|
||||
入力フォーマットが定義されていない場合、エグゼキュータは生のJSONを`<api.input>`でのみ公開します。
|
||||
入力フォーマットが定義されていない場合、エグゼキューターは `<api.input>` でのみ生のJSONを公開します。
|
||||
|
||||
<Callout type="warning">
|
||||
ワークフローには1つのAPIトリガーしか含めることができません。変更後は新しいデプロイメントを公開して、エンドポイントを最新の状態に保ってください。
|
||||
ワークフローには1つのAPIトリガーのみ含めることができます。変更後は新しいデプロイメントを公開して、エンドポイントを最新の状態に保ってください。
|
||||
</Callout>
|
||||
|
||||
@@ -10,7 +10,6 @@ type: object
|
||||
required:
|
||||
- type
|
||||
- name
|
||||
- inputs
|
||||
- connections
|
||||
properties:
|
||||
type:
|
||||
@@ -22,21 +21,23 @@ properties:
|
||||
description: Display name for this loop block
|
||||
inputs:
|
||||
type: object
|
||||
required:
|
||||
- loopType
|
||||
description: Optional. If omitted, defaults will be applied.
|
||||
properties:
|
||||
loopType:
|
||||
type: string
|
||||
enum: [for, forEach]
|
||||
description: Type of loop to execute
|
||||
default: for
|
||||
iterations:
|
||||
type: number
|
||||
description: Number of iterations (for 'for' loops)
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
collection:
|
||||
type: string
|
||||
description: Collection to iterate over (for 'forEach' loops)
|
||||
default: ""
|
||||
maxConcurrency:
|
||||
type: number
|
||||
description: Maximum concurrent executions
|
||||
@@ -45,13 +46,10 @@ properties:
|
||||
maximum: 10
|
||||
connections:
|
||||
type: object
|
||||
required:
|
||||
- loop
|
||||
properties:
|
||||
# Nested format (recommended)
|
||||
loop:
|
||||
type: object
|
||||
required:
|
||||
- start
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
@@ -59,26 +57,37 @@ properties:
|
||||
end:
|
||||
type: string
|
||||
description: Target block ID for loop completion (optional)
|
||||
# Direct handle format (alternative)
|
||||
loop-start-source:
|
||||
type: string | string[]
|
||||
description: Target block ID to execute inside the loop (direct format)
|
||||
loop-end-source:
|
||||
type: string | string[]
|
||||
description: Target block ID for loop completion (direct format, optional)
|
||||
error:
|
||||
type: string
|
||||
description: Target block ID for error handling
|
||||
note: Use either the nested 'loop' format OR the direct 'loop-start-source' format, not both
|
||||
```
|
||||
|
||||
## 接続設定
|
||||
|
||||
ループブロックは `loop` セクションを持つ特別な接続形式を使用します:
|
||||
ループブロックは2つの接続形式をサポートしています:
|
||||
|
||||
### 直接ハンドル形式(代替)
|
||||
|
||||
```yaml
|
||||
connections:
|
||||
loop:
|
||||
start: <string> # Target block ID to execute inside the loop
|
||||
end: <string> # Target block ID after loop completion (optional)
|
||||
loop-start-source: <string> # Target block ID to execute inside the loop
|
||||
loop-end-source: <string> # Target block ID after loop completion (optional)
|
||||
error: <string> # Target block ID for error handling (optional)
|
||||
```
|
||||
|
||||
## 子ブロック設定
|
||||
両方の形式は同じように機能します。お好みの方を使用してください。
|
||||
|
||||
ループ内のブロックは、その `parentId` をループブロックIDに設定する必要があります:
|
||||
## 子ブロックの設定
|
||||
|
||||
ループ内のブロックは、その `parentId` をループブロックIDに設定する必要があります。`extent` プロパティは自動的に `'parent'` に設定されるため、指定する必要はありません:
|
||||
|
||||
```yaml
|
||||
loop-1:
|
||||
@@ -261,6 +270,59 @@ process-task:
|
||||
success: task-completed
|
||||
```
|
||||
|
||||
### 直接ハンドル形式の例
|
||||
|
||||
同じループは直接ハンドル形式を使用して記述することもできます:
|
||||
|
||||
```yaml
|
||||
my-loop:
|
||||
type: loop
|
||||
name: "Process Items"
|
||||
inputs:
|
||||
loopType: forEach
|
||||
collection: <start.items>
|
||||
connections:
|
||||
loop-start-source: process-item # Direct handle format
|
||||
loop-end-source: final-results # Direct handle format
|
||||
error: handle-error
|
||||
|
||||
process-item:
|
||||
type: agent
|
||||
name: "Process Item"
|
||||
parentId: my-loop
|
||||
inputs:
|
||||
systemPrompt: "Process this item"
|
||||
userPrompt: <loop.currentItem>
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
### 最小限のループ例(デフォルトを使用)
|
||||
|
||||
`inputs` セクションを完全に省略することができ、デフォルト値が適用されます:
|
||||
|
||||
```yaml
|
||||
simple-loop:
|
||||
type: loop
|
||||
name: "Simple Loop"
|
||||
# No inputs section - defaults to loopType: 'for', iterations: 5
|
||||
connections:
|
||||
loop-start-source: process-step
|
||||
loop-end-source: complete
|
||||
|
||||
process-step:
|
||||
type: agent
|
||||
name: "Process Step"
|
||||
parentId: simple-loop
|
||||
inputs:
|
||||
systemPrompt: "Execute step"
|
||||
userPrompt: "Step <loop.index>"
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
このループはデフォルトで5回の反復を実行します。
|
||||
|
||||
## ループ変数
|
||||
|
||||
ループ子ブロック内では、以下の特殊変数が利用可能です:
|
||||
@@ -274,7 +336,7 @@ process-task:
|
||||
|
||||
## 出力参照
|
||||
|
||||
ループが完了した後、集約された結果を参照できます:
|
||||
ループが完了した後、その集計結果を参照できます:
|
||||
|
||||
```yaml
|
||||
# In blocks after the loop
|
||||
@@ -286,10 +348,10 @@ final-processor:
|
||||
|
||||
## ベストプラクティス
|
||||
|
||||
- 長い実行時間を避けるために適切な繰り返し回数の制限を設定する
|
||||
- 長い実行時間を避けるため、適切な繰り返し制限を設定する
|
||||
- コレクション処理にはforEachを、固定回数の繰り返しにはforループを使用する
|
||||
- I/O処理の多い操作にはmaxConcurrencyの使用を検討する
|
||||
- I/O処理が多い操作にはmaxConcurrencyの使用を検討する
|
||||
- 堅牢なループ実行のためにエラー処理を含める
|
||||
- ループの子ブロックには説明的な名前を使用する
|
||||
- ループ子ブロックには説明的な名前を使用する
|
||||
- 最初に小さなコレクションでテストする
|
||||
- 大きなコレクションの実行時間を監視する
|
||||
@@ -172,44 +172,18 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
- 读取对话历史以获取上下文
|
||||
- 在代理运行后附加其回复
|
||||
|
||||
```yaml
|
||||
# 1) Add latest user message
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: user
|
||||
content: {{input}}
|
||||
|
||||
# 2) Load conversation history
|
||||
- Memory (operation: get)
|
||||
id: chat
|
||||
|
||||
# 3) Run the agent with prior messages available
|
||||
- Agent
|
||||
System Prompt: ...
|
||||
User Prompt: |
|
||||
Use the conversation so far:
|
||||
{{memory_get.memories}}
|
||||
Current user message: {{input}}
|
||||
|
||||
# 4) Store the agent reply
|
||||
- Memory (operation: add)
|
||||
id: chat
|
||||
role: assistant
|
||||
content: {{agent.content}}
|
||||
```
|
||||
|
||||
有关详细信息,请参阅 `Memory` 块参考:[工具/内存](/tools/memory)。
|
||||
有关详细信息,请参阅 [`Memory`](/tools/memory) 块引用。
|
||||
|
||||
## 输入和输出
|
||||
|
||||
<Tabs items={['配置', '变量', '结果']}>
|
||||
<Tabs items={['Configuration', 'Variables', 'Results']}>
|
||||
<Tab>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>系统提示</strong>:定义代理行为和角色的指令
|
||||
</li>
|
||||
<li>
|
||||
<strong>用户提示</strong>:要处理的输入文本或数据
|
||||
<strong>用户提示</strong>:需要处理的输入文本或数据
|
||||
</li>
|
||||
<li>
|
||||
<strong>模型</strong>:AI 模型选择(OpenAI、Anthropic、Google 等)
|
||||
@@ -218,7 +192,7 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
<strong>温度</strong>:响应随机性控制(0-2)
|
||||
</li>
|
||||
<li>
|
||||
<strong>工具</strong>:可用于函数调用的工具数组
|
||||
<strong>工具</strong>:可用工具数组,用于功能调用
|
||||
</li>
|
||||
<li>
|
||||
<strong>响应格式</strong>:用于结构化输出的 JSON Schema
|
||||
@@ -234,10 +208,10 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
<strong>agent.tokens</strong>:令牌使用统计对象
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.tool_calls</strong>:工具执行详细信息数组
|
||||
<strong>agent.tool_calls</strong>:工具执行详情数组
|
||||
</li>
|
||||
<li>
|
||||
<strong>agent.cost</strong>:API 调用的估计成本(如果可用)
|
||||
<strong>agent.cost</strong>:估算的 API 调用成本(如果可用)
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
@@ -247,7 +221,7 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
<strong>内容</strong>:代理的主要响应输出
|
||||
</li>
|
||||
<li>
|
||||
<strong>元数据</strong>:使用统计信息和执行详细信息
|
||||
<strong>元数据</strong>:使用统计和执行详情
|
||||
</li>
|
||||
<li>
|
||||
<strong>访问</strong>:在代理之后的块中可用
|
||||
@@ -261,9 +235,9 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
### 客户支持自动化
|
||||
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">场景:通过数据库访问处理客户咨询</h4>
|
||||
<h4 className="font-medium">场景:通过数据库访问处理客户查询</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>用户通过 API 模块提交支持工单</li>
|
||||
<li>用户通过 API 块提交支持工单</li>
|
||||
<li>代理在 Postgres 中检查订单/订阅,并在知识库中搜索指导</li>
|
||||
<li>如果需要升级,代理会创建一个包含相关上下文的 Linear 问题</li>
|
||||
<li>代理起草一封清晰的电子邮件回复</li>
|
||||
@@ -277,10 +251,10 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
<div className="mb-4 rounded-md border p-4">
|
||||
<h4 className="font-medium">场景:使用不同的 AI 模型分析内容</h4>
|
||||
<ol className="list-decimal pl-5 text-sm">
|
||||
<li>功能模块处理上传的文档</li>
|
||||
<li>功能块处理上传的文档</li>
|
||||
<li>使用 GPT-4o 的代理执行技术分析</li>
|
||||
<li>使用 Claude 的代理分析情感和语气</li>
|
||||
<li>功能模块结合结果生成最终报告</li>
|
||||
<li>功能块结合结果生成最终报告</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
@@ -298,6 +272,6 @@ Agent 模块通过统一的推理接口支持多个 LLM 提供商。可用模型
|
||||
|
||||
## 最佳实践
|
||||
|
||||
- **在系统提示中具体说明**:清晰定义代理的角色、语气和限制。您的指令越具体,代理就越能更好地实现其预期目的。
|
||||
- **选择合适的温度设置**:当准确性很重要时,使用较低的温度设置(0-0.3);当需要更具创意或多样化的响应时,增加温度(0.7-2.0)。
|
||||
- **有效利用工具**:集成与代理目的互补并增强其能力的工具。选择性地提供工具,以避免让代理不堪重负。对于重叠较少的任务,使用另一个代理模块以获得最佳结果。
|
||||
- **在系统提示中具体说明**:清晰定义代理的角色、语气和限制。您的指令越具体,代理越能更好地完成其预期目的。
|
||||
- **选择合适的温度设置**:当准确性很重要时,使用较低的温度设置(0-0.3);当需要更具创意或多样化的响应时,可将温度提高(0.7-2.0)。
|
||||
- **有效利用工具**:集成与代理目的互补并增强其能力的工具。选择性地提供工具,以避免让代理不堪重负。对于重叠较少的任务,使用另一个代理块以获得最佳效果。
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
官方的 Python SDK 允许您通过 Python 应用程序以编程方式执行工作流。
|
||||
|
||||
<Callout type="info">
|
||||
Python SDK 支持 Python 3.8+,并提供同步工作流执行。目前所有工作流执行均为同步模式。
|
||||
Python SDK 支持 Python 3.8+,具备异步执行支持、自动速率限制(带指数退避)以及使用情况跟踪功能。
|
||||
</Callout>
|
||||
|
||||
## 安装
|
||||
@@ -72,10 +72,15 @@ result = client.execute_workflow(
|
||||
|
||||
**参数:**
|
||||
- `workflow_id` (str): 要执行的工作流 ID
|
||||
- `input_data` (dict, 可选): 传递给工作流的输入数据
|
||||
- `timeout` (float, 可选): 超时时间(以秒为单位,默认值:30.0)
|
||||
- `input_data` (dict, optional): 传递给工作流的输入数据
|
||||
- `timeout` (float, optional): 超时时间(以秒为单位,默认值:30.0)
|
||||
- `stream` (bool, optional): 启用流式响应(默认值:False)
|
||||
- `selected_outputs` (list[str], optional): 以 `blockName.attribute` 格式阻止输出流(例如,`["agent1.content"]`)
|
||||
- `async_execution` (bool, optional): 异步执行(默认值:False)
|
||||
|
||||
**返回值:** `WorkflowExecutionResult`
|
||||
**返回值:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
当 `async_execution=True` 时,立即返回任务 ID 以供轮询。否则,等待完成。
|
||||
|
||||
##### get_workflow_status()
|
||||
|
||||
@@ -103,32 +108,122 @@ if is_ready:
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `workflow_id` (str):工作流的 ID
|
||||
- `workflow_id` (str): 工作流的 ID
|
||||
|
||||
**返回值:** `bool`
|
||||
|
||||
##### execute_workflow_sync()
|
||||
##### get_job_status()
|
||||
|
||||
<Callout type="info">
|
||||
当前,此方法与 `execute_workflow()` 相同,因为所有执行都是同步的。提供此方法是为了在将来添加异步执行时保持兼容性。
|
||||
</Callout>
|
||||
|
||||
执行工作流(当前为同步,与 `execute_workflow()` 相同)。
|
||||
获取异步任务执行的状态。
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `task_id` (str): 异步执行返回的任务 ID
|
||||
|
||||
**返回值:** `Dict[str, Any]`
|
||||
|
||||
**响应字段:**
|
||||
- `success` (bool): 请求是否成功
|
||||
- `taskId` (str): 任务 ID
|
||||
- `status` (str): 可能的值包括 `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (dict): 包含 `startedAt`, `completedAt` 和 `duration`
|
||||
- `output` (any, optional): 工作流输出(完成时)
|
||||
- `error` (any, optional): 错误详情(失败时)
|
||||
- `estimatedDuration` (int, optional): 估计持续时间(以毫秒为单位,处理中/排队时)
|
||||
|
||||
##### execute_with_retry()
|
||||
|
||||
使用指数退避在速率限制错误上自动重试执行工作流。
|
||||
|
||||
```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
|
||||
)
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `workflow_id` (str):要执行的工作流 ID
|
||||
- `input_data` (dict, optional):传递给工作流的输入数据
|
||||
- `timeout` (float):初始请求的超时时间(以秒为单位)
|
||||
- `workflow_id` (str): 要执行的工作流 ID
|
||||
- `input_data` (dict, optional): 传递给工作流的输入数据
|
||||
- `timeout` (float, optional): 超时时间(以秒为单位)
|
||||
- `stream` (bool, optional): 启用流式响应
|
||||
- `selected_outputs` (list, optional): 阻止输出流
|
||||
- `async_execution` (bool, optional): 异步执行
|
||||
- `max_retries` (int, optional): 最大重试次数(默认值:3)
|
||||
- `initial_delay` (float, optional): 初始延迟时间(以秒为单位,默认值:1.0)
|
||||
- `max_delay` (float, optional): 最大延迟时间(以秒为单位,默认值:30.0)
|
||||
- `backoff_multiplier` (float, optional): 退避倍数(默认值:2.0)
|
||||
|
||||
**返回值:** `WorkflowExecutionResult`
|
||||
**返回值:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
重试逻辑使用指数退避(1 秒 → 2 秒 → 4 秒 → 8 秒...),并带有 ±25% 的抖动以防止惊群效应。如果 API 提供了 `retry-after` 标头,则会使用该标头。
|
||||
|
||||
##### get_rate_limit_info()
|
||||
|
||||
从上一次 API 响应中获取当前的速率限制信息。
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
**返回值:** `RateLimitInfo | None`
|
||||
|
||||
##### get_usage_limits()
|
||||
|
||||
获取您的账户当前的使用限制和配额信息。
|
||||
|
||||
```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"])
|
||||
```
|
||||
|
||||
**返回值:** `UsageLimits`
|
||||
|
||||
**响应结构:**
|
||||
|
||||
```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 +265,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 +288,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 +319,13 @@ class SimStudioError(Exception):
|
||||
self.status = status
|
||||
```
|
||||
|
||||
**常见错误代码:**
|
||||
- `UNAUTHORIZED`: 无效的 API 密钥
|
||||
- `TIMEOUT`: 请求超时
|
||||
- `RATE_LIMIT_EXCEEDED`: 超出速率限制
|
||||
- `USAGE_LIMIT_EXCEEDED`: 超出使用限制
|
||||
- `EXECUTION_ERROR`: 工作流执行失败
|
||||
|
||||
## 示例
|
||||
|
||||
### 基本工作流执行
|
||||
@@ -214,7 +349,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 +387,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 +419,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 +433,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."""
|
||||
@@ -339,6 +474,230 @@ for result in results:
|
||||
print(f"Workflow {result['workflow_id']}: {'Success' if result['success'] else 'Failed'}")
|
||||
```
|
||||
|
||||
### 异步工作流执行
|
||||
|
||||
为长时间运行的任务异步执行工作流:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### 速率限制与重试
|
||||
|
||||
通过指数退避自动处理速率限制:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### 使用监控
|
||||
|
||||
监控您的账户使用情况和限制:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### 流式工作流执行
|
||||
|
||||
通过实时流式响应执行工作流:
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
流式响应遵循服务器发送事件 (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]
|
||||
```
|
||||
|
||||
**Flask 流式示例:**
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
### 环境配置
|
||||
|
||||
使用环境变量配置客户端:
|
||||
@@ -352,8 +711,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 +724,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")
|
||||
)
|
||||
```
|
||||
|
||||
@@ -382,15 +741,15 @@ for result in results:
|
||||
|
||||
<Steps>
|
||||
<Step title="登录 Sim">
|
||||
访问 [Sim](https://sim.ai) 并登录您的账户。
|
||||
前往 [Sim](https://sim.ai) 并登录您的账户。
|
||||
</Step>
|
||||
<Step title="打开您的工作流">
|
||||
导航到您想要以编程方式执行的工作流。
|
||||
前往您想要以编程方式执行的工作流。
|
||||
</Step>
|
||||
<Step title="部署您的工作流">
|
||||
点击“部署”以部署您的工作流(如果尚未部署)。
|
||||
如果尚未部署,请点击“部署”以部署您的工作流。
|
||||
</Step>
|
||||
<Step title="创建或选择 API 密钥">
|
||||
<Step title="创建或选择一个 API 密钥">
|
||||
在部署过程中,选择或创建一个 API 密钥。
|
||||
</Step>
|
||||
<Step title="复制 API 密钥">
|
||||
|
||||
@@ -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'
|
||||
|
||||
Sim 的官方 TypeScript/JavaScript SDK 提供完整的类型安全,支持 Node.js 和浏览器环境,允许您从 Node.js 应用程序、Web 应用程序和其他 JavaScript 环境中以编程方式执行工作流。目前,所有工作流执行均为同步。
|
||||
Sim 的官方 TypeScript/JavaScript SDK 提供完整的类型安全,支持 Node.js 和浏览器环境,允许您从 Node.js 应用程序、Web 应用程序和其他 JavaScript 环境中以编程方式执行工作流。
|
||||
|
||||
<Callout type="info">
|
||||
TypeScript SDK 提供完整的类型安全,支持 Node.js 和浏览器环境。目前,所有工作流执行均为同步。
|
||||
TypeScript SDK 提供完整的类型安全、异步执行支持、带有指数回退的自动速率限制以及使用跟踪。
|
||||
</Callout>
|
||||
|
||||
## 安装
|
||||
@@ -91,12 +91,17 @@ const result = await client.executeWorkflow('workflow-id', {
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `workflowId`(字符串):要执行的工作流的 ID
|
||||
- `options`(ExecutionOptions,可选):
|
||||
- `input`(任意类型):传递给工作流的输入数据
|
||||
- `timeout`(数字):超时时间(以毫秒为单位,默认值:30000)
|
||||
- `workflowId` (字符串): 要执行的工作流的 ID
|
||||
- `options` (ExecutionOptions,可选):
|
||||
- `input` (任意类型): 传递给工作流的输入数据
|
||||
- `timeout` (数字): 超时时间(以毫秒为单位,默认值:30000)
|
||||
- `stream` (布尔值): 启用流式响应(默认值:false)
|
||||
- `selectedOutputs` (字符串数组): 以 `blockName.attribute` 格式阻止流中的输出(例如,`["agent1.content"]`)
|
||||
- `async` (布尔值): 异步执行(默认值:false)
|
||||
|
||||
**返回值:** `Promise<WorkflowExecutionResult>`
|
||||
**返回值:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
当 `async: true` 时,立即返回一个用于轮询的任务 ID。否则,等待完成。
|
||||
|
||||
##### getWorkflowStatus()
|
||||
|
||||
@@ -108,7 +113,7 @@ console.log('Is deployed:', status.isDeployed);
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `workflowId`(字符串):工作流的 ID
|
||||
- `workflowId` (字符串): 工作流的 ID
|
||||
|
||||
**返回值:** `Promise<WorkflowStatus>`
|
||||
|
||||
@@ -124,32 +129,121 @@ if (isReady) {
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `workflowId`(字符串):工作流的 ID
|
||||
- `workflowId` (字符串): 工作流的 ID
|
||||
|
||||
**返回值:** `Promise<boolean>`
|
||||
|
||||
##### executeWorkflowSync()
|
||||
##### getJobStatus()
|
||||
|
||||
<Callout type="info">
|
||||
当前,此方法与 `executeWorkflow()` 相同,因为所有执行都是同步的。提供此方法是为了在将来添加异步执行时保持兼容性。
|
||||
</Callout>
|
||||
|
||||
执行工作流(当前为同步,与 `executeWorkflow()` 相同)。
|
||||
获取异步任务执行的状态。
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `taskId` (字符串): 异步执行返回的任务 ID
|
||||
|
||||
**返回值:** `Promise<JobStatus>`
|
||||
|
||||
**响应字段:**
|
||||
- `success` (布尔值): 请求是否成功
|
||||
- `taskId` (字符串): 任务 ID
|
||||
- `status` (字符串): 可能的值包括 `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (对象): 包含 `startedAt`, `completedAt` 和 `duration`
|
||||
- `output` (任意类型,可选): 工作流输出(完成时)
|
||||
- `error` (任意类型,可选): 错误详情(失败时)
|
||||
- `estimatedDuration` (数字,可选): 估计持续时间(以毫秒为单位,处理中/排队时)
|
||||
|
||||
##### executeWithRetry()
|
||||
|
||||
使用指数退避机制,在遇到速率限制错误时自动重试执行工作流。
|
||||
|
||||
```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
|
||||
});
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `workflowId`(字符串):要执行的工作流的 ID
|
||||
- `options`(ExecutionOptions,可选):
|
||||
- `input`(任意类型):传递给工作流的输入数据
|
||||
- `timeout`(数字):初始请求的超时时间(以毫秒为单位)
|
||||
- `options`(ExecutionOptions,可选):与 `executeWorkflow()` 相同
|
||||
- `retryOptions`(RetryOptions,可选):
|
||||
- `maxRetries`(数字):最大重试次数(默认值:3)
|
||||
- `initialDelay`(数字):初始延迟时间(以毫秒为单位,默认值:1000)
|
||||
- `maxDelay`(数字):最大延迟时间(以毫秒为单位,默认值:30000)
|
||||
- `backoffMultiplier`(数字):退避倍数(默认值:2)
|
||||
|
||||
**返回值:** `Promise<WorkflowExecutionResult>`
|
||||
**返回值:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
重试逻辑使用指数退避(1秒 → 2秒 → 4秒 → 8秒...),并带有 ±25% 的抖动以防止蜂拥效应。如果 API 提供了 `retry-after` 头,则会使用该头。
|
||||
|
||||
##### getRateLimitInfo()
|
||||
|
||||
从上一次 API 响应中获取当前速率限制信息。
|
||||
|
||||
```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));
|
||||
}
|
||||
```
|
||||
|
||||
**返回值:** `RateLimitInfo | null`
|
||||
|
||||
##### getUsageLimits()
|
||||
|
||||
获取您的账户当前的使用限制和配额信息。
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
**返回值:** `Promise<UsageLimits>`
|
||||
|
||||
**响应结构:**
|
||||
|
||||
```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()
|
||||
|
||||
@@ -187,6 +281,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
|
||||
@@ -198,6 +306,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
|
||||
@@ -207,6 +354,13 @@ class SimStudioError extends Error {
|
||||
}
|
||||
```
|
||||
|
||||
**常见错误代码:**
|
||||
- `UNAUTHORIZED`: 无效的 API 密钥
|
||||
- `TIMEOUT`: 请求超时
|
||||
- `RATE_LIMIT_EXCEEDED`: 超出速率限制
|
||||
- `USAGE_LIMIT_EXCEEDED`: 超出使用限制
|
||||
- `EXECUTION_ERROR`: 工作流执行失败
|
||||
|
||||
## 示例
|
||||
|
||||
### 基本工作流执行
|
||||
@@ -230,7 +384,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 +425,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 +469,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 +487,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 +511,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 +553,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(
|
||||
@@ -469,14 +623,14 @@ document.getElementById('executeBtn')?.addEventListener('click', executeClientSi
|
||||
|
||||
### React Hook 示例
|
||||
|
||||
为工作流执行创建一个自定义 React Hook:
|
||||
为工作流执行创建自定义 React hook:
|
||||
|
||||
```typescript
|
||||
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 {
|
||||
@@ -532,7 +686,7 @@ function WorkflowComponent() {
|
||||
<button onClick={handleExecute} disabled={loading}>
|
||||
{loading ? 'Executing...' : 'Execute Workflow'}
|
||||
</button>
|
||||
|
||||
|
||||
{error && <div>Error: {error.message}</div>}
|
||||
{result && (
|
||||
<div>
|
||||
@@ -545,38 +699,267 @@ function WorkflowComponent() {
|
||||
}
|
||||
```
|
||||
|
||||
## 获取您的 API 密钥
|
||||
### 异步工作流执行
|
||||
|
||||
为长时间运行的任务异步执行工作流:
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
### 速率限制和重试
|
||||
|
||||
通过指数退避自动处理速率限制:
|
||||
|
||||
```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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用监控
|
||||
|
||||
监控您的账户使用情况和限制:
|
||||
|
||||
```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:
|
||||
|
||||
### 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 {
|
||||
// 为特定的块输出启用流式传输
|
||||
const result = await client.executeWorkflow('workflow-id', {
|
||||
input: { message: 'Count to five' },
|
||||
stream: true,
|
||||
selectedOutputs: ['agent1.content'] // 使用 blockName.attribute 格式
|
||||
});
|
||||
|
||||
console.log('工作流结果:', result);
|
||||
} catch (error) {
|
||||
console.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('');
|
||||
|
||||
// 重要提示:请从您的后端服务器发起此 API 调用,而不是从浏览器发起
|
||||
// 切勿在客户端代码中暴露您的 API 密钥
|
||||
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! // 仅限服务器端环境变量
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message: '生成一个故事',
|
||||
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('执行完成:', parsed.metadata);
|
||||
}
|
||||
} catch (e) {
|
||||
// 跳过无效的 JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={executeStreaming} disabled={loading}>
|
||||
{loading ? '生成中...' : '开始流式处理'}
|
||||
</button>
|
||||
<div style={{ whiteSpace: 'pre-wrap' }}>{output}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Your API Key
|
||||
|
||||
<Steps>
|
||||
<Step title="登录 Sim">
|
||||
访问 [Sim](https://sim.ai) 并登录您的账户。
|
||||
<Step title="Log in to Sim">
|
||||
Navigate to [Sim](https://sim.ai) and log in to your account.
|
||||
</Step>
|
||||
<Step title="打开您的工作流">
|
||||
导航到您想要以编程方式执行的工作流。
|
||||
<Step title="Open your workflow">
|
||||
Navigate to the workflow you want to execute programmatically.
|
||||
</Step>
|
||||
<Step title="部署您的工作流">
|
||||
如果尚未部署,请点击“部署”以部署您的工作流。
|
||||
<Step title="Deploy your workflow">
|
||||
Click on "Deploy" to deploy your workflow if it hasn't been deployed yet.
|
||||
</Step>
|
||||
<Step title="创建或选择一个 API 密钥">
|
||||
在部署过程中,选择或创建一个 API 密钥。
|
||||
<Step title="Create or select an API key">
|
||||
During the deployment process, select or create an API key.
|
||||
</Step>
|
||||
<Step title="复制 API 密钥">
|
||||
复制 API 密钥以在您的 TypeScript/JavaScript 应用程序中使用。
|
||||
<Step title="Copy the API key">
|
||||
Copy the API key to use in your TypeScript/JavaScript application.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning">
|
||||
请确保您的 API 密钥安全,切勿将其提交到版本控制中。使用环境变量或安全配置管理。
|
||||
Keep your API key secure and never commit it to version control. Use environment variables or secure configuration management.
|
||||
</Callout>
|
||||
|
||||
## 要求
|
||||
## Requirements
|
||||
|
||||
- Node.js 16+
|
||||
- TypeScript 5.0+(适用于 TypeScript 项目)
|
||||
- TypeScript 5.0+ (for TypeScript projects)
|
||||
|
||||
## TypeScript 支持
|
||||
## TypeScript Support
|
||||
|
||||
SDK 是用 TypeScript 编写的,并提供完整的类型安全:
|
||||
The SDK is written in TypeScript and provides full type safety:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -586,22 +969,22 @@ import {
|
||||
SimStudioError
|
||||
} from 'simstudio-ts-sdk';
|
||||
|
||||
// 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
|
||||
// 类型安全的工作流执行
|
||||
const result: WorkflowExecutionResult = await client.executeWorkflow('workflow-id', {
|
||||
input: {
|
||||
message: 'Hello, TypeScript!'
|
||||
message: '你好,TypeScript!'
|
||||
}
|
||||
});
|
||||
|
||||
// Type-safe status checking
|
||||
// 类型安全的状态检查
|
||||
const status: WorkflowStatus = await client.getWorkflowStatus('workflow-id');
|
||||
```
|
||||
|
||||
## 许可证
|
||||
|
||||
Apache-2.0
|
||||
Apache-2.0
|
||||
|
||||
@@ -24,15 +24,7 @@ API 触发器将您的工作流公开为一个安全的 HTTP 端点。将 JSON
|
||||
|
||||
为每个参数添加一个 **输入格式** 字段。运行时输出键会镜像该模式,并且也可以在 `<api.input>` 下使用。
|
||||
|
||||
```yaml
|
||||
- type: string
|
||||
name: userId
|
||||
value: demo-user # optional manual test value
|
||||
- type: number
|
||||
name: maxTokens
|
||||
```
|
||||
|
||||
编辑器中的手动运行使用 `value` 列,因此您可以在不发送请求的情况下进行测试。在执行期间,解析器会填充 `<api.userId>` 和 `<api.input.userId>`。
|
||||
在编辑器中手动运行使用 `value` 列,因此您可以在不发送请求的情况下进行测试。在执行过程中,解析器会填充 `<api.userId>` 和 `<api.input.userId>`。
|
||||
|
||||
## 请求示例
|
||||
|
||||
@@ -44,17 +36,95 @@ curl -X POST \
|
||||
-d '{"userId":"demo-user","maxTokens":1024}'
|
||||
```
|
||||
|
||||
成功的响应会返回来自执行器的序列化执行结果。错误会显示验证、身份验证或工作流失败的原因。
|
||||
成功的响应会返回来自执行器的序列化执行结果。错误会显示验证、认证或工作流失败的信息。
|
||||
|
||||
## 流式响应
|
||||
|
||||
启用实时流式传输以在生成时逐字符接收工作流输出。这对于向用户逐步显示 AI 响应非常有用。
|
||||
|
||||
### 请求参数
|
||||
|
||||
添加以下参数以启用流式传输:
|
||||
|
||||
- `stream` - 设置为 `true` 以启用服务器发送事件 (SSE) 流式传输
|
||||
- `selectedOutputs` - 要流式传输的块输出数组(例如,`["agent1.content"]`)
|
||||
|
||||
### 块输出格式
|
||||
|
||||
使用 `blockName.attribute` 格式指定要流式传输的块输出:
|
||||
- 格式:`"blockName.attribute"`(例如,如果您想流式传输 Agent 1 块的内容,可以使用 `"agent1.content"`)
|
||||
- 块名称不区分大小写,空格会被忽略
|
||||
|
||||
### 示例请求
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
### 响应格式
|
||||
|
||||
流式响应使用服务器发送事件 (SSE) 格式:
|
||||
|
||||
```
|
||||
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]
|
||||
```
|
||||
|
||||
每个事件包括:
|
||||
- **流式块**:`{"blockId": "...", "chunk": "text"}` - 实时生成的文本
|
||||
- **最终事件**:`{"event": "done", ...}` - 执行元数据和完整结果
|
||||
- **终止符**:`[DONE]` - 表示流结束
|
||||
|
||||
### 多块流式传输
|
||||
|
||||
当 `selectedOutputs` 包含多个块时,每个块会指示其来源:
|
||||
|
||||
```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"]
|
||||
}'
|
||||
```
|
||||
|
||||
每个块中的 `blockId` 字段可让您将输出路由到正确的 UI 元素:
|
||||
|
||||
```
|
||||
data: {"blockId":"agent1-uuid","chunk":"Processing..."}
|
||||
|
||||
data: {"blockId":"agent2-uuid","chunk":"Analyzing..."}
|
||||
|
||||
data: {"blockId":"agent1-uuid","chunk":" complete"}
|
||||
```
|
||||
|
||||
## 输出参考
|
||||
|
||||
| 参考 | 描述 |
|
||||
|-----------|-------------|
|
||||
| `<api.field>` | 在输入格式中定义的字段 |
|
||||
| `<api.field>` | 输入格式中定义的字段 |
|
||||
| `<api.input>` | 整个结构化请求体 |
|
||||
|
||||
如果未定义输入格式,执行器仅在 `<api.input>` 处公开原始 JSON。
|
||||
如果未定义输入格式,执行器仅在 `<api.input>` 处暴露原始 JSON。
|
||||
|
||||
<Callout type="warning">
|
||||
一个工作流只能包含一个 API 触发器。更改后请发布新的部署,以确保端点保持最新。
|
||||
一个工作流只能包含一个 API 触发器。更改后发布新部署,以确保端点保持最新。
|
||||
</Callout>
|
||||
|
||||
@@ -10,7 +10,6 @@ type: object
|
||||
required:
|
||||
- type
|
||||
- name
|
||||
- inputs
|
||||
- connections
|
||||
properties:
|
||||
type:
|
||||
@@ -22,21 +21,23 @@ properties:
|
||||
description: Display name for this loop block
|
||||
inputs:
|
||||
type: object
|
||||
required:
|
||||
- loopType
|
||||
description: Optional. If omitted, defaults will be applied.
|
||||
properties:
|
||||
loopType:
|
||||
type: string
|
||||
enum: [for, forEach]
|
||||
description: Type of loop to execute
|
||||
default: for
|
||||
iterations:
|
||||
type: number
|
||||
description: Number of iterations (for 'for' loops)
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
collection:
|
||||
type: string
|
||||
description: Collection to iterate over (for 'forEach' loops)
|
||||
default: ""
|
||||
maxConcurrency:
|
||||
type: number
|
||||
description: Maximum concurrent executions
|
||||
@@ -45,13 +46,10 @@ properties:
|
||||
maximum: 10
|
||||
connections:
|
||||
type: object
|
||||
required:
|
||||
- loop
|
||||
properties:
|
||||
# Nested format (recommended)
|
||||
loop:
|
||||
type: object
|
||||
required:
|
||||
- start
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
@@ -59,26 +57,37 @@ properties:
|
||||
end:
|
||||
type: string
|
||||
description: Target block ID for loop completion (optional)
|
||||
# Direct handle format (alternative)
|
||||
loop-start-source:
|
||||
type: string | string[]
|
||||
description: Target block ID to execute inside the loop (direct format)
|
||||
loop-end-source:
|
||||
type: string | string[]
|
||||
description: Target block ID for loop completion (direct format, optional)
|
||||
error:
|
||||
type: string
|
||||
description: Target block ID for error handling
|
||||
note: Use either the nested 'loop' format OR the direct 'loop-start-source' format, not both
|
||||
```
|
||||
|
||||
## 连接配置
|
||||
|
||||
Loop 块使用一种特殊的连接格式,其中包含一个 `loop` 部分:
|
||||
循环块支持两种连接格式:
|
||||
|
||||
### 直接句柄格式(替代方案)
|
||||
|
||||
```yaml
|
||||
connections:
|
||||
loop:
|
||||
start: <string> # Target block ID to execute inside the loop
|
||||
end: <string> # Target block ID after loop completion (optional)
|
||||
loop-start-source: <string> # Target block ID to execute inside the loop
|
||||
loop-end-source: <string> # Target block ID after loop completion (optional)
|
||||
error: <string> # Target block ID for error handling (optional)
|
||||
```
|
||||
|
||||
两种格式的功能完全相同,可根据您的喜好选择使用。
|
||||
|
||||
## 子块配置
|
||||
|
||||
循环中的块必须将其 `parentId` 设置为循环块的 ID:
|
||||
循环中的块必须将其 `parentId` 设置为循环块的 ID。`extent` 属性会自动设置为 `'parent'`,无需手动指定:
|
||||
|
||||
```yaml
|
||||
loop-1:
|
||||
@@ -261,9 +270,62 @@ process-task:
|
||||
success: task-completed
|
||||
```
|
||||
|
||||
### 直接句柄格式示例
|
||||
|
||||
同一个循环可以使用直接句柄格式编写:
|
||||
|
||||
```yaml
|
||||
my-loop:
|
||||
type: loop
|
||||
name: "Process Items"
|
||||
inputs:
|
||||
loopType: forEach
|
||||
collection: <start.items>
|
||||
connections:
|
||||
loop-start-source: process-item # Direct handle format
|
||||
loop-end-source: final-results # Direct handle format
|
||||
error: handle-error
|
||||
|
||||
process-item:
|
||||
type: agent
|
||||
name: "Process Item"
|
||||
parentId: my-loop
|
||||
inputs:
|
||||
systemPrompt: "Process this item"
|
||||
userPrompt: <loop.currentItem>
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
### 最小循环示例(使用默认值)
|
||||
|
||||
您可以完全省略 `inputs` 部分,系统将应用默认值:
|
||||
|
||||
```yaml
|
||||
simple-loop:
|
||||
type: loop
|
||||
name: "Simple Loop"
|
||||
# No inputs section - defaults to loopType: 'for', iterations: 5
|
||||
connections:
|
||||
loop-start-source: process-step
|
||||
loop-end-source: complete
|
||||
|
||||
process-step:
|
||||
type: agent
|
||||
name: "Process Step"
|
||||
parentId: simple-loop
|
||||
inputs:
|
||||
systemPrompt: "Execute step"
|
||||
userPrompt: "Step <loop.index>"
|
||||
model: gpt-4o
|
||||
apiKey: '{{OPENAI_API_KEY}}'
|
||||
```
|
||||
|
||||
此循环默认将执行 5 次迭代。
|
||||
|
||||
## 循环变量
|
||||
|
||||
在循环的子块中,可以使用以下特殊变量:
|
||||
在循环子块中,可以使用以下特殊变量:
|
||||
|
||||
```yaml
|
||||
# Available in all child blocks of the loop
|
||||
@@ -274,7 +336,7 @@ process-task:
|
||||
|
||||
## 输出引用
|
||||
|
||||
循环完成后,可以引用其聚合结果:
|
||||
循环完成后,您可以引用其聚合结果:
|
||||
|
||||
```yaml
|
||||
# In blocks after the loop
|
||||
@@ -286,10 +348,10 @@ final-processor:
|
||||
|
||||
## 最佳实践
|
||||
|
||||
- 设置合理的迭代限制以避免长时间的执行
|
||||
- 使用 forEach 处理集合,使用 for 循环处理固定迭代
|
||||
- 对于 I/O 密集型操作,考虑使用 maxConcurrency
|
||||
- 设置合理的迭代限制以避免长时间执行
|
||||
- 使用 forEach 处理集合,使用 for 循环进行固定迭代
|
||||
- 考虑对 I/O 密集型操作使用 maxConcurrency
|
||||
- 包含错误处理以确保循环执行的健壮性
|
||||
- 为循环子块使用描述性名称
|
||||
- 先用小集合进行测试
|
||||
- 对大集合的执行时间进行监控
|
||||
- 对于大集合,监控执行时间
|
||||
@@ -2231,8 +2231,8 @@ checksums:
|
||||
d394ac42b56429e524dc5a771b0610b9:
|
||||
meta/title: 9da9098244c6c7a0ebcc3009cef66c7b
|
||||
content/0: 9218a2e190598690d0fc5c27c30f01bb
|
||||
content/1: 8a3feb937915c3191f1eecb10b94297d
|
||||
content/2: 99af1bfe8d1629acdb5a9229430af791
|
||||
content/1: 6c88f52bdb4e4a5668d1b25b5f444f48
|
||||
content/2: 7e827833339b6b4c6abdf154de7f9a0c
|
||||
content/3: 391128dee61b5d0d43eba88567aaef42
|
||||
content/4: 4d132e6346723ecf45c408afeab2757b
|
||||
content/5: d3df764a69d2926d10aed65ad8693e9f
|
||||
@@ -2254,79 +2254,155 @@ checksums:
|
||||
content/21: bd0e851fdde30c0e94c00b60f85d655e
|
||||
content/22: 837ca74ccf63f23333c54e010faf681c
|
||||
content/23: 8fb33cfc314b86d35df8ea1b10466f20
|
||||
content/24: 09e003c28fb1810e9afefe51324265fd
|
||||
content/25: 07fb2d6b16c75839a32d383f12419ca5
|
||||
content/26: 9fd0cd99a879360d355d91e9cfb41531
|
||||
content/27: f6fed8ebf67ba12199b4474a754969ae
|
||||
content/28: bcee3febe1be079e53aea841e2b08b3b
|
||||
content/29: f00be560fcd4ff3f53d61c70c249597b
|
||||
content/30: fa4fa1573c369fcc2eee57d7852caf9c
|
||||
content/31: fa68c1f8c9ea3dba96b2ea7edb8680d7
|
||||
content/32: 304e608d459ef53f308e6ea1f6f8b54a
|
||||
content/33: cb63e267fb16a7aaeea45c4ca29bf697
|
||||
content/34: f00be560fcd4ff3f53d61c70c249597b
|
||||
content/35: f7c266db4d07d040f8f788be598476cf
|
||||
content/36: cd306281b5136831335e6376edb1e822
|
||||
content/37: afb7b7f27d48deb3154da26135e17fb8
|
||||
content/38: 179000198c9cd78601b5c862e9c8659f
|
||||
content/39: 28c37db52f39323e125fcaf0e60911db
|
||||
content/40: 8de5041c3c93b70619ec1723f657757f
|
||||
content/41: 07fb2d6b16c75839a32d383f12419ca5
|
||||
content/42: 65db7855a79ab283c6409e81a7703d19
|
||||
content/43: 191fb7087315702a36001c69d745ebed
|
||||
content/44: f6113edfd7a0062af4d88bcf31a73f45
|
||||
content/45: 1e84fc1eee794c20e3411b3a34a02278
|
||||
content/46: ec31e300f79185f734d32b1cfaf8a137
|
||||
content/47: f7ad301d02e8826921644a5268f13f32
|
||||
content/48: 025d60fdaf93713ccb34abcbc71dfa2b
|
||||
content/49: 70a9ece41fdad09f3a06ca0efdb92ae9
|
||||
content/50: 356d67409ae0d82a72d052573314f660
|
||||
content/51: bb172f1678686d9d49666c516716de24
|
||||
content/52: 529711647eccfdf031dbb5bc70581986
|
||||
content/53: 9a84c92505eb468916637fcf2cef70f2
|
||||
content/54: 225bca3fb37bd38cd645e8a698abbfa9
|
||||
content/55: 33b9b1e9744318597da4b925b0995be2
|
||||
content/56: 6afe3b62e6d53c3dcd07149abcab4c05
|
||||
content/57: b6363faee219321c16d41a9c3f8d3bdd
|
||||
content/58: 24ef65dd034a2881a978d8d0065fb258
|
||||
content/59: b8b23ab79a7eb32c6f8d5f49f43c51f6
|
||||
content/60: be358297e2bbb9ab4689d11d072611d1
|
||||
content/61: b2eaadc86870d2e64b55c89e0348ef93
|
||||
content/62: 450265802cb0ba5b435b74b9cac1bf23
|
||||
content/63: b735ede8764e4b2dfb25967e33ab5143
|
||||
content/64: 0f881e586a03c4b916456c73fad48358
|
||||
content/65: 62bbbeca4e0500062f5cdbbc1614dde0
|
||||
content/66: 55d47e12745c1b0b62c9bdf6e8449730
|
||||
content/67: 1d873c7ccd87f564e2b30387b40ee9e9
|
||||
content/68: 3304a33dfb626c6e2267c062e8956a9d
|
||||
content/69: 77256b36307e9f7293bd00063239c8ee
|
||||
content/70: ac686382ccbb07d75b0f141af500dfd5
|
||||
content/71: 38f7308105b0843792c8e2fb93e1895d
|
||||
content/72: 62f6977928b2f596ed7d54383d1e779d
|
||||
content/73: 3415d6c5ad1df56b212d69519bdf0fea
|
||||
content/74: d1a104e667cd2284ab5b3fead4a6ba1d
|
||||
content/75: a81d7cd4a644a0061dad3a5973b4fe06
|
||||
content/76: 981447969a71fd038049e9d9f40f4f8c
|
||||
content/77: 531941216d31cb1947367c3c02127baa
|
||||
content/78: bf1afa789fdfa5815faaf43574341e90
|
||||
content/79: 5f2fe55d098d4e4f438af595708b2280
|
||||
content/80: 41b8f7cf8899a0e92e255a3f845f9584
|
||||
content/81: 5040bab65fb6bb77862f8098d16afbb5
|
||||
content/82: a88260a5b5e23da73e4534376adeb193
|
||||
content/83: e5e2329cdc226186fe9d44767528a4a0
|
||||
content/84: 1773624e9ac3d5132b505894ef51977e
|
||||
content/85: d62c9575cc66feec7589fba95c9f7aee
|
||||
content/86: 7af652c5407ae7e156ab27b21a4f26d3
|
||||
content/87: 4aa69b29cca745389dea8cd74eba4f83
|
||||
content/88: 46877074b69519165997fa0968169611
|
||||
content/89: d8ebc69b18baf83689ba315e7b4946ea
|
||||
content/90: ecd571818ddf3d31b08b80a25958a662
|
||||
content/91: 7dcdf2fbf3fce3f94987046506e12a9b
|
||||
content/24: f8fbd9375113651be0f2498bdacde0ef
|
||||
content/25: 2c57d87589b65f785e0fbbda60d32e54
|
||||
content/26: 2541eb37fca67a6d7c5a10f8067127a3
|
||||
content/27: 9fd0cd99a879360d355d91e9cfb41531
|
||||
content/28: f6fed8ebf67ba12199b4474a754969ae
|
||||
content/29: bcee3febe1be079e53aea841e2b08b3b
|
||||
content/30: f00be560fcd4ff3f53d61c70c249597b
|
||||
content/31: fa4fa1573c369fcc2eee57d7852caf9c
|
||||
content/32: fa68c1f8c9ea3dba96b2ea7edb8680d7
|
||||
content/33: 304e608d459ef53f308e6ea1f6f8b54a
|
||||
content/34: cb63e267fb16a7aaeea45c4ca29bf697
|
||||
content/35: f00be560fcd4ff3f53d61c70c249597b
|
||||
content/36: f7c266db4d07d040f8f788be598476cf
|
||||
content/37: d93b320646fde160c0fdd1936ee63cfb
|
||||
content/38: c76e2089a41880dd6feac759ec8867c2
|
||||
content/39: 0d61b9631788e64d1c1335b08c907107
|
||||
content/40: 5ec50e6f56bd0a9a55fae14fa02185d9
|
||||
content/41: 47bdc3ba4908bf1ce3d1a0a8f646b339
|
||||
content/42: 5e8af7125448a6021a6ea431486dd587
|
||||
content/43: 15017685691db74889cc6116373e44a5
|
||||
content/44: 4d4ad5d56e800e5d227a07339300fc7f
|
||||
content/45: c035728b4b81d006a18ba9ba7b9c638d
|
||||
content/46: f1c9ad60574d19a5f93c837ab9d88890
|
||||
content/47: 2c57d87589b65f785e0fbbda60d32e54
|
||||
content/48: e7019a0e12f7295893c5822356fc0df0
|
||||
content/49: 5912d8d9df5bbe435579d8eb0677685c
|
||||
content/50: 4e1da4edce56837c750ce8da4c0e6cf2
|
||||
content/51: 3d35097bb958e6eddd6976aeb1fe9e41
|
||||
content/52: 78dce98d48ba070dbe100ee2a94cb17d
|
||||
content/53: 38ec85acf292485e3dd837a29208fd2c
|
||||
content/54: 58d582d90c8715f5570f76fed2be508d
|
||||
content/55: 7d2b7134d447172c502b5f40fc3b38e6
|
||||
content/56: 4a71171863d7329da6813b94772c0d4e
|
||||
content/57: 1900d5b89dbca22d7a455bdc3367f0f5
|
||||
content/58: 45126feb4fc831922a7edabfa2d54e4a
|
||||
content/59: 65db7855a79ab283c6409e81a7703d19
|
||||
content/60: 191fb7087315702a36001c69d745ebed
|
||||
content/61: f6113edfd7a0062af4d88bcf31a73f45
|
||||
content/62: 1e84fc1eee794c20e3411b3a34a02278
|
||||
content/63: ec31e300f79185f734d32b1cfaf8a137
|
||||
content/64: f7ad301d02e8826921644a5268f13f32
|
||||
content/65: 025d60fdaf93713ccb34abcbc71dfa2b
|
||||
content/66: 70a9ece41fdad09f3a06ca0efdb92ae9
|
||||
content/67: 356d67409ae0d82a72d052573314f660
|
||||
content/68: 5a80933fb21deea17a0a200564f0111b
|
||||
content/69: 9527ba2ab5ddd8001baaaaf25f1a7acc
|
||||
content/70: bb172f1678686d9d49666c516716de24
|
||||
content/71: 529711647eccfdf031dbb5bc70581986
|
||||
content/72: baa408b1603f35a8e24dd60b88773c72
|
||||
content/73: c42a9f19d0678d8d1a36cf1f93e4a5ba
|
||||
content/74: f6180f2341e8a7ae24afb05d7a185340
|
||||
content/75: 8196e101e443ec2aac13cefd90a6d454
|
||||
content/76: 9a84c92505eb468916637fcf2cef70f2
|
||||
content/77: 225bca3fb37bd38cd645e8a698abbfa9
|
||||
content/78: 7431c09b430effd69de843ee0fbaafe8
|
||||
content/79: 33b9b1e9744318597da4b925b0995be2
|
||||
content/80: 6afe3b62e6d53c3dcd07149abcab4c05
|
||||
content/81: b6363faee219321c16d41a9c3f8d3bdd
|
||||
content/82: 2449c8e8f55e2bf3f732527352d35c9f
|
||||
content/83: b8b23ab79a7eb32c6f8d5f49f43c51f6
|
||||
content/84: be358297e2bbb9ab4689d11d072611d1
|
||||
content/85: eb774a8a86d778153905b0f6cdcdf517
|
||||
content/86: 450265802cb0ba5b435b74b9cac1bf23
|
||||
content/87: b735ede8764e4b2dfb25967e33ab5143
|
||||
content/88: 0f881e586a03c4b916456c73fad48358
|
||||
content/89: f51639ab2b7ccac72b850e2064e694e9
|
||||
content/90: 55d47e12745c1b0b62c9bdf6e8449730
|
||||
content/91: e6223d6aa9efa444282e58d7d9a99ced
|
||||
content/92: 3304a33dfb626c6e2267c062e8956a9d
|
||||
content/93: 77256b36307e9f7293bd00063239c8ee
|
||||
content/94: ac686382ccbb07d75b0f141af500dfd5
|
||||
content/95: 5610b6538a29672335b572d6f35d0657
|
||||
content/96: 62f6977928b2f596ed7d54383d1e779d
|
||||
content/97: 3415d6c5ad1df56b212d69519bdf0fea
|
||||
content/98: 6bd60468d8cc072c5fe4214481fa9f60
|
||||
content/99: a81d7cd4a644a0061dad3a5973b4fe06
|
||||
content/100: 981447969a71fd038049e9d9f40f4f8c
|
||||
content/101: 531941216d31cb1947367c3c02127baa
|
||||
content/102: bf1afa789fdfa5815faaf43574341e90
|
||||
content/103: 5f2fe55d098d4e4f438af595708b2280
|
||||
content/104: 41b8f7cf8899a0e92e255a3f845f9584
|
||||
content/105: 61ddd890032078ffd2da931b1d153b6d
|
||||
content/106: 7873aa7487bc3e8a4826d65c1760a4a0
|
||||
content/107: 98182d9aabe14d5bad43a5ee76a75eab
|
||||
content/108: 2bdb01e4bcb08b1d99f192acf8e2fba7
|
||||
content/109: 7079d9c00b1e1882c329b7e9b8f74552
|
||||
content/110: 0f9d65eaf6e8de43c3d5fa7e62bc838d
|
||||
content/111: 58c8e9d2d0ac37efd958203b8fbc8193
|
||||
content/112: 7859d36a7a6d0122c0818b28ee29aa3e
|
||||
content/113: ce185e7b041b8f95ebc11370d3e0aad9
|
||||
content/114: 701e9bf4fd4d0669da0584eac5bd96e0
|
||||
content/115: d1bab8ec5a51a9da5464eb47e2a16b50
|
||||
content/116: da658275cc81a20f9cf7e4c66c7af1e3
|
||||
content/117: 377d7c99a5df4b72166946573f7210b8
|
||||
content/118: 3afc03a5ab1dc9db2bfa092b0ac4826a
|
||||
content/119: 18ddfcaf2be4a6f1d9819407dad9ce7c
|
||||
content/120: 2f6263b2e95f09f7e4842453f4bf4a0a
|
||||
content/121: 4603578d6b314b662f45564a34ca430d
|
||||
content/122: cf4c97eb254d0bd6ea6633344621c2c2
|
||||
content/123: 7b4640989fab002039936156f857eb21
|
||||
content/124: 65ca9f08745b47b4cce8ea8247d043bf
|
||||
content/125: 162b4180611ff0a53b782e4dc8109293
|
||||
content/126: 6b367a189eb53cb198e3666023def89c
|
||||
content/127: dbb2125cefcf618849600c1eccae8a64
|
||||
content/128: 04eedda0da3767b06e6017c559e05414
|
||||
content/129: 661688450606eb09d8faee1468e88331
|
||||
content/130: 8ff8367c3246103b3e3e02499e34ae0b
|
||||
content/131: 44678bda9166f746da1d61b694ced482
|
||||
content/132: a5e75db27c0a901f4cacf6598f450e6c
|
||||
content/133: d1bab8ec5a51a9da5464eb47e2a16b50
|
||||
content/134: da658275cc81a20f9cf7e4c66c7af1e3
|
||||
content/135: 377d7c99a5df4b72166946573f7210b8
|
||||
content/136: 3afc03a5ab1dc9db2bfa092b0ac4826a
|
||||
content/137: 18ddfcaf2be4a6f1d9819407dad9ce7c
|
||||
content/138: 2f6263b2e95f09f7e4842453f4bf4a0a
|
||||
content/139: 4603578d6b314b662f45564a34ca430d
|
||||
content/140: cf4c97eb254d0bd6ea6633344621c2c2
|
||||
content/141: 7b4640989fab002039936156f857eb21
|
||||
content/142: 65ca9f08745b47b4cce8ea8247d043bf
|
||||
content/143: 162b4180611ff0a53b782e4dc8109293
|
||||
content/144: 6b367a189eb53cb198e3666023def89c
|
||||
content/145: dbb2125cefcf618849600c1eccae8a64
|
||||
content/146: 04eedda0da3767b06e6017c559e05414
|
||||
content/147: 661688450606eb09d8faee1468e88331
|
||||
content/148: 8ff8367c3246103b3e3e02499e34ae0b
|
||||
content/149: 44678bda9166f746da1d61b694ced482
|
||||
content/150: 192a89879084dd7a74a6f44bcecae958
|
||||
content/151: 41c2bb95317d7c0421817a2b1a68cc09
|
||||
content/152: 4c95f9fa55f698f220577380dff95011
|
||||
content/153: 9ef273d776aada1b2cff3452f08ff985
|
||||
content/154: 100e12673551d4ceb5b906b1b9c65059
|
||||
content/155: ce253674cd7c49320203cda2bdd3685b
|
||||
content/156: 8910afcea8c205a28256eb30de6a1f26
|
||||
content/157: 4d7ad757d2c70fdff7834146d38dddd8
|
||||
content/158: a88260a5b5e23da73e4534376adeb193
|
||||
content/159: e5e2329cdc226186fe9d44767528a4a0
|
||||
content/160: 1773624e9ac3d5132b505894ef51977e
|
||||
content/161: d62c9575cc66feec7589fba95c9f7aee
|
||||
content/162: 7af652c5407ae7e156ab27b21a4f26d3
|
||||
content/163: 4aa69b29cca745389dea8cd74eba4f83
|
||||
content/164: 46877074b69519165997fa0968169611
|
||||
content/165: 2e81908c18033109ac82a054b3fafd3d
|
||||
content/166: ecd571818ddf3d31b08b80a25958a662
|
||||
content/167: 7dcdf2fbf3fce3f94987046506e12a9b
|
||||
27578f1315b6f1b7418d5e0d6042722e:
|
||||
meta/title: 8c555594662512e95f28e20d3880f186
|
||||
content/0: 9218a2e190598690d0fc5c27c30f01bb
|
||||
content/1: feca29d7cbb17f461bc8706f142cb475
|
||||
content/2: 65705e1bef9ddf2674454c20e77af61f
|
||||
content/2: 9cb58e08402fc80050ad6a62cae3f643
|
||||
content/3: 391128dee61b5d0d43eba88567aaef42
|
||||
content/4: fa77bab0a8660a7999bf3104921aac5c
|
||||
content/5: e8839cfb872185cea76973caaa7f84e0
|
||||
@@ -2342,67 +2418,107 @@ checksums:
|
||||
content/15: 64005abb7b5c1c3edef8970a8a7d17b2
|
||||
content/16: 837ca74ccf63f23333c54e010faf681c
|
||||
content/17: 626054376e08522e7195a60c34db9af8
|
||||
content/18: 03c715df3c784e92ce1c0ce6a4dcd2e3
|
||||
content/19: dcb92b9a1f222393f2e81cdae239885c
|
||||
content/20: 2f5c7e73763a1884893739283f0d0659
|
||||
content/21: f6fed8ebf67ba12199b4474a754969ae
|
||||
content/22: c8f9a1d43885f2b9fe8b64c79d8af8b8
|
||||
content/23: e1a2ca39583549a731d942082e1fa07c
|
||||
content/24: 14e077bdb64d87457870efa215384654
|
||||
content/25: c2e86eaf4b7d1cd53ed8172264337cc9
|
||||
content/26: 304e608d459ef53f308e6ea1f6f8b54a
|
||||
content/27: 9d04294f8385211535ed7622d164871f
|
||||
content/28: e1a2ca39583549a731d942082e1fa07c
|
||||
content/29: 279c20e11af33abb94993e8ea3e80669
|
||||
content/30: eec7d8395f8cf305106deb7b25384ecf
|
||||
content/31: 921824b44c391f8a0cdc5ce4cd283e77
|
||||
content/32: d5aaccb9399a1255f986b703921594e5
|
||||
content/33: dba855cc28255e4576026e3da0cdf05b
|
||||
content/34: 17fdd93c6df75b108e352a62a195bc73
|
||||
content/35: dcb92b9a1f222393f2e81cdae239885c
|
||||
content/36: fb6fddfdf4753a36c7878ef60b345822
|
||||
content/37: 191fb7087315702a36001c69d745ebed
|
||||
content/38: 1ffef0a4e0d6a6bbca85776c113e1164
|
||||
content/39: 61caafaf79e863df9525c4baf72c14e1
|
||||
content/40: ec31e300f79185f734d32b1cfaf8a137
|
||||
content/41: 65a172d64ffca3b03c6e0ed08f0bd821
|
||||
content/42: 2db387754d7fb3539bcb986dfaac1c8c
|
||||
content/43: e118d997ba48a5230ec70a564d436860
|
||||
content/44: 77268362a748dafad471f31acfd230dc
|
||||
content/45: b55b3773df2dfba66b6e675db7e2470e
|
||||
content/46: 70a9ece41fdad09f3a06ca0efdb92ae9
|
||||
content/47: 646ee615d86faf3b6a8da03115a30efa
|
||||
content/48: bb172f1678686d9d49666c516716de24
|
||||
content/49: a025b3b746d72e0f676f58703ee19a47
|
||||
content/50: 9a84c92505eb468916637fcf2cef70f2
|
||||
content/51: a4c78d85ed9be63b07b657166510f440
|
||||
content/52: 33b9b1e9744318597da4b925b0995be2
|
||||
content/53: 6afe3b62e6d53c3dcd07149abcab4c05
|
||||
content/54: b6363faee219321c16d41a9c3f8d3bdd
|
||||
content/55: f939bc99e05d04e1d52bf4b9ec3f1825
|
||||
content/56: b8b23ab79a7eb32c6f8d5f49f43c51f6
|
||||
content/57: be358297e2bbb9ab4689d11d072611d1
|
||||
content/58: d8fcefba15a99bf4a9cf71c985097677
|
||||
content/59: 7d098f0349c782f389431377ee512e92
|
||||
content/60: 22b39537f6a104803389469d211154e4
|
||||
content/61: 5dc147f9fe5e8117dfa6c94808c4ff54
|
||||
content/62: f29d6bfd74ba3fee0b90180f620b4f47
|
||||
content/63: 2a59466500b62e57481fe27692a3ed0f
|
||||
content/64: d3ac9ea2a213cafb1f871dda8f6e6fe0
|
||||
content/65: 450265802cb0ba5b435b74b9cac1bf23
|
||||
content/66: b735ede8764e4b2dfb25967e33ab5143
|
||||
content/67: 0f881e586a03c4b916456c73fad48358
|
||||
content/68: 3f643fb43f3a022a449ded1e7c4db8bf
|
||||
content/69: 55d47e12745c1b0b62c9bdf6e8449730
|
||||
content/70: 166b3975e39841707381880ae4df3984
|
||||
content/71: 3304a33dfb626c6e2267c062e8956a9d
|
||||
content/72: a88260a5b5e23da73e4534376adeb193
|
||||
content/73: cc31ae653c5642b223ec634888de29c6
|
||||
content/74: 1773624e9ac3d5132b505894ef51977e
|
||||
content/75: d62c9575cc66feec7589fba95c9f7aee
|
||||
content/76: 8df5939abc771b5d24c115ef20d42d6f
|
||||
content/77: ecd571818ddf3d31b08b80a25958a662
|
||||
content/78: 7dcdf2fbf3fce3f94987046506e12a9b
|
||||
content/18: 12153919e0229ac0a3699de043eae2a2
|
||||
content/19: 59ceca96004d0746448717245eb65c5c
|
||||
content/20: a0ff152e09498effe90572fe5cdfad1b
|
||||
content/21: 2f5c7e73763a1884893739283f0d0659
|
||||
content/22: f6fed8ebf67ba12199b4474a754969ae
|
||||
content/23: c8f9a1d43885f2b9fe8b64c79d8af8b8
|
||||
content/24: e1a2ca39583549a731d942082e1fa07c
|
||||
content/25: 14e077bdb64d87457870efa215384654
|
||||
content/26: c2e86eaf4b7d1cd53ed8172264337cc9
|
||||
content/27: 304e608d459ef53f308e6ea1f6f8b54a
|
||||
content/28: 9d04294f8385211535ed7622d164871f
|
||||
content/29: e1a2ca39583549a731d942082e1fa07c
|
||||
content/30: 279c20e11af33abb94993e8ea3e80669
|
||||
content/31: 9e772c161a4b008c2f1db15a967d07ab
|
||||
content/32: c76e2089a41880dd6feac759ec8867c2
|
||||
content/33: 5d9a7b1e681cbe8f02def7eefabb0ac5
|
||||
content/34: b4e0e90d40a60a024f64f80b193dcb48
|
||||
content/35: b9f46c03c91c1070dd3ca0eba461f29b
|
||||
content/36: fbecf63d14b56039ba44471f7a8afd4a
|
||||
content/37: 58701f4ec097582ee105714a9363ccbe
|
||||
content/38: 4d4ad5d56e800e5d227a07339300fc7f
|
||||
content/39: 7f2a42a752279d7871064a21d0891b73
|
||||
content/40: 8462e2271506b0545c62e5f70865a2f4
|
||||
content/41: 59ceca96004d0746448717245eb65c5c
|
||||
content/42: e7019a0e12f7295893c5822356fc0df0
|
||||
content/43: 29d376146cd1149025028c61eb33e7ab
|
||||
content/44: 4e1da4edce56837c750ce8da4c0e6cf2
|
||||
content/45: 666a62d9fd54735b2adcad6277b3e07f
|
||||
content/46: db012cfc3749d025f1dd40b5db1d9d63
|
||||
content/47: 478fe7c3fbdd5e7d779691c9a09795c9
|
||||
content/48: 58d582d90c8715f5570f76fed2be508d
|
||||
content/49: 710baf5cf18c21cc284e70df97b36f40
|
||||
content/50: 6363bbb118f3f51ca1b1acf3e9ec2f7c
|
||||
content/51: 1900d5b89dbca22d7a455bdc3367f0f5
|
||||
content/52: 959f29f44825109bf4bb16129896a8dd
|
||||
content/53: fb6fddfdf4753a36c7878ef60b345822
|
||||
content/54: 191fb7087315702a36001c69d745ebed
|
||||
content/55: 1ffef0a4e0d6a6bbca85776c113e1164
|
||||
content/56: 61caafaf79e863df9525c4baf72c14e1
|
||||
content/57: ec31e300f79185f734d32b1cfaf8a137
|
||||
content/58: 65a172d64ffca3b03c6e0ed08f0bd821
|
||||
content/59: 2db387754d7fb3539bcb986dfaac1c8c
|
||||
content/60: e118d997ba48a5230ec70a564d436860
|
||||
content/61: 77268362a748dafad471f31acfd230dc
|
||||
content/62: b55b3773df2dfba66b6e675db7e2470e
|
||||
content/63: 70a9ece41fdad09f3a06ca0efdb92ae9
|
||||
content/64: 646ee615d86faf3b6a8da03115a30efa
|
||||
content/65: 5a80933fb21deea17a0a200564f0111b
|
||||
content/66: a82d5e5fad0fbfd60ca97e5312d11941
|
||||
content/67: bb172f1678686d9d49666c516716de24
|
||||
content/68: a025b3b746d72e0f676f58703ee19a47
|
||||
content/69: baa408b1603f35a8e24dd60b88773c72
|
||||
content/70: c0cc113d0001826984f9c096c79cd18b
|
||||
content/71: f6180f2341e8a7ae24afb05d7a185340
|
||||
content/72: 3d414a5669f152cd296af27b61104858
|
||||
content/73: 9a84c92505eb468916637fcf2cef70f2
|
||||
content/74: a4c78d85ed9be63b07b657166510f440
|
||||
content/75: 7431c09b430effd69de843ee0fbaafe8
|
||||
content/76: 33b9b1e9744318597da4b925b0995be2
|
||||
content/77: 6afe3b62e6d53c3dcd07149abcab4c05
|
||||
content/78: b6363faee219321c16d41a9c3f8d3bdd
|
||||
content/79: 08410ce9f0ec358b3c7230a56bc66399
|
||||
content/80: b8b23ab79a7eb32c6f8d5f49f43c51f6
|
||||
content/81: be358297e2bbb9ab4689d11d072611d1
|
||||
content/82: 09fea7c0d742a0eefa77e982e848de6c
|
||||
content/83: 7d098f0349c782f389431377ee512e92
|
||||
content/84: 22b39537f6a104803389469d211154e4
|
||||
content/85: d9ec74ab28b264d76f797fdae7c8f3d3
|
||||
content/86: f29d6bfd74ba3fee0b90180f620b4f47
|
||||
content/87: 2a59466500b62e57481fe27692a3ed0f
|
||||
content/88: cbbb123fc3a12bf2ab72dc1bbe373a6e
|
||||
content/89: 7873aa7487bc3e8a4826d65c1760a4a0
|
||||
content/90: 98182d9aabe14d5bad43a5ee76a75eab
|
||||
content/91: 67bfa8ae3e22d9a949f08c79a40b8df5
|
||||
content/92: 7079d9c00b1e1882c329b7e9b8f74552
|
||||
content/93: 0f9d65eaf6e8de43c3d5fa7e62bc838d
|
||||
content/94: bcf0ce93a4493586ad32c20d9d2b285c
|
||||
content/95: 7859d36a7a6d0122c0818b28ee29aa3e
|
||||
content/96: ce185e7b041b8f95ebc11370d3e0aad9
|
||||
content/97: dae96b41f0c029b464f02ac65d3c5796
|
||||
content/98: 41c2bb95317d7c0421817a2b1a68cc09
|
||||
content/99: 4c95f9fa55f698f220577380dff95011
|
||||
content/100: 6695bd47a05f9963134d8a71abb3d298
|
||||
content/101: 100e12673551d4ceb5b906b1b9c65059
|
||||
content/102: ce253674cd7c49320203cda2bdd3685b
|
||||
content/103: 94d4346a735149c2a83f6d2a21b8ab4c
|
||||
content/104: 3ee4b16b8204ef3b5b7c0322ff636fab
|
||||
content/105: 450265802cb0ba5b435b74b9cac1bf23
|
||||
content/106: b735ede8764e4b2dfb25967e33ab5143
|
||||
content/107: 0f881e586a03c4b916456c73fad48358
|
||||
content/108: 4570af52d41ecda8d91e6bbe2bc19891
|
||||
content/109: 55d47e12745c1b0b62c9bdf6e8449730
|
||||
content/110: 82507d357ec8766f0173b9b1081c4c56
|
||||
content/111: 3304a33dfb626c6e2267c062e8956a9d
|
||||
content/112: a88260a5b5e23da73e4534376adeb193
|
||||
content/113: cc31ae653c5642b223ec634888de29c6
|
||||
content/114: 1773624e9ac3d5132b505894ef51977e
|
||||
content/115: d62c9575cc66feec7589fba95c9f7aee
|
||||
content/116: 8df5939abc771b5d24c115ef20d42d6f
|
||||
content/117: ecd571818ddf3d31b08b80a25958a662
|
||||
content/118: 7dcdf2fbf3fce3f94987046506e12a9b
|
||||
004fe5dc5ca33719cb175f3619fe5208:
|
||||
meta/title: be754b00d8a2c13c561e314f6f526515
|
||||
content/0: 7e581dbf3e581d503ac94f7fb7938b1f
|
||||
@@ -3378,19 +3494,18 @@ checksums:
|
||||
content/38: 1b693e51b5b8e31dd088c602663daab4
|
||||
content/39: 5003cde407a705d39b969eff5bdab18a
|
||||
content/40: 624199f0ed2378588024cfe6055d6b6b
|
||||
content/41: 8accff30f8b36f6fc562b44d8fe271dd
|
||||
content/42: fc62aefa5b726f57f2c9a17a276e601f
|
||||
content/43: d72903dda50a36b12ec050a06ef23a1b
|
||||
content/44: 19984dc55d279f1ae3226edf4b62aaa3
|
||||
content/45: 9c2f91f89a914bf4661512275e461104
|
||||
content/46: adc97756961688b2f4cc69b773c961c9
|
||||
content/47: 7b0c309be79b5e1ab30e58a98ea0a778
|
||||
content/48: ac70442527be4edcc6b0936e2e5dc8c1
|
||||
content/49: a298d382850ddaa0b53e19975b9d12d2
|
||||
content/50: 27535bb1de08548a7389708045c10714
|
||||
content/51: 6f366fdb6389a03bfc4d83c12fa4099d
|
||||
content/52: b2a4a0c279f47d58a2456f25a1e1c6f9
|
||||
content/53: 17af9269613458de7f8e36a81b2a6d30
|
||||
content/41: a2c636da376e80aa3427ce26b2dce0fd
|
||||
content/42: d72903dda50a36b12ec050a06ef23a1b
|
||||
content/43: 19984dc55d279f1ae3226edf4b62aaa3
|
||||
content/44: 9c2f91f89a914bf4661512275e461104
|
||||
content/45: adc97756961688b2f4cc69b773c961c9
|
||||
content/46: 7b0c309be79b5e1ab30e58a98ea0a778
|
||||
content/47: ac70442527be4edcc6b0936e2e5dc8c1
|
||||
content/48: a298d382850ddaa0b53e19975b9d12d2
|
||||
content/49: 27535bb1de08548a7389708045c10714
|
||||
content/50: 6f366fdb6389a03bfc4d83c12fa4099d
|
||||
content/51: b2a4a0c279f47d58a2456f25a1e1c6f9
|
||||
content/52: 17af9269613458de7f8e36a81b2a6d30
|
||||
fa2a1ea3b95cd7608e0a7d78834b7d49:
|
||||
meta/title: d8df37d5e95512e955c43661de8a40d0
|
||||
meta/description: d25527b81409cb3d42d9841e8ed318d4
|
||||
@@ -3567,30 +3682,39 @@ checksums:
|
||||
meta/title: 27e1d8e6df8b8d3ee07124342bcc5599
|
||||
meta/description: 5a19804a907fe2a0c7ddc8a933e7e147
|
||||
content/0: 07f0ef1d9ef5ee2993ab113d95797f37
|
||||
content/1: c7dc738fa39cff694ecc04dca0aec33e
|
||||
content/1: 200b847a5b848c11a507cecfcb381e02
|
||||
content/2: dd7f8a45778d4dddd9bda78c19f046a4
|
||||
content/3: 298c570bb45cd493870e9406dc8be50e
|
||||
content/4: d4346df7c1c5da08e84931d2d449023a
|
||||
content/5: bacf5914637cc0c1e00dfac72f60cf1f
|
||||
content/6: 700ac74ffe23bbcc102b001676ee1818
|
||||
content/7: d7c2f6c70070e594bffd0af3d20bbccb
|
||||
content/8: 33b9b1e9744318597da4b925b0995be2
|
||||
content/9: caa663af7342e02001aca78c23695b22
|
||||
content/10: 1fa44e09185c753fec303e2c73e44eaf
|
||||
content/11: d794cf2ea75f4aa8e73069f41fe8bc45
|
||||
content/12: f9553f38263ad53c261995083622bdde
|
||||
content/13: e4625b8a75814b2fcfe3c643a47e22cc
|
||||
content/14: 20ee0fd34f3baab1099a2f8fb06b13cf
|
||||
content/15: 73a9d04015d0016a994cf1e8fe8d5c12
|
||||
content/16: 9a71a905db9dd5d43bdd769f006caf14
|
||||
content/17: 18c31983f32539861fd5b4e8dd943169
|
||||
content/18: 55300ae3e3c3213c4ad82c1cf21c89b2
|
||||
content/19: 8a8aa301371bd07b15c6f568a8e7826f
|
||||
content/20: a98cce6db23d9a86ac51179100f32529
|
||||
content/21: b5a605662dbb6fc20ad37fdb436f0581
|
||||
content/22: 2b204164f64dcf034baa6e5367679735
|
||||
content/23: b2a4a0c279f47d58a2456f25a1e1c6f9
|
||||
content/24: 15ebde5d554a3ec6000f71cf32b16859
|
||||
content/3: b1870986cdef32b6cf3c79a4cd56a8b0
|
||||
content/4: 5e7c060bf001ead8fb4005385509e857
|
||||
content/5: 1de0d605f73842c3464e7fb2e09fb92c
|
||||
content/6: 1a8e292ce7cc3adb2fe38cf2f5668b43
|
||||
content/7: bacf5914637cc0c1e00dfac72f60cf1f
|
||||
content/8: 336fdb536f9f5654d4d69a5adb1cf071
|
||||
content/9: d7c2f6c70070e594bffd0af3d20bbccb
|
||||
content/10: 33b9b1e9744318597da4b925b0995be2
|
||||
content/11: caa663af7342e02001aca78c23695b22
|
||||
content/12: 1fa44e09185c753fec303e2c73e44eaf
|
||||
content/13: d794cf2ea75f4aa8e73069f41fe8bc45
|
||||
content/14: f9553f38263ad53c261995083622bdde
|
||||
content/15: e4625b8a75814b2fcfe3c643a47e22cc
|
||||
content/16: 20ee0fd34f3baab1099a2f8fb06b13cf
|
||||
content/17: 73a9d04015d0016a994cf1e8fe8d5c12
|
||||
content/18: 9a71a905db9dd5d43bdd769f006caf14
|
||||
content/19: b4017a890213e9ac0afd6b2cfc1bdefc
|
||||
content/20: 479fd4d587cd0a1b8d27dd440e019215
|
||||
content/21: db263bbe8b5984777eb738e9e4c3ec71
|
||||
content/22: 1128c613d71aad35f668367ba2065a01
|
||||
content/23: 12be239d6ea36a71b022996f56d66901
|
||||
content/24: aa2240ef8ced8d9b67f7ab50665caae5
|
||||
content/25: 5cce1d6a21fae7252b8670a47a2fae9e
|
||||
content/26: 18c31983f32539861fd5b4e8dd943169
|
||||
content/27: 55300ae3e3c3213c4ad82c1cf21c89b2
|
||||
content/28: 8a8aa301371bd07b15c6f568a8e7826f
|
||||
content/29: a98cce6db23d9a86ac51179100f32529
|
||||
content/30: b5a605662dbb6fc20ad37fdb436f0581
|
||||
content/31: 2b204164f64dcf034baa6e5367679735
|
||||
content/32: b2a4a0c279f47d58a2456f25a1e1c6f9
|
||||
content/33: 15ebde5d554a3ec6000f71cf32b16859
|
||||
132869ed8674995bace940b1cefc4241:
|
||||
meta/title: a753d6bd11bc5876c739b95c6d174914
|
||||
meta/description: 71efdaceb123c4d6b6ee19c085cd9f0f
|
||||
@@ -3958,12 +4082,29 @@ checksums:
|
||||
content/3: b3c762557a1a308f3531ef1f19701807
|
||||
content/4: bf29da79344f37eeadd4c176aa19b8ff
|
||||
content/5: ae52879ebefa5664a6b7bf8ce5dd57ab
|
||||
content/6: 5e1cbe37c5714b16c908c7e0fe0b23e3
|
||||
content/7: ce487c9bc7a730e7d9da4a87b8eaa0a6
|
||||
content/8: e73f4b831f5b77c71d7d86c83abcbf11
|
||||
content/9: 07e064793f3e0bbcb02c4dc6083b6daa
|
||||
content/10: a702b191c3f94458bee880d33853e0cb
|
||||
content/11: ce110ab5da3ff96f8cbf96ce3376fc51
|
||||
content/12: 83f9b3ab46b0501c8eb3989bec3f4f1b
|
||||
content/13: e00be80effb71b0acb014f9aa53dfbe1
|
||||
content/14: 847a381137856ded9faa5994fbc489fb
|
||||
content/6: ce487c9bc7a730e7d9da4a87b8eaa0a6
|
||||
content/7: e73f4b831f5b77c71d7d86c83abcbf11
|
||||
content/8: 07e064793f3e0bbcb02c4dc6083b6daa
|
||||
content/9: a702b191c3f94458bee880d33853e0cb
|
||||
content/10: c497057cbb9dd53599071f8550f327cd
|
||||
content/11: cc6e48f85d5c6bfc05f846341f2d5cc9
|
||||
content/12: 8a80a6a97da9bf375fac565f1caabb49
|
||||
content/13: 098cc8e062187eb877fe5e172a4aa467
|
||||
content/14: e452a7cb33d7cf2f7cf1804703edaa20
|
||||
content/15: 466cfd61b1d0fcd8fc93d867dfd0f3e3
|
||||
content/16: 377572316021236994f444e88949ef34
|
||||
content/17: 54852933b2cbe3deb3b1c3059dba6a15
|
||||
content/18: 9e66b045763abe053a3ba8d2c23e9aa1
|
||||
content/19: d34f0950591e3beb085e99db64d07d2f
|
||||
content/20: 8677ef07618f7289b04fef3cce8bf745
|
||||
content/21: c0e6d2790e369569e7f272a5ec9ae21a
|
||||
content/22: 93643a0d9d9745f131e4eabf7ead2018
|
||||
content/23: 89c7da6d2e8fbc25e303a7381e147237
|
||||
content/24: a8ec63597dc3a3564bc5f0c3a6e5f42c
|
||||
content/25: 379618989b6cd427b319cfdab523297d
|
||||
content/26: bc4c2e699a7514771276e90e9aee53ba
|
||||
content/27: 38e14193b679ef774c3db93d399e700e
|
||||
content/28: ce110ab5da3ff96f8cbf96ce3376fc51
|
||||
content/29: 83f9b3ab46b0501c8eb3989bec3f4f1b
|
||||
content/30: e00be80effb71b0acb014f9aa53dfbe1
|
||||
content/31: 847a381137856ded9faa5994fbc489fb
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
"@vercel/analytics": "1.5.0",
|
||||
"@vercel/og": "^0.6.5",
|
||||
"clsx": "^2.1.1",
|
||||
"fumadocs-core": "^15.7.5",
|
||||
"fumadocs-mdx": "^11.5.6",
|
||||
"fumadocs-ui": "^15.7.5",
|
||||
"fumadocs-core": "15.8.2",
|
||||
"fumadocs-mdx": "11.10.1",
|
||||
"fumadocs-ui": "15.8.2",
|
||||
"lucide-react": "^0.511.0",
|
||||
"next": "15.4.1",
|
||||
"next-themes": "^0.4.6",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user