Skip to content

Commit 3907427

Browse files
committed
updates
1 parent 7ffc11a commit 3907427

File tree

8 files changed

+533
-4
lines changed

8 files changed

+533
-4
lines changed

apps/sim/blocks/blocks/tinybird.ts

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { TinybirdIcon } from '@/components/icons'
2+
import type { BlockConfig } from '@/blocks/types'
3+
import { AuthMode } from '@/blocks/types'
4+
import type { TinybirdResponse } from '@/tools/tinybird/types'
5+
6+
export const TinybirdBlock: BlockConfig<TinybirdResponse> = {
7+
type: 'tinybird',
8+
name: 'Tinybird',
9+
description: 'Send events and query data with Tinybird',
10+
authMode: AuthMode.ApiKey,
11+
longDescription:
12+
'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.',
13+
docsLink: 'https://www.tinybird.co/docs/api-reference',
14+
category: 'tools',
15+
bgColor: '#2EF598',
16+
icon: TinybirdIcon,
17+
subBlocks: [
18+
{
19+
id: 'operation',
20+
title: 'Operation',
21+
type: 'dropdown',
22+
options: [
23+
{ label: 'Send Events', id: 'tinybird_events' },
24+
{ label: 'Query', id: 'tinybird_query' },
25+
],
26+
value: () => 'tinybird_events',
27+
},
28+
{
29+
id: 'base_url',
30+
title: 'Base URL',
31+
type: 'short-input',
32+
placeholder: 'https://api.tinybird.co',
33+
required: true,
34+
},
35+
{
36+
id: 'token',
37+
title: 'API Token',
38+
type: 'short-input',
39+
placeholder: 'Enter your Tinybird API token',
40+
password: true,
41+
required: true,
42+
},
43+
// Send Events operation inputs
44+
{
45+
id: 'datasource',
46+
title: 'Data Source',
47+
type: 'short-input',
48+
placeholder: 'my_events_datasource',
49+
condition: { field: 'operation', value: 'tinybird_events' },
50+
required: true,
51+
},
52+
{
53+
id: 'data',
54+
title: 'Data',
55+
type: 'long-input',
56+
placeholder:
57+
'{"event": "click", "timestamp": "2024-01-01T12:00:00Z"}\n{"event": "view", "timestamp": "2024-01-01T12:00:01Z"}',
58+
condition: { field: 'operation', value: 'tinybird_events' },
59+
required: true,
60+
},
61+
{
62+
id: 'format',
63+
title: 'Format',
64+
type: 'dropdown',
65+
options: [
66+
{ label: 'NDJSON (Newline-delimited JSON)', id: 'ndjson' },
67+
{ label: 'JSON', id: 'json' },
68+
],
69+
value: () => 'ndjson',
70+
condition: { field: 'operation', value: 'tinybird_events' },
71+
},
72+
{
73+
id: 'compression',
74+
title: 'Compression',
75+
type: 'dropdown',
76+
options: [
77+
{ label: 'None', id: 'none' },
78+
{ label: 'Gzip', id: 'gzip' },
79+
],
80+
value: () => 'none',
81+
mode: 'advanced',
82+
condition: { field: 'operation', value: 'tinybird_events' },
83+
},
84+
{
85+
id: 'wait',
86+
title: 'Wait for Acknowledgment',
87+
type: 'dropdown',
88+
options: [
89+
{ label: 'No (faster, default)', id: 'false' },
90+
{ label: 'Yes (safer retries)', id: 'true' },
91+
],
92+
value: () => 'false',
93+
condition: { field: 'operation', value: 'tinybird_events' },
94+
},
95+
// Query operation inputs
96+
{
97+
id: 'query',
98+
title: 'SQL Query',
99+
type: 'long-input',
100+
placeholder: 'SELECT * FROM my_pipe FORMAT JSON\nOR\nSELECT * FROM my_pipe FORMAT CSV',
101+
condition: { field: 'operation', value: 'tinybird_query' },
102+
required: true,
103+
},
104+
{
105+
id: 'pipeline',
106+
title: 'Pipeline Name',
107+
type: 'short-input',
108+
placeholder: 'my_pipe (optional)',
109+
condition: { field: 'operation', value: 'tinybird_query' },
110+
},
111+
],
112+
tools: {
113+
access: ['tinybird_events', 'tinybird_query'],
114+
config: {
115+
tool: (params) => params.operation || 'tinybird_events',
116+
},
117+
},
118+
inputs: {
119+
operation: { type: 'string', description: 'Operation to perform' },
120+
base_url: { type: 'string', description: 'Tinybird API base URL' },
121+
// Send Events inputs
122+
datasource: {
123+
type: 'string',
124+
description: 'Name of the Tinybird Data Source',
125+
},
126+
data: {
127+
type: 'string',
128+
description: 'Data to send as JSON or NDJSON string',
129+
},
130+
wait: { type: 'boolean', description: 'Wait for database acknowledgment' },
131+
format: {
132+
type: 'string',
133+
description: 'Format of the events (ndjson or json)',
134+
},
135+
compression: {
136+
type: 'string',
137+
description: 'Compression format (none or gzip)',
138+
},
139+
// Query inputs
140+
query: { type: 'string', description: 'SQL query to execute' },
141+
pipeline: { type: 'string', description: 'Optional pipeline name' },
142+
// Common
143+
token: { type: 'string', description: 'Tinybird API Token' },
144+
},
145+
outputs: {
146+
// Send Events outputs
147+
successful_rows: {
148+
type: 'number',
149+
description: 'Number of rows successfully ingested',
150+
},
151+
quarantined_rows: {
152+
type: 'number',
153+
description: 'Number of rows quarantined (failed validation)',
154+
},
155+
// Query outputs
156+
data: {
157+
type: 'json',
158+
description:
159+
'Query result data. FORMAT JSON: array of objects. Other formats (CSV, TSV, etc.): raw text string.',
160+
},
161+
rows: { type: 'number', description: 'Number of rows returned (only with FORMAT JSON)' },
162+
statistics: {
163+
type: 'json',
164+
description:
165+
'Query execution statistics - elapsed time, rows read, bytes read (only with FORMAT JSON)',
166+
},
167+
},
168+
}

