From 4431a1a48486e2049c36873fe9e606468e639f98 Mon Sep 17 00:00:00 2001 From: Martin Yankov <23098926+Lutherwaves@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:59:08 +0200 Subject: [PATCH 01/10] fix(helm): add custom egress rules to realtime network policy (#2481) The realtime service network policy was missing the custom egress rules section that allows configuration of additional egress rules via values.yaml. This caused the realtime pods to be unable to connect to external databases (e.g., PostgreSQL on port 5432) when using external database configurations. The app network policy already had this section, but the realtime network policy was missing it, creating an inconsistency and preventing the realtime service from accessing external databases configured via networkPolicy.egress values. This fix adds the same custom egress rules template section to the realtime network policy, matching the app network policy behavior and allowing users to configure database connectivity via values.yaml. --- helm/sim/templates/networkpolicy.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helm/sim/templates/networkpolicy.yaml b/helm/sim/templates/networkpolicy.yaml index deac5a5dba..7ef8697417 100644 --- a/helm/sim/templates/networkpolicy.yaml +++ b/helm/sim/templates/networkpolicy.yaml @@ -141,6 +141,10 @@ spec: ports: - protocol: TCP port: 443 + # Allow custom egress rules + {{- with .Values.networkPolicy.egress }} + {{- toYaml . | nindent 2 }} + {{- end }} {{- end }} {{- if .Values.postgresql.enabled }} From 39074274574d95cc568034c6caf63abff9f9e13a Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya Date: Mon, 12 Jan 2026 12:26:16 -0800 Subject: [PATCH 02/10] updates --- apps/sim/blocks/blocks/tinybird.ts | 168 +++++++++++++++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 18 +++- apps/sim/tools/registry.ts | 3 + apps/sim/tools/tinybird/events.ts | 127 ++++++++++++++++++++++ apps/sim/tools/tinybird/index.ts | 5 + apps/sim/tools/tinybird/query.ts | 155 ++++++++++++++++++++++++++ apps/sim/tools/tinybird/types.ts | 59 ++++++++++ 8 files changed, 533 insertions(+), 4 deletions(-) create mode 100644 apps/sim/blocks/blocks/tinybird.ts create mode 100644 apps/sim/tools/tinybird/events.ts create mode 100644 apps/sim/tools/tinybird/index.ts create mode 100644 apps/sim/tools/tinybird/query.ts create mode 100644 apps/sim/tools/tinybird/types.ts diff --git a/apps/sim/blocks/blocks/tinybird.ts b/apps/sim/blocks/blocks/tinybird.ts new file mode 100644 index 0000000000..fcd01c0a94 --- /dev/null +++ b/apps/sim/blocks/blocks/tinybird.ts @@ -0,0 +1,168 @@ +import { TinybirdIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' +import type { TinybirdResponse } from '@/tools/tinybird/types' + +export const TinybirdBlock: BlockConfig = { + type: 'tinybird', + name: 'Tinybird', + description: 'Send events and query data with Tinybird', + authMode: AuthMode.ApiKey, + longDescription: + 'Interact with Tinybird using the Events API to stream JSON or NDJSON events, or use the Query API to execute SQL queries against Pipes and Data Sources.', + docsLink: 'https://www.tinybird.co/docs/api-reference', + category: 'tools', + bgColor: '#2EF598', + icon: TinybirdIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Send Events', id: 'tinybird_events' }, + { label: 'Query', id: 'tinybird_query' }, + ], + value: () => 'tinybird_events', + }, + { + id: 'base_url', + title: 'Base URL', + type: 'short-input', + placeholder: 'https://api.tinybird.co', + required: true, + }, + { + id: 'token', + title: 'API Token', + type: 'short-input', + placeholder: 'Enter your Tinybird API token', + password: true, + required: true, + }, + // Send Events operation inputs + { + id: 'datasource', + title: 'Data Source', + type: 'short-input', + placeholder: 'my_events_datasource', + condition: { field: 'operation', value: 'tinybird_events' }, + required: true, + }, + { + id: 'data', + title: 'Data', + type: 'long-input', + placeholder: + '{"event": "click", "timestamp": "2024-01-01T12:00:00Z"}\n{"event": "view", "timestamp": "2024-01-01T12:00:01Z"}', + condition: { field: 'operation', value: 'tinybird_events' }, + required: true, + }, + { + id: 'format', + title: 'Format', + type: 'dropdown', + options: [ + { label: 'NDJSON (Newline-delimited JSON)', id: 'ndjson' }, + { label: 'JSON', id: 'json' }, + ], + value: () => 'ndjson', + condition: { field: 'operation', value: 'tinybird_events' }, + }, + { + id: 'compression', + title: 'Compression', + type: 'dropdown', + options: [ + { label: 'None', id: 'none' }, + { label: 'Gzip', id: 'gzip' }, + ], + value: () => 'none', + mode: 'advanced', + condition: { field: 'operation', value: 'tinybird_events' }, + }, + { + id: 'wait', + title: 'Wait for Acknowledgment', + type: 'dropdown', + options: [ + { label: 'No (faster, default)', id: 'false' }, + { label: 'Yes (safer retries)', id: 'true' }, + ], + value: () => 'false', + condition: { field: 'operation', value: 'tinybird_events' }, + }, + // Query operation inputs + { + id: 'query', + title: 'SQL Query', + type: 'long-input', + placeholder: 'SELECT * FROM my_pipe FORMAT JSON\nOR\nSELECT * FROM my_pipe FORMAT CSV', + condition: { field: 'operation', value: 'tinybird_query' }, + required: true, + }, + { + id: 'pipeline', + title: 'Pipeline Name', + type: 'short-input', + placeholder: 'my_pipe (optional)', + condition: { field: 'operation', value: 'tinybird_query' }, + }, + ], + tools: { + access: ['tinybird_events', 'tinybird_query'], + config: { + tool: (params) => params.operation || 'tinybird_events', + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + base_url: { type: 'string', description: 'Tinybird API base URL' }, + // Send Events inputs + datasource: { + type: 'string', + description: 'Name of the Tinybird Data Source', + }, + data: { + type: 'string', + description: 'Data to send as JSON or NDJSON string', + }, + wait: { type: 'boolean', description: 'Wait for database acknowledgment' }, + format: { + type: 'string', + description: 'Format of the events (ndjson or json)', + }, + compression: { + type: 'string', + description: 'Compression format (none or gzip)', + }, + // Query inputs + query: { type: 'string', description: 'SQL query to execute' }, + pipeline: { type: 'string', description: 'Optional pipeline name' }, + // Common + token: { type: 'string', description: 'Tinybird API Token' }, + }, + outputs: { + // Send Events outputs + successful_rows: { + type: 'number', + description: 'Number of rows successfully ingested', + }, + quarantined_rows: { + type: 'number', + description: 'Number of rows quarantined (failed validation)', + }, + // Query outputs + data: { + type: 'json', + description: + 'Query result data. FORMAT JSON: array of objects. Other formats (CSV, TSV, etc.): raw text string.', + }, + rows: { type: 'number', description: 'Number of rows returned (only with FORMAT JSON)' }, + statistics: { + type: 'json', + description: + 'Query execution statistics - elapsed time, rows read, bytes read (only with FORMAT JSON)', + }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 8a4d75121f..abea625548 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -119,6 +119,7 @@ import { SupabaseBlock } from '@/blocks/blocks/supabase' import { TavilyBlock } from '@/blocks/blocks/tavily' import { TelegramBlock } from '@/blocks/blocks/telegram' import { ThinkingBlock } from '@/blocks/blocks/thinking' +import { TinybirdBlock } from '@/blocks/blocks/tinybird' import { TranslateBlock } from '@/blocks/blocks/translate' import { TrelloBlock } from '@/blocks/blocks/trello' import { TtsBlock } from '@/blocks/blocks/tts' @@ -271,6 +272,7 @@ export const registry: Record = { tavily: TavilyBlock, telegram: TelegramBlock, thinking: ThinkingBlock, + tinybird: TinybirdBlock, translate: TranslateBlock, trello: TrelloBlock, twilio_sms: TwilioSMSBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index de0ab92021..8df89e0c1c 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1872,6 +1872,19 @@ export function TelegramIcon(props: SVGProps) { ) } +export function TinybirdIcon(props: SVGProps) { + return ( + + + + + + + + + ) +} + export function ClayIcon(props: SVGProps) { return ( @@ -3131,10 +3144,7 @@ export function ServerIcon(props: SVGProps) { strokeLinecap='round' strokeLinejoin='round' > - - - - + {/* todo */} ) } diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index dae22573d8..967d764648 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1259,6 +1259,7 @@ import { telegramSendVideoTool, } from '@/tools/telegram' import { thinkingTool } from '@/tools/thinking' +import { tinybirdEventsTool, tinybirdQueryTool } from '@/tools/tinybird' import { trelloAddCommentTool, trelloCreateCardTool, @@ -2016,6 +2017,8 @@ export const tools: Record = { apollo_email_accounts: apolloEmailAccountsTool, mistral_parser: mistralParserTool, thinking_tool: thinkingTool, + tinybird_events: tinybirdEventsTool, + tinybird_query: tinybirdQueryTool, stagehand_extract: stagehandExtractTool, stagehand_agent: stagehandAgentTool, mem0_add_memories: mem0AddMemoriesTool, diff --git a/apps/sim/tools/tinybird/events.ts b/apps/sim/tools/tinybird/events.ts new file mode 100644 index 0000000000..5dbc7657ff --- /dev/null +++ b/apps/sim/tools/tinybird/events.ts @@ -0,0 +1,127 @@ +import { createLogger } from '@sim/logger' +import type { TinybirdEventsParams, TinybirdEventsResponse } from '@/tools/tinybird/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('tinybird-events') + +export const eventsTool: ToolConfig = { + id: 'tinybird_events', + name: 'Tinybird Events', + description: + 'Send events to a Tinybird Data Source using the Events API. Supports JSON and NDJSON formats with optional compression.', + version: '1.0.0', + + params: { + base_url: { + type: 'string', + required: true, + visibility: 'user-only', + description: + 'Tinybird API base URL (e.g., https://api.tinybird.co or https://api.us-east.tinybird.co)', + }, + datasource: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Name of the Tinybird Data Source to send events to', + }, + data: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Data to send as NDJSON (newline-delimited JSON) or JSON string. Each event should be a valid JSON object.', + }, + wait: { + type: 'boolean', + required: false, + visibility: 'user-only', + description: + 'Wait for database acknowledgment before responding. Enables safer retries but introduces latency. Defaults to false.', + }, + format: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Format of the events data: "ndjson" (default) or "json"', + }, + compression: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Compression format: "none" (default) or "gzip"', + }, + token: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tinybird API Token with DATASOURCE:APPEND or DATASOURCE:CREATE scope', + }, + }, + + request: { + url: (params) => { + const baseUrl = params.base_url.endsWith('/') ? params.base_url.slice(0, -1) : params.base_url + const url = new URL(`${baseUrl}/v0/events`) + url.searchParams.set('name', params.datasource) + if (params.wait) { + url.searchParams.set('wait', 'true') + } + return url.toString() + }, + method: 'POST', + headers: (params) => { + const headers: Record = { + Authorization: `Bearer ${params.token}`, + } + + if (params.compression === 'gzip') { + headers['Content-Encoding'] = 'gzip' + } + + if (params.format === 'json') { + headers['Content-Type'] = 'application/json' + } else { + headers['Content-Type'] = 'application/x-ndjson' + } + + return headers + }, + body: (params) => params.data, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + const errorMessage = + data.error?.message || data.error || `Failed to send events (HTTP ${response.status})` + logger.error('Failed to send events to Tinybird', { status: response.status, error: data }) + throw new Error(errorMessage) + } + + logger.info('Successfully sent events to Tinybird', { + successful: data.successful_rows, + quarantined: data.quarantined_rows, + }) + + return { + success: true, + output: { + successful_rows: data.successful_rows ?? 0, + quarantined_rows: data.quarantined_rows ?? 0, + }, + } + }, + + outputs: { + successful_rows: { + type: 'number', + description: 'Number of rows successfully ingested', + }, + quarantined_rows: { + type: 'number', + description: 'Number of rows quarantined (failed validation)', + }, + }, +} diff --git a/apps/sim/tools/tinybird/index.ts b/apps/sim/tools/tinybird/index.ts new file mode 100644 index 0000000000..5eb7e6af0b --- /dev/null +++ b/apps/sim/tools/tinybird/index.ts @@ -0,0 +1,5 @@ +import { eventsTool } from '@/tools/tinybird/events' +import { queryTool } from '@/tools/tinybird/query' + +export const tinybirdEventsTool = eventsTool +export const tinybirdQueryTool = queryTool diff --git a/apps/sim/tools/tinybird/query.ts b/apps/sim/tools/tinybird/query.ts new file mode 100644 index 0000000000..eb238f9eb1 --- /dev/null +++ b/apps/sim/tools/tinybird/query.ts @@ -0,0 +1,155 @@ +import { createLogger } from '@sim/logger' +import type { TinybirdQueryParams, TinybirdQueryResponse } from '@/tools/tinybird/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('tinybird-query') + +/** + * Tinybird Query Tool + * + * Executes SQL queries against Tinybird and returns results in the format specified in the query. + * - FORMAT JSON: Returns structured data with rows/statistics metadata + * - FORMAT CSV/TSV/etc: Returns raw text string + * + * The tool automatically detects the response format based on Content-Type headers. + */ +export const queryTool: ToolConfig = { + id: 'tinybird_query', + name: 'Tinybird Query', + description: 'Execute SQL queries against Tinybird Pipes and Data Sources using the Query API.', + version: '1.0.0', + + params: { + base_url: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tinybird API base URL (e.g., https://api.tinybird.co)', + }, + query: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'SQL query to execute. Specify your desired output format (e.g., FORMAT JSON, FORMAT CSV, FORMAT TSV). JSON format provides structured data, while other formats return raw text.', + }, + pipeline: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional pipe name. When provided, enables SELECT * FROM _ syntax', + }, + token: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tinybird API Token with PIPE:READ scope', + }, + }, + + request: { + url: (params) => { + const baseUrl = params.base_url.endsWith('/') ? params.base_url.slice(0, -1) : params.base_url + return `${baseUrl}/v0/sql` + }, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Bearer ${params.token}`, + }), + body: (params) => { + const searchParams = new URLSearchParams() + searchParams.set('q', params.query) + if (params.pipeline) { + searchParams.set('pipeline', params.pipeline) + } + return searchParams.toString() + }, + }, + + transformResponse: async (response: Response) => { + const responseText = await response.text() + const contentType = response.headers.get('content-type') || '' + + if (!response.ok) { + // Try to parse error as JSON first + let errorMessage = `Failed to execute query (HTTP ${response.status})` + try { + const errorData = JSON.parse(responseText) + errorMessage = errorData.error?.message || errorData.error || errorMessage + } catch { + // If not JSON, use raw text + errorMessage = responseText || errorMessage + } + logger.error('Failed to execute Tinybird query', { + status: response.status, + error: errorMessage, + }) + throw new Error(errorMessage) + } + + // Check if response is JSON based on content-type or try parsing + const isJson = contentType.includes('application/json') || contentType.includes('text/json') + + if (isJson) { + try { + const data = JSON.parse(responseText) + logger.info('Successfully executed Tinybird query (JSON)', { + rows: data.rows, + elapsed: data.statistics?.elapsed, + }) + + return { + success: true, + output: { + data: data.data || [], + rows: data.rows || 0, + statistics: data.statistics + ? { + elapsed: data.statistics.elapsed, + rows_read: data.statistics.rows_read, + bytes_read: data.statistics.bytes_read, + } + : undefined, + }, + } + } catch (parseError) { + logger.error('Failed to parse JSON response', { + contentType, + parseError: parseError instanceof Error ? parseError.message : String(parseError), + }) + throw new Error( + `Invalid JSON response: ${parseError instanceof Error ? parseError.message : 'Parse error'}` + ) + } + } + + // For non-JSON formats (CSV, TSV, etc.), return as raw text + logger.info('Successfully executed Tinybird query (non-JSON)', { contentType }) + return { + success: true, + output: { + data: responseText, + rows: undefined, + statistics: undefined, + }, + } + }, + + outputs: { + data: { + type: 'json', + description: + 'Query result data. For FORMAT JSON: array of objects. For other formats (CSV, TSV, etc.): raw text string.', + }, + rows: { + type: 'number', + description: 'Number of rows returned (only available with FORMAT JSON)', + }, + statistics: { + type: 'json', + description: + 'Query execution statistics - elapsed time, rows read, bytes read (only available with FORMAT JSON)', + }, + }, +} diff --git a/apps/sim/tools/tinybird/types.ts b/apps/sim/tools/tinybird/types.ts new file mode 100644 index 0000000000..2e681bf411 --- /dev/null +++ b/apps/sim/tools/tinybird/types.ts @@ -0,0 +1,59 @@ +import type { ToolResponse } from '@/tools/types' + +/** + * Base parameters for Tinybird API tools + */ +export interface TinybirdBaseParams { + token: string +} + +/** + * Parameters for sending events to Tinybird + */ +export interface TinybirdEventsParams extends TinybirdBaseParams { + base_url: string + datasource: string + data: string + wait?: boolean + format?: 'ndjson' | 'json' + compression?: 'none' | 'gzip' +} + +/** + * Response from sending events to Tinybird + */ +export interface TinybirdEventsResponse extends ToolResponse { + output: { + successful_rows: number + quarantined_rows: number + } +} + +/** + * Parameters for querying Tinybird + */ +export interface TinybirdQueryParams extends TinybirdBaseParams { + base_url: string + query: string + pipeline?: string +} + +/** + * Response from querying Tinybird + */ +export interface TinybirdQueryResponse extends ToolResponse { + output: { + data: unknown[] | string + rows?: number + statistics?: { + elapsed: number + rows_read: number + bytes_read: number + } + } +} + +/** + * Union type for all possible Tinybird responses + */ +export type TinybirdResponse = TinybirdEventsResponse | TinybirdQueryResponse From 217d4a9859e286a4f68c1ffc515c997b85a41408 Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:49:08 -0800 Subject: [PATCH 03/10] Update apps/sim/components/icons.tsx Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- apps/sim/components/icons.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 8df89e0c1c..d3e3efac62 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -3144,7 +3144,10 @@ export function ServerIcon(props: SVGProps) { strokeLinecap='round' strokeLinejoin='round' > - {/* todo */} + + + + ) } From 186c54fe874f2668ff05f812dec29747445d036d Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya Date: Mon, 12 Jan 2026 13:07:54 -0800 Subject: [PATCH 04/10] code tag --- apps/sim/blocks/blocks/tinybird.ts | 4 ++-- bun.lock | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/sim/blocks/blocks/tinybird.ts b/apps/sim/blocks/blocks/tinybird.ts index fcd01c0a94..fa52fad2ae 100644 --- a/apps/sim/blocks/blocks/tinybird.ts +++ b/apps/sim/blocks/blocks/tinybird.ts @@ -52,7 +52,7 @@ export const TinybirdBlock: BlockConfig = { { id: 'data', title: 'Data', - type: 'long-input', + type: 'code', placeholder: '{"event": "click", "timestamp": "2024-01-01T12:00:00Z"}\n{"event": "view", "timestamp": "2024-01-01T12:00:01Z"}', condition: { field: 'operation', value: 'tinybird_events' }, @@ -96,7 +96,7 @@ export const TinybirdBlock: BlockConfig = { { id: 'query', title: 'SQL Query', - type: 'long-input', + type: 'code', placeholder: 'SELECT * FROM my_pipe FORMAT JSON\nOR\nSELECT * FROM my_pipe FORMAT CSV', condition: { field: 'operation', value: 'tinybird_query' }, required: true, diff --git a/bun.lock b/bun.lock index ad4825181d..c08c899810 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "simstudio", From a1ef5abd0030b60c0e5a3ff59b73a65d899b14b1 Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya Date: Mon, 12 Jan 2026 13:18:13 -0800 Subject: [PATCH 05/10] updates --- apps/sim/blocks/blocks/tinybird.ts | 46 ++++++++++++++++++++++++++---- apps/sim/tools/tinybird/events.ts | 11 +++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/apps/sim/blocks/blocks/tinybird.ts b/apps/sim/blocks/blocks/tinybird.ts index fa52fad2ae..b1a48143a7 100644 --- a/apps/sim/blocks/blocks/tinybird.ts +++ b/apps/sim/blocks/blocks/tinybird.ts @@ -84,12 +84,9 @@ export const TinybirdBlock: BlockConfig = { { id: 'wait', title: 'Wait for Acknowledgment', - type: 'dropdown', - options: [ - { label: 'No (faster, default)', id: 'false' }, - { label: 'Yes (safer retries)', id: 'true' }, - ], + type: 'switch', value: () => 'false', + mode: 'advanced', condition: { field: 'operation', value: 'tinybird_events' }, }, // Query operation inputs @@ -113,6 +110,45 @@ export const TinybirdBlock: BlockConfig = { access: ['tinybird_events', 'tinybird_query'], config: { tool: (params) => params.operation || 'tinybird_events', + params: (params) => { + const operation = params.operation || 'tinybird_events' + const result: Record = { + base_url: params.base_url, + token: params.token, + } + + if (operation === 'tinybird_events') { + // Send Events operation + if (!params.datasource) { + throw new Error('Data Source is required for Send Events operation') + } + if (!params.data) { + throw new Error('Data is required for Send Events operation') + } + + result.datasource = params.datasource + result.data = params.data + result.format = params.format || 'ndjson' + result.compression = params.compression || 'none' + + // Convert wait from string to boolean + if (params.wait !== undefined) { + result.wait = params.wait === 'true' || params.wait === true || params.wait === 'True' + } + } else if (operation === 'tinybird_query') { + // Query operation + if (!params.query) { + throw new Error('SQL Query is required for Query operation') + } + + result.query = params.query + if (params.pipeline) { + result.pipeline = params.pipeline + } + } + + return result + }, }, }, inputs: { diff --git a/apps/sim/tools/tinybird/events.ts b/apps/sim/tools/tinybird/events.ts index 5dbc7657ff..c56d8cb5c5 100644 --- a/apps/sim/tools/tinybird/events.ts +++ b/apps/sim/tools/tinybird/events.ts @@ -1,3 +1,4 @@ +import { gzipSync } from 'zlib' import { createLogger } from '@sim/logger' import type { TinybirdEventsParams, TinybirdEventsResponse } from '@/tools/tinybird/types' import type { ToolConfig } from '@/tools/types' @@ -8,7 +9,7 @@ export const eventsTool: ToolConfig params.data, + body: (params) => { + const data = params.data + if (params.compression === 'gzip') { + return gzipSync(Buffer.from(data, 'utf-8')) + } + return data + }, }, transformResponse: async (response: Response) => { From 0f686c8e269a7efc633f73e1b3db71a9714804e6 Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:31:03 -0800 Subject: [PATCH 06/10] Update apps/sim/blocks/blocks/tinybird.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- apps/sim/blocks/blocks/tinybird.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/sim/blocks/blocks/tinybird.ts b/apps/sim/blocks/blocks/tinybird.ts index b1a48143a7..948709f7d8 100644 --- a/apps/sim/blocks/blocks/tinybird.ts +++ b/apps/sim/blocks/blocks/tinybird.ts @@ -131,9 +131,11 @@ export const TinybirdBlock: BlockConfig = { result.format = params.format || 'ndjson' result.compression = params.compression || 'none' + // Convert wait from string to boolean // Convert wait from string to boolean if (params.wait !== undefined) { - result.wait = params.wait === 'true' || params.wait === true || params.wait === 'True' + const waitValue = typeof params.wait === 'string' ? params.wait.toLowerCase() : params.wait + result.wait = waitValue === 'true' || waitValue === true } } else if (operation === 'tinybird_query') { // Query operation From 254f799e1f87f8e0b99673a4d7658971a7ec6dc0 Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:33:22 -0800 Subject: [PATCH 07/10] Update apps/sim/tools/tinybird/query.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- apps/sim/tools/tinybird/query.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/tools/tinybird/query.ts b/apps/sim/tools/tinybird/query.ts index eb238f9eb1..ccecbc98dd 100644 --- a/apps/sim/tools/tinybird/query.ts +++ b/apps/sim/tools/tinybird/query.ts @@ -78,8 +78,8 @@ export const queryTool: ToolConfig = const errorData = JSON.parse(responseText) errorMessage = errorData.error?.message || errorData.error || errorMessage } catch { - // If not JSON, use raw text - errorMessage = responseText || errorMessage + // If not JSON, use raw text (truncated if too long) + errorMessage = responseText ? responseText.substring(0, 500) : errorMessage } logger.error('Failed to execute Tinybird query', { status: response.status, From c9b0c2c67bd96f10f5c5dc1a52e68206319ce291 Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya Date: Mon, 12 Jan 2026 14:58:18 -0800 Subject: [PATCH 08/10] docs --- apps/docs/components/icons.tsx | 13 ++++ apps/docs/components/ui/icon-mapping.ts | 2 + .../content/docs/en/tools/google_drive.mdx | 4 +- apps/docs/content/docs/en/tools/meta.json | 1 + apps/docs/content/docs/en/tools/tinybird.mdx | 70 +++++++++++++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 apps/docs/content/docs/en/tools/tinybird.mdx diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index de0ab92021..d3e3efac62 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -1872,6 +1872,19 @@ export function TelegramIcon(props: SVGProps) { ) } +export function TinybirdIcon(props: SVGProps) { + return ( + + + + + + + + + ) +} + export function ClayIcon(props: SVGProps) { return ( diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 60d0351e57..83d412198e 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -105,6 +105,7 @@ import { SupabaseIcon, TavilyIcon, TelegramIcon, + TinybirdIcon, TranslateIcon, TrelloIcon, TTSIcon, @@ -227,6 +228,7 @@ export const blockTypeToIconMap: Record = { tavily: TavilyIcon, telegram: TelegramIcon, thinking: BrainIcon, + tinybird: TinybirdIcon, translate: TranslateIcon, trello: TrelloIcon, tts: TTSIcon, diff --git a/apps/docs/content/docs/en/tools/google_drive.mdx b/apps/docs/content/docs/en/tools/google_drive.mdx index 54b3ed3bc3..3bb75fa16a 100644 --- a/apps/docs/content/docs/en/tools/google_drive.mdx +++ b/apps/docs/content/docs/en/tools/google_drive.mdx @@ -96,13 +96,13 @@ Download a file from Google Drive with complete metadata (exports Google Workspa | `fileId` | string | Yes | The ID of the file to download | | `mimeType` | string | No | The MIME type to export Google Workspace files to \(optional\) | | `fileName` | string | No | Optional filename override | -| `includeRevisions` | boolean | No | Whether to include revision history in the metadata \(default: true\) | +| `includeRevisions` | boolean | No | Whether to include revision history in the metadata \(default: true, returns first 100 revisions\) | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `file` | object | Downloaded file stored in execution files | +| `file` | object | Downloaded file data | ### `google_drive_list` diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 8a2bca881a..c4b0614261 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -102,6 +102,7 @@ "tavily", "telegram", "thinking", + "tinybird", "translate", "trello", "tts", diff --git a/apps/docs/content/docs/en/tools/tinybird.mdx b/apps/docs/content/docs/en/tools/tinybird.mdx new file mode 100644 index 0000000000..9da20cce93 --- /dev/null +++ b/apps/docs/content/docs/en/tools/tinybird.mdx @@ -0,0 +1,70 @@ +--- +title: Tinybird +description: Send events and query data with Tinybird +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Interact with Tinybird using the Events API to stream JSON or NDJSON events, or use the Query API to execute SQL queries against Pipes and Data Sources. + + + +## Tools + +### `tinybird_events` + +Send events to a Tinybird Data Source using the Events API. Supports JSON and NDJSON formats with optional gzip compression. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `base_url` | string | Yes | Tinybird API base URL \(e.g., https://api.tinybird.co or https://api.us-east.tinybird.co\) | +| `datasource` | string | Yes | Name of the Tinybird Data Source to send events to | +| `data` | string | Yes | Data to send as NDJSON \(newline-delimited JSON\) or JSON string. Each event should be a valid JSON object. | +| `wait` | boolean | No | Wait for database acknowledgment before responding. Enables safer retries but introduces latency. Defaults to false. | +| `format` | string | No | Format of the events data: "ndjson" \(default\) or "json" | +| `compression` | string | No | Compression format: "none" \(default\) or "gzip" | +| `token` | string | Yes | Tinybird API Token with DATASOURCE:APPEND or DATASOURCE:CREATE scope | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `successful_rows` | number | Number of rows successfully ingested | +| `quarantined_rows` | number | Number of rows quarantined \(failed validation\) | + +### `tinybird_query` + +Execute SQL queries against Tinybird Pipes and Data Sources using the Query API. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `base_url` | string | Yes | Tinybird API base URL \(e.g., https://api.tinybird.co\) | +| `query` | string | Yes | SQL query to execute. Specify your desired output format \(e.g., FORMAT JSON, FORMAT CSV, FORMAT TSV\). JSON format provides structured data, while other formats return raw text. | +| `pipeline` | string | No | Optional pipe name. When provided, enables SELECT * FROM _ syntax | +| `token` | string | Yes | Tinybird API Token with PIPE:READ scope | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Query result data. For FORMAT JSON: array of objects. For other formats \(CSV, TSV, etc.\): raw text string. | +| `rows` | number | Number of rows returned \(only available with FORMAT JSON\) | +| `statistics` | json | Query execution statistics - elapsed time, rows read, bytes read \(only available with FORMAT JSON\) | + + + +## Notes + +- Category: `tools` +- Type: `tinybird` From c348dee910039087f305945fe3c46ecfa97be467 Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya Date: Tue, 13 Jan 2026 10:59:28 -0800 Subject: [PATCH 09/10] updates --- apps/sim/tools/tinybird/events.ts | 8 +------- apps/sim/tools/tinybird/query.ts | 18 +----------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/apps/sim/tools/tinybird/events.ts b/apps/sim/tools/tinybird/events.ts index c56d8cb5c5..6de9137044 100644 --- a/apps/sim/tools/tinybird/events.ts +++ b/apps/sim/tools/tinybird/events.ts @@ -11,6 +11,7 @@ export const eventsTool: ToolConfig { const data = await response.json() - if (!response.ok) { - const errorMessage = - data.error?.message || data.error || `Failed to send events (HTTP ${response.status})` - logger.error('Failed to send events to Tinybird', { status: response.status, error: data }) - throw new Error(errorMessage) - } - logger.info('Successfully sent events to Tinybird', { successful: data.successful_rows, quarantined: data.quarantined_rows, diff --git a/apps/sim/tools/tinybird/query.ts b/apps/sim/tools/tinybird/query.ts index ccecbc98dd..7046f9a67d 100644 --- a/apps/sim/tools/tinybird/query.ts +++ b/apps/sim/tools/tinybird/query.ts @@ -18,6 +18,7 @@ export const queryTool: ToolConfig = name: 'Tinybird Query', description: 'Execute SQL queries against Tinybird Pipes and Data Sources using the Query API.', version: '1.0.0', + errorExtractor: 'nested-error-object', params: { base_url: { @@ -71,23 +72,6 @@ export const queryTool: ToolConfig = const responseText = await response.text() const contentType = response.headers.get('content-type') || '' - if (!response.ok) { - // Try to parse error as JSON first - let errorMessage = `Failed to execute query (HTTP ${response.status})` - try { - const errorData = JSON.parse(responseText) - errorMessage = errorData.error?.message || errorData.error || errorMessage - } catch { - // If not JSON, use raw text (truncated if too long) - errorMessage = responseText ? responseText.substring(0, 500) : errorMessage - } - logger.error('Failed to execute Tinybird query', { - status: response.status, - error: errorMessage, - }) - throw new Error(errorMessage) - } - // Check if response is JSON based on content-type or try parsing const isJson = contentType.includes('application/json') || contentType.includes('text/json') From 897c03392c2e5acd794ae4b689f8977e277d18a8 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Wed, 14 Jan 2026 00:20:59 -0800 Subject: [PATCH 10/10] fix lint --- apps/sim/blocks/blocks/tinybird.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/sim/blocks/blocks/tinybird.ts b/apps/sim/blocks/blocks/tinybird.ts index 948709f7d8..436543de76 100644 --- a/apps/sim/blocks/blocks/tinybird.ts +++ b/apps/sim/blocks/blocks/tinybird.ts @@ -134,7 +134,8 @@ export const TinybirdBlock: BlockConfig = { // Convert wait from string to boolean // Convert wait from string to boolean if (params.wait !== undefined) { - const waitValue = typeof params.wait === 'string' ? params.wait.toLowerCase() : params.wait + const waitValue = + typeof params.wait === 'string' ? params.wait.toLowerCase() : params.wait result.wait = waitValue === 'true' || waitValue === true } } else if (operation === 'tinybird_query') {