From 8c29fd20ad8ddd84703bfc2b7bee65349b74080a Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 7 Jul 2026 08:23:37 -0700 Subject: [PATCH 1/2] feat(tinybird): validate integration against API, add job-status polling, scope block outputs - Validated all 6 existing tools against Tinybird's live REST API docs - Added tinybird_get_job tool to poll async import/delete jobs - Scoped block outputs with per-operation condition --- apps/sim/blocks/blocks/tinybird.ts | 172 ++++++++++++++++++++++++++--- apps/sim/tools/registry.ts | 2 + apps/sim/tools/tinybird/get_job.ts | 138 +++++++++++++++++++++++ apps/sim/tools/tinybird/index.ts | 2 + apps/sim/tools/tinybird/types.ts | 28 +++++ 5 files changed, 327 insertions(+), 15 deletions(-) create mode 100644 apps/sim/tools/tinybird/get_job.ts diff --git a/apps/sim/blocks/blocks/tinybird.ts b/apps/sim/blocks/blocks/tinybird.ts index aefd038863d..09721bff271 100644 --- a/apps/sim/blocks/blocks/tinybird.ts +++ b/apps/sim/blocks/blocks/tinybird.ts @@ -9,7 +9,7 @@ export const TinybirdBlock: BlockConfig = { description: 'Send events, query data, and manage Data Sources with Tinybird', authMode: AuthMode.ApiKey, longDescription: - 'Interact with Tinybird: stream JSON or NDJSON events with the Events API, run SQL with the Query API, call published Pipe API Endpoints by name with dynamic parameters, and manage Data Sources by appending from a URL, truncating, or deleting rows by condition.', + 'Interact with Tinybird: stream JSON or NDJSON events with the Events API, run SQL with the Query API, call published Pipe API Endpoints by name with dynamic parameters, manage Data Sources by appending from a URL, truncating, or deleting rows by condition, and poll the status of asynchronous jobs.', docsLink: 'https://docs.sim.ai/integrations/tinybird', category: 'tools', integrationType: IntegrationType.Analytics, @@ -27,6 +27,7 @@ export const TinybirdBlock: BlockConfig = { { label: 'Append Data Source (from URL)', id: 'tinybird_append_datasource' }, { label: 'Truncate Data Source', id: 'tinybird_truncate_datasource' }, { label: 'Delete Data Source Rows', id: 'tinybird_delete_datasource_rows' }, + { label: 'Get Job Status', id: 'tinybird_get_job' }, ], value: () => 'tinybird_events', }, @@ -195,6 +196,15 @@ export const TinybirdBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', value: 'tinybird_delete_datasource_rows' }, }, + // Get Job Status operation inputs + { + id: 'job_id', + title: 'Job ID', + type: 'short-input', + placeholder: 'Job ID returned by an append or delete operation', + condition: { field: 'operation', value: 'tinybird_get_job' }, + required: true, + }, ], tools: { access: [ @@ -204,6 +214,7 @@ export const TinybirdBlock: BlockConfig = { 'tinybird_append_datasource', 'tinybird_truncate_datasource', 'tinybird_delete_datasource_rows', + 'tinybird_get_job', ], config: { tool: (params) => params.operation || 'tinybird_events', @@ -294,6 +305,13 @@ export const TinybirdBlock: BlockConfig = { typeof params.dry_run === 'string' ? params.dry_run.toLowerCase() : params.dry_run result.dry_run = dryRunValue === 'true' || dryRunValue === true } + } else if (operation === 'tinybird_get_job') { + // Get Job Status operation + if (!params.job_id) { + throw new Error('Job ID is required for Get Job Status operation') + } + + result.job_id = params.job_id } return result @@ -334,6 +352,8 @@ export const TinybirdBlock: BlockConfig = { // Delete Data Source Rows inputs delete_condition: { type: 'string', description: 'SQL condition selecting rows to delete' }, dry_run: { type: 'boolean', description: 'Test the delete without removing data' }, + // Get Job Status inputs + job_id: { type: 'string', description: 'ID of the job to check' }, // Common token: { type: 'string', description: 'Tinybird API Token' }, }, @@ -342,45 +362,160 @@ export const TinybirdBlock: BlockConfig = { successful_rows: { type: 'number', description: 'Number of rows successfully ingested', + condition: { field: 'operation', value: 'tinybird_events' }, }, quarantined_rows: { type: 'number', description: 'Number of rows quarantined (failed validation)', + condition: { field: 'operation', value: 'tinybird_events' }, }, - // Query outputs + // Query / Query Pipe outputs data: { type: 'json', description: 'Query result data. FORMAT JSON: array of objects. Other formats (CSV, TSV, etc.): raw text string.', + condition: { field: 'operation', value: ['tinybird_query', 'tinybird_query_pipe'] }, }, meta: { type: 'json', description: 'Column metadata for the result set: [{name, type}] (only with FORMAT JSON)', + condition: { field: 'operation', value: ['tinybird_query', 'tinybird_query_pipe'] }, + }, + rows: { + type: 'number', + description: 'Number of rows returned (only with FORMAT JSON)', + condition: { field: 'operation', value: ['tinybird_query', 'tinybird_query_pipe'] }, }, - rows: { type: 'number', description: 'Number of rows returned (only with FORMAT JSON)' }, rows_before_limit_at_least: { type: 'number', description: 'Minimum rows without a LIMIT clause (only with FORMAT JSON)', + condition: { field: 'operation', value: ['tinybird_query', 'tinybird_query_pipe'] }, }, statistics: { type: 'json', description: 'Query execution statistics - elapsed time, rows read, bytes read (only with FORMAT JSON)', + condition: { field: 'operation', value: ['tinybird_query', 'tinybird_query_pipe'] }, + }, + // Data Source management outputs (append / truncate / delete / get job) + id: { + type: 'string', + description: 'Operation identifier', + condition: { + field: 'operation', + value: [ + 'tinybird_append_datasource', + 'tinybird_delete_datasource_rows', + 'tinybird_get_job', + ], + }, + }, + import_id: { + type: 'string', + description: 'Import identifier (append)', + condition: { field: 'operation', value: 'tinybird_append_datasource' }, + }, + job_id: { + type: 'string', + description: + 'Job identifier to poll with the Get Job Status operation (append/delete/get job)', + condition: { + field: 'operation', + value: [ + 'tinybird_append_datasource', + 'tinybird_delete_datasource_rows', + 'tinybird_get_job', + ], + }, + }, + delete_id: { + type: 'string', + description: 'Deletion identifier (delete)', + condition: { field: 'operation', value: 'tinybird_delete_datasource_rows' }, + }, + job_url: { + type: 'string', + description: 'URL to query job status (append/delete/get job)', + condition: { + field: 'operation', + value: [ + 'tinybird_append_datasource', + 'tinybird_delete_datasource_rows', + 'tinybird_get_job', + ], + }, + }, + status: { + type: 'string', + description: 'Current job status (append/delete/get job)', + condition: { + field: 'operation', + value: [ + 'tinybird_append_datasource', + 'tinybird_delete_datasource_rows', + 'tinybird_get_job', + ], + }, }, - // Data Source management outputs (append / truncate / delete) - id: { type: 'string', description: 'Operation identifier (append/delete)' }, - import_id: { type: 'string', description: 'Import identifier (append)' }, - job_id: { type: 'string', description: 'Job identifier to poll status (append/delete)' }, - delete_id: { type: 'string', description: 'Deletion identifier (delete)' }, - job_url: { type: 'string', description: 'URL to query job status (append/delete)' }, - status: { type: 'string', description: 'Current job status (append/delete)' }, job: { type: 'json', - description: 'Full job details: kind, id, status, datasource, rows_affected (append/delete)', + description: + 'Full job details: kind, id, status, datasource, rows_affected (append/delete/get job)', + condition: { + field: 'operation', + value: [ + 'tinybird_append_datasource', + 'tinybird_delete_datasource_rows', + 'tinybird_get_job', + ], + }, + }, + datasource: { + type: 'json', + description: 'Target Data Source metadata (append)', + condition: { field: 'operation', value: 'tinybird_append_datasource' }, + }, + truncated: { + type: 'boolean', + description: 'Whether the Data Source was truncated', + condition: { field: 'operation', value: 'tinybird_truncate_datasource' }, + }, + result: { + type: 'json', + description: 'Raw truncate response body, if any', + condition: { field: 'operation', value: 'tinybird_truncate_datasource' }, + }, + // Get Job Status outputs + kind: { + type: 'string', + description: 'Job kind (e.g., "import", "delete_data", "populateview", "copy")', + condition: { field: 'operation', value: 'tinybird_get_job' }, + }, + created_at: { + type: 'string', + description: 'Timestamp the job was created', + condition: { field: 'operation', value: 'tinybird_get_job' }, + }, + started_at: { + type: 'string', + description: 'Timestamp the job started running', + condition: { field: 'operation', value: 'tinybird_get_job' }, + }, + updated_at: { + type: 'string', + description: 'Timestamp of the last job status update', + condition: { field: 'operation', value: 'tinybird_get_job' }, + }, + is_cancellable: { + type: 'boolean', + description: 'Whether the job can still be cancelled', + condition: { field: 'operation', value: 'tinybird_get_job' }, + }, + error: { + type: 'string', + description: 'Error message, present only when the job status is "error"', + condition: { field: 'operation', value: 'tinybird_get_job' }, }, - datasource: { type: 'json', description: 'Target Data Source metadata (append)' }, - truncated: { type: 'boolean', description: 'Whether the Data Source was truncated' }, - result: { type: 'json', description: 'Raw truncate response body, if any' }, }, } @@ -484,7 +619,14 @@ export const TinybirdBlockMeta = { description: 'Append from a URL, truncate, or delete rows by condition in a Tinybird Data Source.', content: - "# Manage Tinybird Data Source Rows\n\nMaintain a Data Source by loading, clearing, or pruning its rows.\n\n## Steps\n1. To load data, use Append Data Source (from URL) with the Data Source name, a Source File URL, and the source format (CSV, NDJSON, or Parquet).\n2. To clear everything, use Truncate Data Source with the Data Source name.\n3. To remove specific rows, use Delete Data Source Rows with a SQL Delete Condition such as event_date < '2024-01-01'.\n4. Enable Dry Run on a delete first to preview how many rows would be removed.\n\n## Output\nReturn the job ID and status for append and delete operations so you can poll for completion, or confirm the truncate succeeded.", + "# Manage Tinybird Data Source Rows\n\nMaintain a Data Source by loading, clearing, or pruning its rows.\n\n## Steps\n1. To load data, use Append Data Source (from URL) with the Data Source name, a Source File URL, and the source format (CSV, NDJSON, or Parquet).\n2. To clear everything, use Truncate Data Source with the Data Source name.\n3. To remove specific rows, use Delete Data Source Rows with a SQL Delete Condition such as event_date < '2024-01-01'.\n4. Enable Dry Run on a delete first to preview how many rows would be removed.\n\n## Output\nReturn the job ID and status for append and delete operations so you can poll with Get Job Status, or confirm the truncate succeeded.", + }, + { + name: 'poll-job-status', + description: + 'Check the status of an asynchronous Tinybird job, such as an append import or a delete-by-condition job.', + content: + '# Poll a Tinybird Job\n\nAppend Data Source and Delete Data Source Rows start asynchronous jobs that need to be polled for completion.\n\n## Steps\n1. Take the Job ID returned by Append Data Source or Delete Data Source Rows.\n2. Use the Get Job Status operation with the Base URL, API Token, and Job ID.\n3. Loop or wait until status is "done" (or "error"), checking again on a delay if still "waiting" or "working".\n\n## Output\nReturn the job kind, status, and full job details so you can confirm completion or surface the error message.', }, ], } as const satisfies BlockMeta diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index f32edc49219..ef386b717b6 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -3961,6 +3961,7 @@ import { tinybirdAppendDatasourceTool, tinybirdDeleteDatasourceRowsTool, tinybirdEventsTool, + tinybirdGetJobTool, tinybirdQueryPipeTool, tinybirdQueryTool, tinybirdTruncateDatasourceTool, @@ -7009,6 +7010,7 @@ export const tools: Record = { tinybird_append_datasource: tinybirdAppendDatasourceTool, tinybird_truncate_datasource: tinybirdTruncateDatasourceTool, tinybird_delete_datasource_rows: tinybirdDeleteDatasourceRowsTool, + tinybird_get_job: tinybirdGetJobTool, stagehand_extract: stagehandExtractTool, stagehand_agent: stagehandAgentTool, mem0_add_memories: mem0AddMemoriesTool, diff --git a/apps/sim/tools/tinybird/get_job.ts b/apps/sim/tools/tinybird/get_job.ts new file mode 100644 index 00000000000..bc504e8af6e --- /dev/null +++ b/apps/sim/tools/tinybird/get_job.ts @@ -0,0 +1,138 @@ +import { createLogger } from '@sim/logger' +import type { TinybirdGetJobParams, TinybirdGetJobResponse } from '@/tools/tinybird/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('tinybird-get-job') + +/** + * Tinybird Get Job Tool + * + * Polls the status of an asynchronous job (import, delete, populate, copy) by ID. + * Used to check on jobs started by the Append Data Source and Delete Data Source Rows + * operations, which return a job_id but do not wait for completion. + */ +export const getJobTool: ToolConfig = { + id: 'tinybird_get_job', + name: 'Tinybird Get Job', + description: 'Check the status of an asynchronous Tinybird job (import, delete, etc.) by ID.', + version: '1.0.0', + errorExtractor: 'nested-error-object', + + params: { + base_url: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tinybird API base URL (e.g., https://api.tinybird.co)', + }, + job_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the job to check, as returned by an append or delete operation', + }, + token: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tinybird API Token with ADMIN scope, or the token that started the job', + }, + }, + + request: { + url: (params) => { + const baseUrl = params.base_url.trim().replace(/\/+$/, '') + return `${baseUrl}/v0/jobs/${encodeURIComponent(params.job_id.trim())}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.token.trim()}`, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + logger.info('Fetched Tinybird job status', { + jobId: data.job_id ?? data.id, + kind: data.kind, + status: data.status, + }) + + return { + success: true, + output: { + id: data.id ?? null, + job_id: data.job_id ?? data.id ?? null, + kind: data.kind ?? null, + status: data.status ?? null, + job_url: data.job_url ?? null, + created_at: data.created_at ?? null, + started_at: data.started_at ?? null, + updated_at: data.updated_at ?? null, + is_cancellable: data.is_cancellable ?? null, + error: data.error ?? null, + job: data, + }, + } + }, + + outputs: { + id: { + type: 'string', + description: 'Job identifier', + optional: true, + }, + job_id: { + type: 'string', + description: 'Job identifier (same as id)', + optional: true, + }, + kind: { + type: 'string', + description: 'Job kind (e.g., "import", "delete_data", "populateview", "copy")', + optional: true, + }, + status: { + type: 'string', + description: 'Current job status: "waiting", "working", "done", "error", or "cancelled"', + optional: true, + }, + job_url: { + type: 'string', + description: 'URL to re-query this job status', + optional: true, + }, + created_at: { + type: 'string', + description: 'Timestamp the job was created', + optional: true, + }, + started_at: { + type: 'string', + description: 'Timestamp the job started running', + optional: true, + }, + updated_at: { + type: 'string', + description: 'Timestamp of the last job status update', + optional: true, + }, + is_cancellable: { + type: 'boolean', + description: 'Whether the job can still be cancelled', + optional: true, + }, + error: { + type: 'string', + description: 'Error message, present only when status is "error"', + optional: true, + }, + job: { + type: 'json', + description: + 'Full raw job details, including kind-specific fields (statistics, datasource, delete_condition, etc.)', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/tinybird/index.ts b/apps/sim/tools/tinybird/index.ts index 17ca52f6b94..a50200b34c0 100644 --- a/apps/sim/tools/tinybird/index.ts +++ b/apps/sim/tools/tinybird/index.ts @@ -1,6 +1,7 @@ import { appendDatasourceTool } from '@/tools/tinybird/append_datasource' import { deleteDatasourceRowsTool } from '@/tools/tinybird/delete_datasource_rows' import { eventsTool } from '@/tools/tinybird/events' +import { getJobTool } from '@/tools/tinybird/get_job' import { queryTool } from '@/tools/tinybird/query' import { queryPipeTool } from '@/tools/tinybird/query_pipe' import { truncateDatasourceTool } from '@/tools/tinybird/truncate_datasource' @@ -11,3 +12,4 @@ export const tinybirdQueryPipeTool = queryPipeTool export const tinybirdAppendDatasourceTool = appendDatasourceTool export const tinybirdTruncateDatasourceTool = truncateDatasourceTool export const tinybirdDeleteDatasourceRowsTool = deleteDatasourceRowsTool +export const tinybirdGetJobTool = getJobTool diff --git a/apps/sim/tools/tinybird/types.ts b/apps/sim/tools/tinybird/types.ts index 07699f8ccfd..3d4392eee51 100644 --- a/apps/sim/tools/tinybird/types.ts +++ b/apps/sim/tools/tinybird/types.ts @@ -150,6 +150,33 @@ export interface TinybirdDeleteDatasourceRowsResponse extends ToolResponse { } } +/** + * Parameters for checking the status of an asynchronous job + */ +export interface TinybirdGetJobParams extends TinybirdBaseParams { + base_url: string + job_id: string +} + +/** + * Response from checking an asynchronous job's status + */ +export interface TinybirdGetJobResponse extends ToolResponse { + output: { + id: string | null + job_id: string | null + kind: string | null + status: string | null + job_url: string | null + created_at: string | null + started_at: string | null + updated_at: string | null + is_cancellable: boolean | null + error: string | null + job: Record | null + } +} + /** * Union type for all possible Tinybird responses */ @@ -160,3 +187,4 @@ export type TinybirdResponse = | TinybirdAppendDatasourceResponse | TinybirdTruncateDatasourceResponse | TinybirdDeleteDatasourceRowsResponse + | TinybirdGetJobResponse From c7c546362cd9d73af4ee3f027978671de314a802 Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 7 Jul 2026 08:37:26 -0700 Subject: [PATCH 2/2] fix(tinybird): align token-scope descriptions with Tinybird's plural scope names - events/query/query_pipe used singular DATASOURCE:/PIPE: scope names; Tinybird's Token API uses plural DATASOURCES:/PIPES: - append_datasource now notes DATASOURCES:APPEND also suffices, not just CREATE --- apps/sim/tools/tinybird/append_datasource.ts | 2 +- apps/sim/tools/tinybird/events.ts | 2 +- apps/sim/tools/tinybird/query.ts | 2 +- apps/sim/tools/tinybird/query_pipe.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/sim/tools/tinybird/append_datasource.ts b/apps/sim/tools/tinybird/append_datasource.ts index 3e4e11a40c1..90b0d5e892a 100644 --- a/apps/sim/tools/tinybird/append_datasource.ts +++ b/apps/sim/tools/tinybird/append_datasource.ts @@ -55,7 +55,7 @@ export const appendDatasourceTool: ToolConfig< type: 'string', required: true, visibility: 'user-only', - description: 'Tinybird API Token with DATASOURCES:CREATE scope', + description: 'Tinybird API Token with DATASOURCES:APPEND or DATASOURCES:CREATE scope', }, }, diff --git a/apps/sim/tools/tinybird/events.ts b/apps/sim/tools/tinybird/events.ts index 0a85dab88f8..0fc2453363f 100644 --- a/apps/sim/tools/tinybird/events.ts +++ b/apps/sim/tools/tinybird/events.ts @@ -58,7 +58,7 @@ export const eventsTool: ToolConfig = type: 'string', required: true, visibility: 'user-only', - description: 'Tinybird API Token with PIPE:READ scope', + description: 'Tinybird API Token with PIPES:READ scope', }, }, diff --git a/apps/sim/tools/tinybird/query_pipe.ts b/apps/sim/tools/tinybird/query_pipe.ts index 0ba60ad302c..cfde3d31994 100644 --- a/apps/sim/tools/tinybird/query_pipe.ts +++ b/apps/sim/tools/tinybird/query_pipe.ts @@ -80,7 +80,7 @@ export const queryPipeTool: ToolConfig