apps/sim/blocks/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ import { SupabaseBlock } from '@/blocks/blocks/supabase'
119119
import { TavilyBlock } from '@/blocks/blocks/tavily'
120120
import { TelegramBlock } from '@/blocks/blocks/telegram'
121121
import { ThinkingBlock } from '@/blocks/blocks/thinking'
122+
import { TinybirdBlock } from '@/blocks/blocks/tinybird'
122123
import { TranslateBlock } from '@/blocks/blocks/translate'
123124
import { TrelloBlock } from '@/blocks/blocks/trello'
124125
import { TtsBlock } from '@/blocks/blocks/tts'
@@ -271,6 +272,7 @@ export const registry: Record<string, BlockConfig> = {
271272
tavily: TavilyBlock,
272273
telegram: TelegramBlock,
273274
thinking: ThinkingBlock,
275+
tinybird: TinybirdBlock,
274276
translate: TranslateBlock,
275277
trello: TrelloBlock,
276278
twilio_sms: TwilioSMSBlock,

apps/sim/components/icons.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,19 @@ export function TelegramIcon(props: SVGProps<SVGSVGElement>) {
18721872
)
18731873
}
18741874

1875+
export function TinybirdIcon(props: SVGProps<SVGSVGElement>) {
1876+
return (
1877+
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'>
1878+
<rect x='0' y='0' width='24' height='24' fill='#2EF598' rx='6' />
1879+
<g transform='translate(2, 2) scale(0.833)'>
1880+
<path d='M25 2.64 17.195.5 14.45 6.635z' fill='#1E7F63' />
1881+
<path d='M17.535 17.77 10.39 15.215 6.195 25.5z' fill='#1E7F63' />
1882+
<path d='M0 11.495 17.535 17.77 20.41 4.36z' fill='#1F2437' />
1883+
</g>
1884+
</svg>
1885+
)
1886+
}
1887+
18751888
export function ClayIcon(props: SVGProps<SVGSVGElement>) {
18761889
return (
18771890
<svg {...props} xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 400 400'>
@@ -3131,10 +3144,7 @@ export function ServerIcon(props: SVGProps<SVGSVGElement>) {
31313144
strokeLinecap='round'
31323145
strokeLinejoin='round'
31333146
>
3134-
<rect width='20' height='8' x='2' y='2' rx='2' ry='2' />
3135-
<rect width='20' height='8' x='2' y='14' rx='2' ry='2' />
3136-
<line x1='6' x2='6.01' y1='6' y2='6' />
3137-
<line x1='6' x2='6.01' y1='18' y2='18' />
3147+
{/* todo */}
31383148
</svg>
31393149
)
31403150
}

apps/sim/tools/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,7 @@ import {
12591259
telegramSendVideoTool,
12601260
} from '@/tools/telegram'
12611261
import { thinkingTool } from '@/tools/thinking'
1262+
import { tinybirdEventsTool, tinybirdQueryTool } from '@/tools/tinybird'
12621263
import {
12631264
trelloAddCommentTool,
12641265
trelloCreateCardTool,
@@ -2016,6 +2017,8 @@ export const tools: Record<string, ToolConfig> = {
20162017
apollo_email_accounts: apolloEmailAccountsTool,
20172018
mistral_parser: mistralParserTool,
20182019
thinking_tool: thinkingTool,
2020+
tinybird_events: tinybirdEventsTool,
2021+
tinybird_query: tinybirdQueryTool,
20192022
stagehand_extract: stagehandExtractTool,
20202023
stagehand_agent: stagehandAgentTool,
20212024
mem0_add_memories: mem0AddMemoriesTool,

apps/sim/tools/tinybird/events.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { createLogger } from '@sim/logger'
2+
import type { TinybirdEventsParams, TinybirdEventsResponse } from '@/tools/tinybird/types'
3+
import type { ToolConfig } from '@/tools/types'
4+
5+
const logger = createLogger('tinybird-events')
6+
7+
export const eventsTool: ToolConfig<TinybirdEventsParams, TinybirdEventsResponse> = {
8+
id: 'tinybird_events',
9+
name: 'Tinybird Events',
10+
description:
11+
'Send events to a Tinybird Data Source using the Events API. Supports JSON and NDJSON formats with optional compression.',
12+
version: '1.0.0',
13+
14+
params: {
15+
base_url: {
16+
type: 'string',
17+
required: true,
18+
visibility: 'user-only',
19+
description:
20+
'Tinybird API base URL (e.g., https://api.tinybird.co or https://api.us-east.tinybird.co)',
21+
},
22+
datasource: {
23+
type: 'string',
24+
required: true,
25+
visibility: 'user-or-llm',
26+
description: 'Name of the Tinybird Data Source to send events to',
27+
},
28+
data: {
29+
type: 'string',
30+
required: true,
31+
visibility: 'user-or-llm',
32+
description:
33+
'Data to send as NDJSON (newline-delimited JSON) or JSON string. Each event should be a valid JSON object.',
34+
},
35+
wait: {
36+
type: 'boolean',
37+
required: false,
38+
visibility: 'user-only',
39+
description:
40+
'Wait for database acknowledgment before responding. Enables safer retries but introduces latency. Defaults to false.',
41+
},
42+
format: {
43+
type: 'string',
44+
required: false,
45+
visibility: 'user-only',
46+
description: 'Format of the events data: "ndjson" (default) or "json"',
47+
},
48+
compression: {
49+
type: 'string',
50+
required: false,
51+
visibility: 'user-only',
52+
description: 'Compression format: "none" (default) or "gzip"',
53+
},
54+
token: {
55+
type: 'string',
56+
required: true,
57+
visibility: 'user-only',
58+
description: 'Tinybird API Token with DATASOURCE:APPEND or DATASOURCE:CREATE scope',
59+
},
60+
},
61+
62+
request: {
63+
url: (params) => {
64+
const baseUrl = params.base_url.endsWith('/') ? params.base_url.slice(0, -1) : params.base_url
65+
const url = new URL(`${baseUrl}/v0/events`)
66+
url.searchParams.set('name', params.datasource)
67+
if (params.wait) {
68+
url.searchParams.set('wait', 'true')
69+
}
70+
return url.toString()
71+
},
72+
method: 'POST',
73+
headers: (params) => {
74+
const headers: Record<string, string> = {
75+
Authorization: `Bearer ${params.token}`,
76+
}
77+
78+
if (params.compression === 'gzip') {
79+
headers['Content-Encoding'] = 'gzip'
80+
}
81+
82+
if (params.format === 'json') {
83+
headers['Content-Type'] = 'application/json'
84+
} else {
85+
headers['Content-Type'] = 'application/x-ndjson'
86+
}
87+
88+
return headers
89+
},
90+
body: (params) => params.data,
91+
},
92+
93+
transformResponse: async (response: Response) => {
94+
const data = await response.json()
95+
96+
if (!response.ok) {
97+
const errorMessage =
98+
data.error?.message || data.error || `Failed to send events (HTTP ${response.status})`
99+
logger.error('Failed to send events to Tinybird', { status: response.status, error: data })
100+
throw new Error(errorMessage)
101+
}
102+
103+
logger.info('Successfully sent events to Tinybird', {
104+
successful: data.successful_rows,
105+
quarantined: data.quarantined_rows,
106+
})
107+
108+
return {
109+
success: true,
110+
output: {
111+
successful_rows: data.successful_rows ?? 0,
112+
quarantined_rows: data.quarantined_rows ?? 0,
113+
},
114+
}
115+
},
116+
117+
outputs: {
118+
successful_rows: {
119+
type: 'number',
120+
description: 'Number of rows successfully ingested',
121+
},
122+
quarantined_rows: {
123+
type: 'number',
124+
description: 'Number of rows quarantined (failed validation)',
125+
},
126+
},
127+
}

apps/sim/tools/tinybird/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { eventsTool } from '@/tools/tinybird/events'
2+
import { queryTool } from '@/tools/tinybird/query'
3+
4+
export const tinybirdEventsTool = eventsTool
5+
export const tinybirdQueryTool = queryTool

0 commit comments

Comments
 (0)