O SDK oficial de TypeScript/JavaScript do Studio oferece segurança de tipos completa e funciona tanto em Node.js quanto no navegador, permitindo executar workflows de forma programática nas suas aplicações Node.js, aplicações web e outros ambientes JavaScript.
O SDK de TypeScript oferece segurança de tipos completa, suporte a execução assíncrona, controle automático de limite de requisições com backoff exponencial e acompanhamento de uso.
Instalação
Instale o SDK com o gerenciador de pacotes que você preferir:
bash npm install studio-ts-sdk bash yarn add studio-ts-sdk bash bun add studio-ts-sdk Início rápido
Um exemplo simples para você começar:
import { StudioClient } from "studio-ts-sdk";
// Initialize the client
const client = new StudioClient({
apiKey: "your-api-key-here",
baseUrl: "https://agent-studio.seeyu.ai", // hosted Studio; the default (https://seeyu.ai) is not the API host
});
// Execute a workflow
try {
const result = await client.executeWorkflow("workflow-id");
console.log("Workflow executed successfully:", result);
} catch (error) {
console.error("Workflow execution failed:", error);
}Referência da API
StudioClient
Construtor
new StudioClient(config: StudioConfig)Configuração:
config.apiKey(string): sua chave de API do Studioconfig.baseUrl(string, opcional): URL base da API do Studio (padrão:https://seeyu.ai). No Studio hospedado, usehttps://agent-studio.seeyu.ai
Métodos
executeWorkflow()
Executa um workflow com dados de entrada opcionais.
const result = await client.executeWorkflow('workflow-id', { message: 'Hello, world!' }, {
timeout: 30000 // 30 seconds
});Parâmetros:
workflowId(string): o ID do workflow a executarinput(any, opcional): dados de entrada a passar para o workflowoptions(ExecutionOptions, opcional):timeout(number): tempo limite em milissegundos (padrão: 30000)stream(boolean): habilita respostas em streaming (padrão: false)selectedOutputs(string[]): saídas de block a transmitir, no formatoblockName.attribute(por exemplo,["agent1.content"])async(boolean): executa de forma assíncrona (padrão: false)executionTimeoutSeconds(number): teto opcional, no servidor, para a execução assíncrona, de 1 a 604800 segundos. Requerasync: truee não pode estender a política da conta.
Retorna: Promise<WorkflowExecutionResult | AsyncExecutionResult>
Com async: true, retorna imediatamente com um runId e uma statusUrl para consulta. Caso contrário, aguarda a conclusão.
getWorkflowStatus()
Obtém o status de um workflow (status de deploy etc.).
const status = await client.getWorkflowStatus("workflow-id");
console.log("Is deployed:", status.isDeployed);Parâmetros:
workflowId(string): o ID do workflow
Retorna: Promise<WorkflowStatus>
validateWorkflow()
Valida se um workflow está pronto para ser executado.
const isReady = await client.validateWorkflow("workflow-id");
if (isReady) {
// Workflow is deployed and ready
}Parâmetros:
workflowId(string): o ID do workflow
Retorna: Promise<boolean>
getWorkflowRun()
Obtém o status e, opcionalmente, as saídas de uma execução de workflow.
const status = await client.getWorkflowRun('workflow-id', 'run-id', {
includeOutput: true
});
console.log('Status:', status.status); // 'queued', 'running', 'completed', 'failed'
if (status.status === 'completed') {
console.log('Output:', status.output);
}Parâmetros:
workflowId(string): o ID do workflowrunId(string): o ID da execução retornado pela execução assíncronaoptions.includeOutput(boolean, opcional): inclui a saída final das execuções concluídasoptions.selectedOutputs(string[], opcional): seletores de saída de block a incluir
Retorna: Promise<WorkflowRunStatus>
Campos da resposta:
runId(string): o ID da execuçãoworkflowId(string): o ID do workflowstatus(string): um de'queued','pending','running','paused','completed','failed','cancelled'startedAt/endedAt(string): timestamps da execuçãodurationMs(number, nullable): duração em milissegundosoutput(any, nullable): a saída do workflow, quando solicitada para uma execução concluídablockOutputs(object, nullable): as saídas de block solicitadaserror(object, nullable): detalhes estruturados da falha, comcode,messagee, opcionalmente,details
getJobStatus()
Obtém o status de um job criado pelo endpoint legado de execução assíncrona. Integrações novas devem usar getWorkflowRun() com o ID da execução.
const status = await client.getJobStatus('legacy-job-id');executeWithRetry()
Executa um workflow com retentativa automática em erros de limite de requisições, usando backoff exponencial.
const result = await client.executeWithRetry('workflow-id', { 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): o ID do workflow a executarinput(any, opcional): dados de entrada a passar para o workflowoptions(ExecutionOptions, opcional): os mesmos deexecuteWorkflow()retryOptions(RetryOptions, opcional):maxRetries(number): número máximo de retentativas (padrão: 3)initialDelay(number): espera inicial em ms (padrão: 1000)maxDelay(number): espera máxima em ms (padrão: 30000)backoffMultiplier(number): multiplicador do backoff (padrão: 2)
Retorna: Promise<WorkflowExecutionResult | AsyncExecutionResult>
A lógica de retentativa usa backoff exponencial (1s → 2s → 4s → 8s...) com ±25% de jitter para evitar o efeito manada. Se a API enviar o header retry-after, esse valor é usado no lugar do cálculo.
getRateLimitInfo()
Obtém as informações de limite de requisições da última resposta da API.
const rateLimitInfo = client.getRateLimitInfo();
if (rateLimitInfo) {
console.log("Limit:", rateLimitInfo.limit);
console.log("Remaining:", rateLimitInfo.remaining);
console.log("Reset:", new Date(rateLimitInfo.reset * 1000));
}Retorna: RateLimitInfo | null
getUsageLimits()
Obtém os limites de uso e as informações de cota atuais da sua conta.
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);Retorna: Promise<UsageLimits>
Estrutura da resposta:
{
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()
Atualiza a chave de API.
client.setApiKey("new-api-key");setBaseUrl()
Atualiza a URL base.
client.setBaseUrl("https://my-custom-domain.com");Tipos
WorkflowExecutionResult
interface WorkflowExecutionResult {
success: boolean;
output?: any;
error?: string;
logs?: any[];
metadata?: {
duration?: number;
runId?: string;
[key: string]: any;
};
traceSpans?: any[];
totalDuration?: number;
}AsyncExecutionResult
interface AsyncExecutionResult {
success: boolean;
runId: string;
statusUrl: string;
message: string;
async: true;
}WorkflowStatus
interface WorkflowStatus {
isDeployed: boolean;
deployedAt?: string;
needsRedeployment: boolean;
}RateLimitInfo
interface RateLimitInfo {
limit: number;
remaining: number;
reset: number;
retryAfter?: number;
}UsageLimits
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;
};
}StudioError
class StudioError extends Error {
code?: string;
status?: number;
}Códigos de erro comuns:
UNAUTHORIZED: chave de API inválidaTIMEOUT: a requisição excedeu o tempo limiteRATE_LIMIT_EXCEEDED: limite de requisições excedidoUSAGE_LIMIT_EXCEEDED: limite de uso excedidoEXECUTION_ERROR: a execução do workflow falhou
Exemplos
Execução básica de um workflow
Configure o StudioClient com sua chave de API.
Verifique se o workflow tem deploy e está pronto para ser executado.
Execute o workflow com os seus dados de entrada.
Processe o resultado da execução e trate eventuais erros.
import { StudioClient } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
async function runWorkflow() {
try {
// Check if workflow is ready
const isReady = await client.validateWorkflow("my-workflow-id");
if (!isReady) {
throw new Error("Workflow is not deployed or ready");
}
// Execute the workflow
const result = await client.executeWorkflow('my-workflow-id', {
message: 'Process this data',
userId: '12345'
});
if (result.success) {
console.log("Output:", result.output);
console.log("Duration:", result.metadata?.duration);
} else {
console.error("Workflow failed:", result.error);
}
} catch (error) {
console.error("Error:", error);
}
}
runWorkflow();Tratamento de erros
Trate os diferentes tipos de erro que podem acontecer durante a execução de um workflow:
import { StudioClient, StudioError } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
async function executeWithErrorHandling() {
try {
const result = await client.executeWorkflow("workflow-id");
return result;
} catch (error) {
if (error instanceof StudioError) {
switch (error.code) {
case "UNAUTHORIZED":
console.error("Invalid API key");
break;
case "TIMEOUT":
console.error("Workflow execution timed out");
break;
case "USAGE_LIMIT_EXCEEDED":
console.error("Usage limit exceeded");
break;
case "INVALID_JSON":
console.error("Invalid JSON in request body");
break;
default:
console.error("Workflow error:", error.message);
}
} else {
console.error("Unexpected error:", error);
}
throw error;
}
}Configuração por ambiente
Configure o cliente usando variáveis de ambiente:
import { StudioClient } from 'studio-ts-sdk';
// Development configuration
const apiKey = process.env.STUDIO_API_KEY;
if (!apiKey) {
throw new Error('STUDIO_API_KEY environment variable is required');
}
const client = new StudioClient({
apiKey,
baseUrl: process.env.STUDIO_BASE_URL || 'https://agent-studio.seeyu.ai'
});import { StudioClient } from 'studio-ts-sdk';
// Production configuration with validation
const apiKey = process.env.STUDIO_API_KEY;
if (!apiKey) {
throw new Error('STUDIO_API_KEY environment variable is required');
}
const client = new StudioClient({
apiKey,
baseUrl: process.env.STUDIO_BASE_URL || 'https://agent-studio.seeyu.ai'
});Integração com Express no Node.js
Integre com um servidor Express.js:
import express from "express";
import { StudioClient } from "studio-ts-sdk";
const app = express();
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
app.use(express.json());
app.post("/execute-workflow", async (req, res) => {
try {
const { workflowId, input } = req.body;
const result = await client.executeWorkflow(workflowId, input, {
timeout: 60000
});
res.json({
success: true,
data: result,
});
} catch (error) {
console.error("Workflow execution error:", error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : "Unknown error",
});
}
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});Rota de API do Next.js
Use com as rotas de API do Next.js:
// pages/api/workflow.ts or app/api/workflow/route.ts
import { NextApiRequest, NextApiResponse } from "next";
import { StudioClient } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
try {
const { workflowId, input } = req.body;
const result = await client.executeWorkflow(workflowId, input, {
timeout: 30000
});
res.status(200).json(result);
} catch (error) {
console.error("Error executing workflow:", error);
res.status(500).json({
error: "Failed to execute workflow",
});
}
}Uso no navegador
Use no navegador (com a configuração de CORS adequada):
import { StudioClient } from "studio-ts-sdk";
// Note: In production, use a proxy server to avoid exposing API keys
const client = new StudioClient({
apiKey: "your-public-api-key", // Use with caution in browser
baseUrl: "https://agent-studio.seeyu.ai",
});
async function executeClientSideWorkflow() {
try {
const result = await client.executeWorkflow('workflow-id', {
userInput: 'Hello from browser'
});
console.log("Workflow result:", result);
// Update UI with result
document.getElementById("result")!.textContent = JSON.stringify(
result.output,
null,
2,
);
} catch (error) {
console.error("Error:", error);
}
}Upload de arquivos
Objetos File são detectados automaticamente e convertidos para base64. Inclua-os no seu input sob o nome de campo que corresponde ao formato de entrada do trigger de API do seu workflow.
O SDK converte objetos File para este formato:
{
type: 'file',
data: 'data:mime/type;base64,base64data',
name: 'filename',
mime: 'mime/type'
}Como alternativa, você pode informar os arquivos manualmente no formato de URL:
{
type: 'url',
data: 'https://example.com/file.pdf',
name: 'file.pdf',
mime: 'application/pdf'
}import { StudioClient } from 'studio-ts-sdk';
import fs from 'fs';
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: 'https://agent-studio.seeyu.ai'
});
// 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', {
documents: [file], // Must match your workflow's "files" field name
query: 'Summarize this document'
});Ao usar o SDK no navegador, tome cuidado para não expor chaves de API sensíveis. Considere usar um proxy de backend ou chaves de API públicas com permissões limitadas.
Exemplo de hook do React
Crie um hook do React para executar workflows:
import { useState, useCallback } from 'react';
import { StudioClient, WorkflowExecutionResult } from 'studio-ts-sdk';
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: 'https://agent-studio.seeyu.ai'
});
interface UseWorkflowResult {
result: WorkflowExecutionResult | null;
loading: boolean;
error: Error | null;
executeWorkflow: (workflowId: string, input?: any) => Promise<void>;
}
export function useWorkflow(): UseWorkflowResult {
const [result, setResult] = useState<WorkflowExecutionResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const executeWorkflow = useCallback(async (workflowId: string, input?: any) => {
setLoading(true);
setError(null);
setResult(null);
try {
const workflowResult = await client.executeWorkflow(workflowId, input, {
timeout: 30000
});
setResult(workflowResult);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
}, []);
return {
result,
loading,
error,
executeWorkflow
};
}
// Usage in component
function WorkflowComponent() {
const { result, loading, error, executeWorkflow } = useWorkflow();
const handleExecute = () => {
executeWorkflow('my-workflow-id', {
message: 'Hello from React!'
});
};
return (
<div>
<button onClick={handleExecute} disabled={loading}>
{loading ? 'Executing...' : 'Execute Workflow'}
</button>
{error && <div>Error: {error.message}</div>}
{result && (
<div>
<h3>Result:</h3>
<pre>{JSON.stringify(result, null, 2)}</pre>
</div>
)}
</div>
);
}Execução assíncrona de workflows
Execute workflows de forma assíncrona para tarefas longas:
import { StudioClient, AsyncExecutionResult } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
async function executeAsync() {
try {
// Start async execution
const result = await client.executeWorkflow('workflow-id', { data: 'large dataset' }, {
async: true // Execute asynchronously
});
// Check if result is an async execution
if ('async' in result && result.async) {
console.log('Run ID:', result.runId);
console.log('Status endpoint:', result.statusUrl);
// Poll for completion
let status = await client.getWorkflowRun('workflow-id', result.runId, {
includeOutput: true
});
while (status.status === 'queued' || status.status === 'pending' || status.status === 'running') {
console.log('Current status:', status.status);
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
status = await client.getWorkflowRun('workflow-id', result.runId, {
includeOutput: true
});
}
if (status.status === 'completed') {
console.log('Workflow completed!');
console.log('Output:', status.output);
console.log('Duration:', status.durationMs);
} else {
console.error("Workflow failed:", status.error);
}
}
} catch (error) {
console.error("Error:", error);
}
}
executeAsync();Limite de requisições e retentativas
Lide com os limites de requisições automaticamente, usando backoff exponencial:
import { StudioClient, StudioError } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
async function executeWithRetryHandling() {
try {
// Automatically retries on rate limit
const result = await client.executeWithRetry('workflow-id', { message: 'Process this' }, {}, {
maxRetries: 5,
initialDelay: 1000,
maxDelay: 60000,
backoffMultiplier: 2
});
console.log("Success:", result);
} catch (error) {
if (
error instanceof StudioError &&
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),
);
}
}
}
}Monitoramento de uso
Acompanhe o uso e os limites da sua conta:
import { StudioClient } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
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();Execução de workflow com streaming
Execute workflows com respostas em streaming em tempo real:
import { StudioClient } from "studio-ts-sdk";
const client = new StudioClient({
apiKey: process.env.STUDIO_API_KEY!,
baseUrl: "https://agent-studio.seeyu.ai",
});
async function executeWithStreaming() {
try {
// Enable streaming for specific block outputs
const result = await client.executeWorkflow('workflow-id', { message: 'Count to five' }, {
stream: true,
selectedOutputs: ["agent1.content"], // Use blockName.attribute format
});
console.log("Workflow result:", result);
} catch (error) {
console.error("Error:", error);
}
}A resposta em streaming segue o formato 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]Exemplo de streaming com React:
import { useState, useEffect } from 'react';
function StreamingWorkflow() {
const [output, setOutput] = useState('');
const [loading, setLoading] = useState(false);
const executeStreaming = async () => {
setLoading(true);
setOutput('');
// IMPORTANT: Make this API call from your backend server, not the browser
// Never expose your API key in client-side code
const response = await fetch('https://agent-studio.seeyu.ai/api/v2/workflows/WORKFLOW_ID/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.STUDIO_API_KEY! // Server-side environment variable only
},
body: JSON.stringify({
input: { 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>
);
}Como obter sua chave de API
Acesse o Studio e entre na sua conta.
Vá até o workflow que você quer executar de forma programática.
Clique em "Deploy" para fazer o deploy do workflow, caso ele ainda não tenha deploy.
Durante o processo de deploy, selecione ou crie uma chave de API.
Copie a chave de API para usar na sua aplicação TypeScript/JavaScript.
Requisitos
- Node.js 16+
- TypeScript 5.0+ (para projetos TypeScript)
Licença
Apache-2.0