mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(execution): payload size bottlenecks with lazy execution value hydration, safer materialization, and batched parallel execution (#4560)
* improvement(resolver): lazy resolution for underlying fields greater than 10MB * progress * feat(parallel): batching * codegen to allow inline substitution * address comments * ui inconsistencies * cleanup redundant code * address more comments * address comments * replace helper * fix tests
This commit is contained in:
@@ -109,20 +109,22 @@ curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
|
||||
-d '{"inputs": {}, "async": true}'
|
||||
```
|
||||
|
||||
This returns immediately with a `taskId`:
|
||||
This returns immediately with a `jobId` and `statusUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"taskId": "job_abc123",
|
||||
"status": "queued"
|
||||
"jobId": "job_abc123",
|
||||
"statusUrl": "https://www.sim.ai/api/jobs/job_abc123",
|
||||
"message": "Workflow execution started",
|
||||
"async": true
|
||||
}
|
||||
```
|
||||
|
||||
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
|
||||
|
||||
```bash
|
||||
curl https://www.sim.ai/api/jobs/{taskId} \
|
||||
curl https://www.sim.ai/api/jobs/{jobId} \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
|
||||
@@ -109,20 +109,22 @@ curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
|
||||
-d '{"inputs": {}, "async": true}'
|
||||
```
|
||||
|
||||
This returns immediately with a `taskId`:
|
||||
This returns immediately with a `jobId` and `statusUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"taskId": "job_abc123",
|
||||
"status": "queued"
|
||||
"jobId": "job_abc123",
|
||||
"statusUrl": "https://www.sim.ai/api/jobs/job_abc123",
|
||||
"message": "Workflow execution started",
|
||||
"async": true
|
||||
}
|
||||
```
|
||||
|
||||
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
|
||||
|
||||
```bash
|
||||
curl https://www.sim.ai/api/jobs/{taskId} \
|
||||
curl https://www.sim.ai/api/jobs/{jobId} \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ result = client.execute_workflow(
|
||||
|
||||
**Returns:** `WorkflowExecutionResult | AsyncExecutionResult`
|
||||
|
||||
When `async_execution=True`, returns immediately with a task ID for polling. Otherwise, waits for completion.
|
||||
When `async_execution=True`, returns immediately with a `job_id` and `status_url` for polling. Otherwise, waits for completion.
|
||||
|
||||
##### get_workflow_status()
|
||||
|
||||
@@ -117,20 +117,20 @@ if is_ready:
|
||||
Get the status of an async job execution.
|
||||
|
||||
```python
|
||||
status = client.get_job_status("task-id-from-async-execution")
|
||||
status = client.get_job_status("job-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
|
||||
- `task_id` (str): The job ID returned from async execution
|
||||
|
||||
**Returns:** `Dict[str, Any]`
|
||||
|
||||
**Response fields:**
|
||||
- `success` (bool): Whether the request was successful
|
||||
- `taskId` (str): The task ID
|
||||
- `taskId` (str): The job 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)
|
||||
@@ -270,10 +270,11 @@ class WorkflowExecutionResult:
|
||||
@dataclass
|
||||
class AsyncExecutionResult:
|
||||
success: bool
|
||||
task_id: str
|
||||
status: str # 'queued'
|
||||
created_at: str
|
||||
links: Dict[str, str] # e.g., {"status": "/api/jobs/{taskId}"}
|
||||
job_id: str
|
||||
status_url: str
|
||||
execution_id: Optional[str] = None
|
||||
message: str = ""
|
||||
async_execution: bool = True
|
||||
```
|
||||
|
||||
### WorkflowStatus
|
||||
@@ -493,17 +494,17 @@ def execute_async():
|
||||
)
|
||||
|
||||
# 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']}")
|
||||
if hasattr(result, 'job_id'):
|
||||
print(f"Job ID: {result.job_id}")
|
||||
print(f"Status endpoint: {result.status_url}")
|
||||
|
||||
# Poll for completion
|
||||
status = client.get_job_status(result.task_id)
|
||||
status = client.get_job_status(result.job_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)
|
||||
status = client.get_job_status(result.job_id)
|
||||
|
||||
if status["status"] == "completed":
|
||||
print("Workflow completed!")
|
||||
@@ -764,7 +765,7 @@ import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "Do I need to deploy a workflow before I can execute it via the SDK?", answer: "Yes. Workflows must be deployed before they can be executed through the SDK. You can use the validate_workflow() method to check whether a workflow is deployed and ready. If it returns False, deploy the workflow from the Sim UI first and create or select an API key during deployment." },
|
||||
{ question: "What is the difference between sync and async execution?", answer: "Sync execution (the default) blocks until the workflow completes and returns the full result. Async execution (async_execution=True) returns immediately with a task ID that you can poll using get_job_status(). Use async mode for long-running workflows to avoid request timeouts. Async job statuses include queued, processing, completed, failed, and cancelled." },
|
||||
{ question: "What is the difference between sync and async execution?", answer: "Sync execution (the default) blocks until the workflow completes and returns the full result. Async execution (async_execution=True) returns immediately with a job ID and status URL that you can poll using get_job_status(). Use async mode for long-running workflows to avoid request timeouts. Async job statuses include queued, processing, completed, failed, and cancelled." },
|
||||
{ question: "How does the SDK handle rate limiting?", answer: "The SDK provides built-in rate limiting support through the execute_with_retry() method. It uses exponential backoff (1s, 2s, 4s, 8s...) with 25% jitter to avoid thundering herd problems. If the API returns a retry-after header, that value is used instead. You can configure max_retries, initial_delay, max_delay, and backoff_multiplier. Use get_rate_limit_info() to check your current rate limit status." },
|
||||
{ question: "Can I use the Python SDK as a context manager?", answer: "Yes. The SimStudioClient supports Python's context manager protocol. Use it with the 'with' statement to automatically close the underlying HTTP session when you are done, which is especially useful for scripts that create and discard client instances." },
|
||||
{ question: "How do I handle different types of errors from the SDK?", answer: "The SDK raises SimStudioError with a code property for API-specific errors. Common error codes are UNAUTHORIZED (invalid API key), TIMEOUT (request timed out), RATE_LIMIT_EXCEEDED (too many requests), USAGE_LIMIT_EXCEEDED (billing limit reached), and EXECUTION_ERROR (workflow failed). Use the error code to implement targeted error handling and recovery logic." },
|
||||
|
||||
@@ -94,7 +94,7 @@ const result = await client.executeWorkflow('workflow-id', { message: 'Hello, wo
|
||||
|
||||
**Returns:** `Promise<WorkflowExecutionResult | AsyncExecutionResult>`
|
||||
|
||||
When `async: true`, returns immediately with a task ID for polling. Otherwise, waits for completion.
|
||||
When `async: true`, returns immediately with a `jobId` and `statusUrl` for polling. Otherwise, waits for completion.
|
||||
|
||||
##### getWorkflowStatus()
|
||||
|
||||
@@ -131,7 +131,7 @@ if (isReady) {
|
||||
Get the status of an async job execution.
|
||||
|
||||
```typescript
|
||||
const status = await client.getJobStatus('task-id-from-async-execution');
|
||||
const status = await client.getJobStatus('job-id-from-async-execution');
|
||||
console.log('Status:', status.status); // 'queued', 'processing', 'completed', 'failed'
|
||||
if (status.status === 'completed') {
|
||||
console.log('Output:', status.output);
|
||||
@@ -139,13 +139,13 @@ if (status.status === 'completed') {
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `taskId` (string): The task ID returned from async execution
|
||||
- `jobId` (string): The job ID returned from async execution
|
||||
|
||||
**Returns:** `Promise<JobStatus>`
|
||||
|
||||
**Response fields:**
|
||||
- `success` (boolean): Whether the request was successful
|
||||
- `taskId` (string): The task ID
|
||||
- `taskId` (string): The job 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)
|
||||
@@ -278,12 +278,11 @@ interface WorkflowExecutionResult {
|
||||
```typescript
|
||||
interface AsyncExecutionResult {
|
||||
success: boolean;
|
||||
taskId: string;
|
||||
status: 'queued';
|
||||
createdAt: string;
|
||||
links: {
|
||||
status: string; // e.g., "/api/jobs/{taskId}"
|
||||
};
|
||||
jobId: string;
|
||||
statusUrl: string;
|
||||
executionId?: string;
|
||||
message: string;
|
||||
async: true;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -767,17 +766,17 @@ async function executeAsync() {
|
||||
});
|
||||
|
||||
// Check if result is an async execution
|
||||
if ('taskId' in result) {
|
||||
console.log('Task ID:', result.taskId);
|
||||
console.log('Status endpoint:', result.links.status);
|
||||
if ('jobId' in result) {
|
||||
console.log('Job ID:', result.jobId);
|
||||
console.log('Status endpoint:', result.statusUrl);
|
||||
|
||||
// Poll for completion
|
||||
let status = await client.getJobStatus(result.taskId);
|
||||
let status = await client.getJobStatus(result.jobId);
|
||||
|
||||
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);
|
||||
status = await client.getJobStatus(result.jobId);
|
||||
}
|
||||
|
||||
if (status.status === 'completed') {
|
||||
@@ -1022,7 +1021,7 @@ import { FAQ } from '@/components/ui/faq'
|
||||
|
||||
<FAQ items={[
|
||||
{ question: "Do I need to deploy a workflow before I can execute it via the SDK?", answer: "Yes. Workflows must be deployed before they can be executed through the SDK. You can use the validateWorkflow() method to check whether a workflow is deployed and ready. If it returns false, deploy the workflow from the Sim UI first and create or select an API key during deployment." },
|
||||
{ question: "What is the difference between sync and async execution?", answer: "Sync execution (the default) blocks until the workflow completes and returns the full result. Async execution returns immediately with a task ID that you can poll using getJobStatus(). Use async mode for long-running workflows to avoid request timeouts. Async job statuses include queued, processing, completed, failed, and cancelled." },
|
||||
{ question: "What is the difference between sync and async execution?", answer: "Sync execution (the default) blocks until the workflow completes and returns the full result. Async execution returns immediately with a job ID and status URL that you can poll using getJobStatus(). Use async mode for long-running workflows to avoid request timeouts. Async job statuses include queued, processing, completed, failed, and cancelled." },
|
||||
{ question: "How does streaming work with the SDK?", answer: "Enable streaming by setting stream: true and specifying selectedOutputs with block names and attributes in blockName.attribute format (e.g., ['agent1.content']). The response uses Server-Sent Events (SSE) format, sending incremental chunks as the workflow executes. Each chunk includes the blockId and the text content. A final done event includes the execution metadata." },
|
||||
{ question: "How does the SDK handle rate limiting?", answer: "The SDK provides built-in rate limiting support through the executeWithRetry() method. It uses exponential backoff (1s, 2s, 4s, 8s...) with 25% jitter to avoid thundering herd problems. If the API returns a retry-after header, that value is used instead. You can configure maxRetries, initialDelay, maxDelay, and backoffMultiplier. Use getRateLimitInfo() to check your current rate limit status." },
|
||||
{ question: "Is it safe to use the SDK in browser-side code?", answer: "You can use the SDK in the browser, but you should not expose your API key in client-side code. In production, use a backend proxy server to handle SDK calls, or use a public API key with limited permissions. The SDK works with both Node.js and browser environments, but sensitive keys should stay server-side." },
|
||||
|
||||
@@ -185,6 +185,53 @@ plt.show()
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Large Inputs and Payload Limits
|
||||
|
||||
Function blocks receive their code, parameters, resolved references, and previous block context in an internal execution request. Sim can safely reference oversized workflow outputs, such as large `loop.results` or `parallel.results`, when you select a smaller nested field like `<loop.results[0][0].id>`. Larger values are stored in execution storage and passed around as small references until code explicitly reads them.
|
||||
|
||||
File outputs are metadata-first by default. Referencing `<file.name>`, `<file.url>`, or similar metadata does not hydrate file contents. In JavaScript functions without imports, a direct base64 reference like `<readfile.file.base64>` is automatically rewritten to a lazy server-side read so the base64 string does not cross the Function request body.
|
||||
|
||||
You can also call the helper explicitly:
|
||||
|
||||
```javascript
|
||||
const file = <readfile.file>;
|
||||
const base64 = await sim.files.readBase64(file);
|
||||
```
|
||||
|
||||
`sim.files.readBase64(file)`, `sim.files.readText(file)`, `sim.files.readBase64Chunk(file, { offset, length })`, and `sim.files.readTextChunk(file, { offset, length })` read from server-side execution storage under memory caps. `sim.values.read(ref)` can explicitly read a large execution value reference. These helpers are available only in JavaScript functions without imports. JavaScript with imports, Python, and shell do not support these lazy helpers yet.
|
||||
|
||||
Very large full reads can still fail by design; use chunk helpers or return a file when you need to handle more data.
|
||||
|
||||
Use text chunks for text-like files such as logs, CSV, JSONL, and markdown:
|
||||
|
||||
```javascript
|
||||
const file = <readfile.file>;
|
||||
const firstMegabyte = await sim.files.readTextChunk(file, {
|
||||
offset: 0,
|
||||
length: 1024 * 1024,
|
||||
});
|
||||
|
||||
return firstMegabyte.split('\n').slice(0, 10);
|
||||
```
|
||||
|
||||
Use base64 chunks for binary files such as images, PDFs, audio, archives, or APIs that expect base64 input:
|
||||
|
||||
```javascript
|
||||
const file = <readfile.file>;
|
||||
const firstMegabyteBase64 = await sim.files.readBase64Chunk(file, {
|
||||
offset: 0,
|
||||
length: 1024 * 1024,
|
||||
});
|
||||
|
||||
return { name: file.name, chunk: firstMegabyteBase64 };
|
||||
```
|
||||
|
||||
Chunk `offset` and `length` are byte-based. For Unicode text, a chunk can split a multi-byte character at the boundary; use text chunks for approximate text processing and prefer smaller structured references when exact parsing matters.
|
||||
|
||||
Avoid passing a full large object into a Function block when you only need one field. For example, prefer `<api.data.customerId>` over `<api.data>` when the API response is large. If a JavaScript Function without imports references a large execution value, Sim automatically reads it through `sim.values.read(...)` at runtime under memory caps.
|
||||
|
||||
For large generated data, write the result to a file or table with `outputPath`, `outputSandboxPath`, or `outputTable` instead of returning the entire payload inline.
|
||||
|
||||
- **Keep functions focused**: Write functions that do one thing well to improve maintainability and debugging
|
||||
- **Handle errors gracefully**: Use try/catch blocks to handle potential errors and provide meaningful error messages
|
||||
- **Test edge cases**: Ensure your code handles unusual inputs, null values, and boundary conditions correctly
|
||||
|
||||
@@ -34,6 +34,7 @@ Choose between two types of parallel execution:
|
||||
</div>
|
||||
|
||||
Use this when you need to run the same operation multiple times concurrently.
|
||||
If the total count is larger than the batch size, Sim runs the work in serial batches while preserving the original result order.
|
||||
|
||||
```
|
||||
Example: Run 5 parallel instances
|
||||
@@ -57,7 +58,7 @@ Choose between two types of parallel execution:
|
||||
/>
|
||||
</div>
|
||||
|
||||
Each instance processes one item from the collection simultaneously.
|
||||
Each instance processes one item from the collection. Large collections run in serial batches while preserving each item's original index.
|
||||
|
||||
```
|
||||
Example: Process ["task1", "task2", "task3"] in parallel
|
||||
@@ -140,6 +141,12 @@ const allResults = <processtasks.results>;
|
||||
// Returns: [result1, result2, result3, ...]
|
||||
```
|
||||
|
||||
For large result sets, reference only the entry or field you need, such as `<processtasks.results[10][0].id>`. Sim keeps aggregate results indexable by storing oversized entries in execution storage and hydrating them only when an indexed server-side path is explicitly referenced.
|
||||
|
||||
### Batch Size
|
||||
|
||||
Parallel blocks run up to 20 branches at a time by default. Increase the total count or collection size to process more work; Sim will execute the next batch after the current batch finishes. You can lower the batch size to reduce concurrency for rate-limited APIs.
|
||||
|
||||
### Instance Isolation
|
||||
|
||||
Each parallel instance runs independently:
|
||||
@@ -157,7 +164,7 @@ Each parallel instance runs independently:
|
||||
While parallel execution is faster, be mindful of:
|
||||
- API rate limits when making concurrent requests
|
||||
- Memory usage with large datasets
|
||||
- Maximum of 20 concurrent instances to prevent resource exhaustion
|
||||
- Maximum of 20 concurrent instances per batch to prevent resource exhaustion
|
||||
</Callout>
|
||||
|
||||
## Parallel vs Loop
|
||||
@@ -186,6 +193,9 @@ Understanding when to use each:
|
||||
<li>
|
||||
<strong>Collection</strong>: Array or object to distribute (collection-based)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Batch size</strong>: Number of branches to run concurrently, from 1 to 20
|
||||
</li>
|
||||
</ul>
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -215,6 +215,25 @@ while (true) {
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
#### Oversized outputs
|
||||
|
||||
Workflow execution responses are capped by platform request and response limits. When an internal output, log field, streamed field, or async status payload contains a value that is too large to inline, Sim may replace that nested value with a versioned reference:
|
||||
|
||||
```json
|
||||
{
|
||||
"__simLargeValueRef": true,
|
||||
"version": 1,
|
||||
"id": "lv_abc123DEF456",
|
||||
"kind": "array",
|
||||
"size": 12582912,
|
||||
"key": "execution/workspace-id/workflow-id/exec_xyz/large-value-lv_abc123DEF456.json",
|
||||
"executionId": "exec_xyz",
|
||||
"preview": { "length": 25000 }
|
||||
}
|
||||
```
|
||||
|
||||
The `version` field is part of the external API contract. Treat the reference as an opaque placeholder for a value that could not be safely embedded in the response. `id`, `key`, and `executionId` are not fetch URLs; `key` points to execution-scoped server storage. Use `selectedOutputs` to request a smaller nested field, reduce the data passed between blocks, or return the data from a Response block when your workflow intentionally owns the HTTP response body. File outputs are metadata-first; request `.base64` only when you need inline file content. JavaScript Function blocks can explicitly read large files or value refs with the `sim.files` and `sim.values` helpers under memory caps.
|
||||
|
||||
### Asynchronous
|
||||
|
||||
For long-running workflows, async mode returns a job ID immediately so you don't need to hold the connection open. Add the `X-Execution-Mode: async` header to your request. The API returns HTTP 202 with a job ID and status URL. Poll the status URL until the job completes.
|
||||
|
||||
@@ -109,20 +109,22 @@ curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
|
||||
-d '{"inputs": {}, "async": true}'
|
||||
```
|
||||
|
||||
This returns immediately with a `taskId`:
|
||||
This returns immediately with a `jobId` and `statusUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"taskId": "job_abc123",
|
||||
"status": "queued"
|
||||
"jobId": "job_abc123",
|
||||
"statusUrl": "https://www.sim.ai/api/jobs/job_abc123",
|
||||
"message": "Workflow execution started",
|
||||
"async": true
|
||||
}
|
||||
```
|
||||
|
||||
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
|
||||
|
||||
```bash
|
||||
curl https://www.sim.ai/api/jobs/{taskId} \
|
||||
curl https://www.sim.ai/api/jobs/{jobId} \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
|
||||
@@ -109,20 +109,22 @@ curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
|
||||
-d '{"inputs": {}, "async": true}'
|
||||
```
|
||||
|
||||
This returns immediately with a `taskId`:
|
||||
This returns immediately with a `jobId` and `statusUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"taskId": "job_abc123",
|
||||
"status": "queued"
|
||||
"jobId": "job_abc123",
|
||||
"statusUrl": "https://www.sim.ai/api/jobs/job_abc123",
|
||||
"message": "Workflow execution started",
|
||||
"async": true
|
||||
}
|
||||
```
|
||||
|
||||
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
|
||||
|
||||
```bash
|
||||
curl https://www.sim.ai/api/jobs/{taskId} \
|
||||
curl https://www.sim.ai/api/jobs/{jobId} \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
|
||||
@@ -109,20 +109,22 @@ curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
|
||||
-d '{"inputs": {}, "async": true}'
|
||||
```
|
||||
|
||||
This returns immediately with a `taskId`:
|
||||
This returns immediately with a `jobId` and `statusUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"taskId": "job_abc123",
|
||||
"status": "queued"
|
||||
"jobId": "job_abc123",
|
||||
"statusUrl": "https://www.sim.ai/api/jobs/job_abc123",
|
||||
"message": "Workflow execution started",
|
||||
"async": true
|
||||
}
|
||||
```
|
||||
|
||||
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
|
||||
|
||||
```bash
|
||||
curl https://www.sim.ai/api/jobs/{taskId} \
|
||||
curl https://www.sim.ai/api/jobs/{jobId} \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
|
||||
@@ -109,20 +109,22 @@ curl -X POST https://www.sim.ai/api/workflows/{workflowId}/execute \
|
||||
-d '{"inputs": {}, "async": true}'
|
||||
```
|
||||
|
||||
This returns immediately with a `taskId`:
|
||||
This returns immediately with a `jobId` and `statusUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"taskId": "job_abc123",
|
||||
"status": "queued"
|
||||
"jobId": "job_abc123",
|
||||
"statusUrl": "https://www.sim.ai/api/jobs/job_abc123",
|
||||
"message": "Workflow execution started",
|
||||
"async": true
|
||||
}
|
||||
```
|
||||
|
||||
Poll the [Get Job Status](/api-reference/workflows/getJobStatus) endpoint until the status is `completed` or `failed`:
|
||||
|
||||
```bash
|
||||
curl https://www.sim.ai/api/jobs/{taskId} \
|
||||
curl https://www.sim.ai/api/jobs/{jobId} \
|
||||
-H "X-API-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
|
||||
@@ -117,20 +117,20 @@ if is_ready:
|
||||
获取异步任务执行的状态。
|
||||
|
||||
```python
|
||||
status = client.get_job_status("task-id-from-async-execution")
|
||||
status = client.get_job_status("job-id-from-async-execution")
|
||||
print("Status:", status["status"]) # 'queued', 'processing', 'completed', 'failed'
|
||||
if status["status"] == "completed":
|
||||
print("Output:", status["output"])
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `task_id` (str): 异步执行返回的任务 ID
|
||||
- `job_id` (str): 异步执行返回的作业 ID
|
||||
|
||||
**返回值:** `Dict[str, Any]`
|
||||
|
||||
**响应字段:**
|
||||
- `success` (bool): 请求是否成功
|
||||
- `taskId` (str): 任务 ID
|
||||
- `taskId` (str): 作业 ID
|
||||
- `status` (str): 可能的值包括 `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
|
||||
- `metadata` (dict): 包含 `startedAt`, `completedAt` 和 `duration`
|
||||
- `output` (any, optional): 工作流输出(完成时)
|
||||
@@ -271,10 +271,11 @@ class WorkflowExecutionResult:
|
||||
@dataclass
|
||||
class AsyncExecutionResult:
|
||||
success: bool
|
||||
task_id: str
|
||||
status: str # 'queued'
|
||||
created_at: str
|
||||
links: Dict[str, str] # e.g., {"status": "/api/jobs/{taskId}"}
|
||||
job_id: str
|
||||
status_url: str
|
||||
execution_id: Optional[str] = None
|
||||
message: str = ""
|
||||
async_execution: bool = True
|
||||
```
|
||||
|
||||
### WorkflowStatus
|
||||
@@ -494,17 +495,17 @@ def execute_async():
|
||||
)
|
||||
|
||||
# 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']}")
|
||||
if hasattr(result, 'job_id'):
|
||||
print(f"Job ID: {result.job_id}")
|
||||
print(f"Status endpoint: {result.status_url}")
|
||||
|
||||
# Poll for completion
|
||||
status = client.get_job_status(result.task_id)
|
||||
status = client.get_job_status(result.job_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)
|
||||
status = client.get_job_status(result.job_id)
|
||||
|
||||
if status["status"] == "completed":
|
||||
print("Workflow completed!")
|
||||
|
||||
@@ -138,7 +138,7 @@ if (isReady) {
|
||||
获取异步任务执行的状态。
|
||||
|
||||
```typescript
|
||||
const status = await client.getJobStatus('task-id-from-async-execution');
|
||||
const status = await client.getJobStatus('job-id-from-async-execution');
|
||||
console.log('Status:', status.status); // 'queued', 'processing', 'completed', 'failed'
|
||||
if (status.status === 'completed') {
|
||||
console.log('Output:', status.output);
|
||||
@@ -146,13 +146,13 @@ if (status.status === 'completed') {
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `taskId`(字符串):异步执行返回的任务 ID
|
||||
- `jobId`(字符串):异步执行返回的作业 ID
|
||||
|
||||
**返回值:** `Promise<JobStatus>`
|
||||
|
||||
**响应字段:**
|
||||
- `success`(布尔值):请求是否成功
|
||||
- `taskId`(字符串):任务 ID
|
||||
- `taskId`(字符串):作业 ID
|
||||
- `status`(字符串):以下之一 `'queued'`、`'processing'`、`'completed'`、`'failed'`、`'cancelled'`
|
||||
- `metadata`(对象):包含 `startedAt`、`completedAt` 和 `duration`
|
||||
- `output`(任意类型,可选):工作流输出(完成时)
|
||||
@@ -286,12 +286,11 @@ interface WorkflowExecutionResult {
|
||||
```typescript
|
||||
interface AsyncExecutionResult {
|
||||
success: boolean;
|
||||
taskId: string;
|
||||
status: 'queued';
|
||||
createdAt: string;
|
||||
links: {
|
||||
status: string; // e.g., "/api/jobs/{taskId}"
|
||||
};
|
||||
jobId: string;
|
||||
statusUrl: string;
|
||||
executionId?: string;
|
||||
message: string;
|
||||
async: true;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -797,17 +796,17 @@ async function executeAsync() {
|
||||
});
|
||||
|
||||
// Check if result is an async execution
|
||||
if ('taskId' in result) {
|
||||
console.log('Task ID:', result.taskId);
|
||||
console.log('Status endpoint:', result.links.status);
|
||||
if ('jobId' in result) {
|
||||
console.log('Job ID:', result.jobId);
|
||||
console.log('Status endpoint:', result.statusUrl);
|
||||
|
||||
// Poll for completion
|
||||
let status = await client.getJobStatus(result.taskId);
|
||||
let status = await client.getJobStatus(result.jobId);
|
||||
|
||||
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);
|
||||
status = await client.getJobStatus(result.jobId);
|
||||
}
|
||||
|
||||
if (status.status === 'completed') {
|
||||
|
||||
Reference in New Issue
Block a user