tinybird 0.0.1.dev252__py3-none-any.whl → 0.0.1.dev254__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tinybird might be problematic. Click here for more details.

tinybird/tb/__cli__.py CHANGED
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/forward/commands'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '0.0.1.dev252'
8
- __revision__ = 'd70cad6'
7
+ __version__ = '0.0.1.dev254'
8
+ __revision__ = '269e345'
@@ -30,22 +30,24 @@ from tinybird.tb.modules.agent.memory import clear_history
30
30
  from tinybird.tb.modules.agent.models import create_model, model_costs
31
31
  from tinybird.tb.modules.agent.prompts import (
32
32
  datafile_instructions,
33
+ endpoint_optimization_instructions,
33
34
  plan_instructions,
34
35
  resources_prompt,
35
36
  sql_instructions,
36
37
  )
37
- from tinybird.tb.modules.agent.tools.append import append
38
+ from tinybird.tb.modules.agent.tools.analyze import analyze_file, analyze_url
39
+ from tinybird.tb.modules.agent.tools.append import append_file, append_url
38
40
  from tinybird.tb.modules.agent.tools.build import build
39
41
  from tinybird.tb.modules.agent.tools.create_datafile import create_datafile
40
42
  from tinybird.tb.modules.agent.tools.deploy import deploy
41
43
  from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
42
- from tinybird.tb.modules.agent.tools.explore import explore_data
44
+ from tinybird.tb.modules.agent.tools.execute_query import execute_query
43
45
  from tinybird.tb.modules.agent.tools.get_endpoint_stats import get_endpoint_stats
44
46
  from tinybird.tb.modules.agent.tools.get_openapi_definition import get_openapi_definition
45
47
  from tinybird.tb.modules.agent.tools.mock import mock
46
48
  from tinybird.tb.modules.agent.tools.plan import plan
47
49
  from tinybird.tb.modules.agent.tools.preview_datafile import preview_datafile
48
- from tinybird.tb.modules.agent.tools.read_fixture_data import read_fixture_data
50
+ from tinybird.tb.modules.agent.tools.request_endpoint import request_endpoint
49
51
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_input
50
52
  from tinybird.tb.modules.build_common import process as build_process
51
53
  from tinybird.tb.modules.common import _analyze, _get_tb_client
@@ -104,18 +106,21 @@ IMPORTANT: DO NOT ADD ANY COMMENTS unless asked by the user.
104
106
 
105
107
  # Tools
106
108
  You have access to the following tools:
107
- 1. `explore_data` - Explore data in the current workspace
108
- 2. `preview_datafile` - Preview the content of a datafile (datasource, endpoint, materialized, sink, copy, connection).
109
- 3. `create_datafile` - Create a file in the project folder. Confirmation will be asked by the tool before creating the file.
110
- 4. `plan` - Plan the creation or update of resources.
111
- 5. `build` - Build the project.
112
- 6. `deploy` - Deploy the project to Tinybird Cloud.
113
- 7. `deploy_check` - Check if the project can be deployed to Tinybird Cloud before deploying it.
114
- 8. `mock` - Create mock data for a landing datasource.
115
- 9. `read_fixture_data` - Read a fixture data file present in the project folder.
116
- 10. `append` - Append existing fixture to a datasource.
109
+ 1. `preview_datafile` - Preview the content of a datafile (datasource, endpoint, materialized, sink, copy, connection).
110
+ 2. `create_datafile` - Create a file in the project folder. Confirmation will be asked by the tool before creating the file.
111
+ 3. `plan` - Plan the creation or update of resources.
112
+ 4. `build` - Build the project.
113
+ 5. `deploy` - Deploy the project to Tinybird Cloud.
114
+ 6. `deploy_check` - Check if the project can be deployed to Tinybird Cloud before deploying it.
115
+ 7. `mock` - Create mock data for a landing datasource.
116
+ 8. `analyze_file` - Analyze the content of a fixture file present in the project folder.
117
+ 9. `analyze_url` - Analyze the content of an external url.
118
+ 9. `append_file` - Append a file present in the project to a datasource.
119
+ 10. `append_url` - Append an external url to a datasource.
117
120
  11. `get_endpoint_stats` - Get metrics of the requests to an endpoint.
118
121
  12. `get_openapi_definition` - Get the OpenAPI definition for all endpoints that are built/deployed to Tinybird Cloud or Local.
122
+ 13. `execute_query` - Execute a query against Tinybird Cloud or Local.
123
+ 13. `request_endpoint` - Request an endpoint against Tinybird Cloud or Local.
119
124
 
120
125
  # When creating or updating datafiles:
121
126
  1. Use `plan` tool to plan the creation or update of resources.
@@ -131,23 +136,20 @@ You have access to the following tools:
131
136
  - If the user does not specify anything about the desired schema, create a schema like this:
132
137
  SCHEMA >
133
138
  `data` String `json:$`
139
+
134
140
  - Use always json paths with .ndjson files.
135
141
 
136
142
  # When user wants to optimize an endpoint:
137
- First check if the query is optimized. E.g is filtering by a column present in the sorting key.
138
- Avoid when possible to not update the landing datasource.
139
- Check endpoint stats to analyze how the endpoint is performing. Use `get_endpoint_stats` tool to get the stats.
140
- When your data is in Tinybird, you can create intermediate data sources to preprocess data and make the endpoints faster. This can be done by using materialized views or copy pipes.
141
- - Copy pipes capture the result of a pipe at a specific point in time and write it to a target data source. They can run on a schedule or run on demand, making them ideal for event-sourced snapshots, data experimentation, and deduplication with snapshots.
142
- - Materialized views continuously re-evaluate a query as new events are inserted, maintaining an always up-to-date derived dataset. Unlike copy pipes which create point-in-time snapshots, materialized views provide real-time transformations of your data.
143
- Each approach has its own strengths and use cases:
144
- - Use copy pipes when you need scheduled or on-demand snapshots of your data.
145
- - Use materialized views when you need continuous, real-time transformations.
146
- Finally, update the existing endpoint itself, do not add a new one.
143
+ {endpoint_optimization_instructions}
147
144
 
148
145
  IMPORTANT: If the user cancels some of the steps or there is an error in file creation, DO NOT continue with the plan. Stop the process and wait for the user before using any other tool.
149
146
  IMPORTANT: Every time you finish a plan and start a new resource creation or update process, create a new plan before starting with the changes.
150
147
 
148
+ # Using deployment tools:
149
+ - Use `deploy_check` tool to check if the project can be deployed to Tinybird Cloud before deploying it.
150
+ - Use `deploy` tool to deploy the project to Tinybird Cloud.
151
+ - Only use deployment tools if user explicitly asks for it.
152
+
151
153
  # When planning the creation or update of resources:
152
154
  {plan_instructions}
153
155
  {datafile_instructions}
@@ -184,7 +186,6 @@ GCS: {gcs_connection_example}
184
186
  Today is {datetime.now().strftime("%Y-%m-%d")}
185
187
  """,
186
188
  tools=[
187
- Tool(explore_data, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
188
189
  Tool(preview_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=False),
189
190
  Tool(create_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
190
191
  Tool(plan, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
@@ -192,8 +193,10 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
192
193
  Tool(deploy, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
193
194
  Tool(deploy_check, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
194
195
  Tool(mock, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
195
- Tool(read_fixture_data, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
196
- Tool(append, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
196
+ Tool(analyze_file, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
197
+ Tool(analyze_url, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
198
+ Tool(append_file, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
199
+ Tool(append_url, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
197
200
  Tool(
198
201
  get_endpoint_stats, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True
199
202
  ),
@@ -203,6 +206,8 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
203
206
  require_parameter_descriptions=True,
204
207
  takes_ctx=True,
205
208
  ),
209
+ Tool(execute_query, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
210
+ Tool(request_endpoint, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
206
211
  ],
207
212
  )
208
213
 
@@ -230,6 +235,8 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
230
235
  analyze_fixture=partial(analyze_fixture, config=config),
231
236
  execute_cloud_query=partial(execute_cloud_query, config=config),
232
237
  execute_local_query=partial(execute_local_query, config=config),
238
+ request_endpoint_cloud=partial(request_endpoint_cloud, config=config),
239
+ request_endpoint_local=partial(request_endpoint_local, config=config),
233
240
  get_project_files=project.get_project_files,
234
241
  folder=folder,
235
242
  thinking_animation=thinking_animation,
@@ -263,12 +270,14 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
263
270
  click.echo("\n")
264
271
 
265
272
 
266
- def run_agent(config: dict[str, Any], project: Project, dangerously_skip_permissions: bool):
273
+ def run_agent(
274
+ config: dict[str, Any], project: Project, dangerously_skip_permissions: bool, prompt: Optional[str] = None
275
+ ):
267
276
  token = config.get("token", None)
268
277
  host = config.get("host", None)
269
278
  user_token = config.get("user_token", None)
270
- workspace_id = config.get("id", None)
271
- workspace_name = config.get("name", None)
279
+ workspace_id = config.get("id", "")
280
+ workspace_name = config.get("name", "")
272
281
  try:
273
282
  if not token or not host or not workspace_id or not user_token:
274
283
  yes = click.confirm(
@@ -294,8 +303,17 @@ def run_agent(config: dict[str, Any], project: Project, dangerously_skip_permiss
294
303
  )
295
304
  return
296
305
 
306
+ # In print mode, always skip permissions to avoid interactive prompts
307
+ skip_permissions = dangerously_skip_permissions or (prompt is not None)
308
+ agent = TinybirdAgent(token, user_token, host, workspace_id, project, skip_permissions)
309
+
310
+ # Print mode: run once with the provided prompt and exit
311
+ if prompt:
312
+ agent.run(prompt, config, project)
313
+ return
314
+
315
+ # Interactive mode: show banner and enter interactive loop
297
316
  display_banner()
298
- agent = TinybirdAgent(token, user_token, host, workspace_id, project, dangerously_skip_permissions)
299
317
  click.echo()
300
318
  click.echo(FeedbackManager.info(message="Describe what you want to create and I'll help you build it"))
301
319
  click.echo(FeedbackManager.info(message="Run /help for more commands"))
@@ -420,17 +438,31 @@ def mock_data(
420
438
  )
421
439
 
422
440
 
423
- def analyze_fixture(config: dict[str, Any], fixture_path: str) -> dict[str, Any]:
441
+ def analyze_fixture(config: dict[str, Any], fixture_path: str, format: str = "json") -> dict[str, Any]:
424
442
  local_client = get_tinybird_local_client(config, test=False, silent=True)
425
- meta, _data = _analyze(fixture_path, local_client, Path(fixture_path).suffix.lstrip("."))
443
+ meta, _data = _analyze(fixture_path, local_client, format)
426
444
  return meta
427
445
 
428
446
 
429
- def execute_cloud_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> str:
447
+ def execute_cloud_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> dict[str, Any]:
430
448
  client = _get_tb_client(config["token"], config["host"])
431
449
  return client.query(sql=query, pipeline=pipe_name)
432
450
 
433
451
 
434
- def execute_local_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> str:
452
+ def execute_local_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> dict[str, Any]:
435
453
  local_client = get_tinybird_local_client(config, test=False, silent=True)
436
454
  return local_client.query(sql=query, pipeline=pipe_name)
455
+
456
+
457
+ def request_endpoint_cloud(
458
+ config: dict[str, Any], endpoint_name: str, params: Optional[dict[str, str]] = None
459
+ ) -> dict[str, Any]:
460
+ client = _get_tb_client(config["token"], config["host"])
461
+ return client.pipe_data(endpoint_name, format="json", params=params)
462
+
463
+
464
+ def request_endpoint_local(
465
+ config: dict[str, Any], endpoint_name: str, params: Optional[dict[str, str]] = None
466
+ ) -> dict[str, Any]:
467
+ local_client = get_tinybird_local_client(config, test=False, silent=True)
468
+ return local_client.pipe_data(endpoint_name, format="json", params=params)
@@ -147,3 +147,240 @@ def get_resource_type(path: Path) -> str:
147
147
  elif path.suffix.lower() == ".connection":
148
148
  return "connection"
149
149
  return "unknown"
150
+
151
+
152
+ endpoint_optimization_instructions = """
153
+ <endpoint_optimization_instructions>
154
+ ## Endpoint Optimization Instructions
155
+ ### Step 1: Identify Performance Issues
156
+ 1. Analyze the endpoint's query performance metrics
157
+ 2. Look for endpoints with high latency or excessive data scanning
158
+ 3. Check read_bytes/write_bytes ratios to detect inefficient operations
159
+
160
+ ### Step 2: Apply the 5-Question Diagnostic Framework
161
+
162
+ #### Question 1: Are you aggregating or transforming data at query time?
163
+ **Detection:**
164
+ - Look for `count()`, `sum()`, `avg()`, or data type casting in published API endpoints
165
+ - Check if the same calculations are performed on every request
166
+
167
+ **Fix:**
168
+ - Create Materialized Views to pre-aggregate data at ingestion time
169
+ - Move transformations from query time to ingestion time
170
+ - Example transformation:
171
+ ```sql
172
+ -- Before (in endpoint)
173
+ SELECT date, count(*) as daily_count
174
+ FROM events
175
+ GROUP BY date
176
+
177
+ -- After (in Materialized View)
178
+ ENGINE "AggregatingMergeTree"
179
+ ENGINE_PARTITION_KEY "toYYYYMM(date)"
180
+ ENGINE_SORTING_KEY "date"
181
+ AS SELECT
182
+ date,
183
+ count(*) as daily_count
184
+ FROM events
185
+ GROUP BY date
186
+ ```
187
+
188
+ #### Question 2: Are you filtering by fields in the sorting key?
189
+ **Detection:**
190
+ - Examine WHERE clauses in queries
191
+ - Check if filtered columns are part of the sorting key
192
+ - Look for filters on partition keys instead of sorting keys
193
+
194
+ **Fix:**
195
+ - Ensure sorting key includes frequently filtered columns
196
+ - Order sorting key columns by selectivity (most selective first)
197
+ - Guidelines:
198
+ - Use 3-5 columns in sorting key
199
+ - Place `customer_id` or tenant identifiers first for multi-tenant apps
200
+ - Avoid `timestamp` as the first sorting key element
201
+ - Never use partition key for filtering
202
+
203
+ **Example Fix:**
204
+ ```sql
205
+ -- Before
206
+ ENGINE_SORTING_KEY "timestamp, customer_id"
207
+
208
+ -- After (better for multi-tenant filtering)
209
+ ENGINE_SORTING_KEY "customer_id, timestamp"
210
+ ```
211
+
212
+ #### Question 3: Are you using the best data types?
213
+ **Detection:**
214
+ - Scan for overly large data types:
215
+ - String where UUID would work
216
+ - Int64 where UInt32 would suffice
217
+ - DateTime with unnecessary precision
218
+ - Nullable columns that could have defaults
219
+
220
+ **Fix:**
221
+ - Downsize data types:
222
+ ```sql
223
+ -- Before
224
+ id String,
225
+ count Int64,
226
+ created_at DateTime64(3),
227
+ status Nullable(String)
228
+
229
+ -- After
230
+ id UUID,
231
+ count UInt32,
232
+ created_at DateTime,
233
+ status LowCardinality(String) DEFAULT 'pending'
234
+ ```
235
+ - Use `LowCardinality()` for strings with <100k unique values
236
+ - Replace Nullable with default values using `coalesce()`
237
+
238
+ #### Question 4: Are you doing complex operations early in the pipeline?
239
+ **Detection:**
240
+ - Look for JOINs or aggregations before filters
241
+ - Check operation order in multi-node pipes
242
+
243
+ **Fix:**
244
+ - Reorder operations: Filter → Simple transforms → Complex operations
245
+ - Example:
246
+ ```sql
247
+ -- Before
248
+ SELECT * FROM (
249
+ SELECT a.*, b.name
250
+ FROM events a
251
+ JOIN users b ON a.user_id = b.id
252
+ ) WHERE date >= today() - 7
253
+
254
+ -- After
255
+ SELECT a.*, b.name
256
+ FROM (
257
+ SELECT * FROM events
258
+ WHERE date >= today() - 7
259
+ ) a
260
+ JOIN users b ON a.user_id = b.id
261
+ ```
262
+
263
+ #### Question 5: Are you joining two or more data sources?
264
+ **Detection:**
265
+ - Identify JOINs in queries
266
+ - Check read_bytes/write_bytes ratio in Materialized Views
267
+ - Look for full table scans on joined tables
268
+
269
+ **Fix Options:**
270
+ 1. Replace JOIN with subquery:
271
+ ```sql
272
+ -- Before
273
+ SELECT e.*, u.name
274
+ FROM events e
275
+ JOIN users u ON e.user_id = u.id
276
+
277
+ -- After
278
+ SELECT e.*,
279
+ (SELECT name FROM users WHERE id = e.user_id) as name
280
+ FROM events e
281
+ WHERE user_id IN (SELECT id FROM users)
282
+ ```
283
+
284
+ 2. Optimize Materialized View JOINs:
285
+ ```sql
286
+ -- Before (inefficient)
287
+ SELECT a.id, a.value, b.value
288
+ FROM a
289
+ LEFT JOIN b USING id
290
+
291
+ -- After (optimized)
292
+ SELECT a.id, a.value, b.value
293
+ FROM a
294
+ LEFT JOIN (
295
+ SELECT id, value
296
+ FROM b
297
+ WHERE b.id IN (SELECT id FROM a)
298
+ ) b USING id
299
+ ```
300
+
301
+ ### Step 3: Implementation Actions
302
+
303
+ #### For Schema Changes:
304
+ 1. Update the datasource schema
305
+ 2. Update the sorting keys and data types
306
+ 3. Update dependent pipes and endpoints
307
+
308
+ #### For Query Optimizations:
309
+ 1. Create Materialized Views for repeated aggregations
310
+ 2. Rewrite queries following best practices
311
+ 3. Test performance improvements
312
+
313
+ #### For JOIN Optimizations:
314
+ 1. Evaluate if JOIN is necessary
315
+ 2. Consider denormalization strategies
316
+ 3. Use Copy Pipes for historical data recalculation
317
+ 4. Implement filtered JOINs in Materialized Views
318
+
319
+ #### In general:
320
+ 1. If you need to iterate an existing resource, do not create a new iteration, just update it with the needed changes.
321
+
322
+ ## Monitoring and Validation
323
+
324
+ ### Monitoring:
325
+ 1. Set up alerts for endpoints exceeding latency thresholds
326
+ 2. Review of tinybird.pipe_stats_rt (realtime stats of last 24h) and tinybird.pipe_stats (historical stats aggregated by day)
327
+ 3. Track processed data patterns over time
328
+ 4. Monitor for query pattern changes
329
+
330
+ ### Success Metrics:
331
+ - Reduced query latency
332
+ - Lower data scanning (read_bytes)
333
+ - Improved read_bytes/write_bytes ratio
334
+ - Consistent sub-second API response times
335
+
336
+ ## Code Templates
337
+
338
+ ### Materialized View Template:
339
+ ```sql
340
+ NODE materialized_view_name
341
+ SQL >
342
+ SELECT
343
+ -- Pre-aggregated fields
344
+ toDate(timestamp) as date,
345
+ customer_id,
346
+ count(*) as event_count,
347
+ sum(amount) as total_amount
348
+ FROM source_table
349
+ GROUP BY date, customer_id
350
+
351
+ TYPE materialized
352
+ DATASOURCE mv_datasource_name
353
+ ENGINE "AggregatingMergeTree"
354
+ ENGINE_PARTITION_KEY "toYYYYMM(date)"
355
+ ENGINE_SORTING_KEY "customer_id, date"
356
+ ```
357
+
358
+ ### Optimized Query Template:
359
+ ```sql
360
+ NODE endpoint_query
361
+ SQL >
362
+ -- Step 1: Filter early
363
+ WITH filtered_data AS (
364
+ SELECT * FROM events
365
+ WHERE customer_id = {{ String(customer_id) }}
366
+ AND date >= {{ Date(start_date) }}
367
+ AND date <= {{ Date(end_date) }}
368
+ )
369
+ -- Step 2: Simple operations
370
+ SELECT
371
+ date,
372
+ sum(amount) as daily_total
373
+ FROM filtered_data
374
+ GROUP BY date
375
+ ORDER BY date DESC
376
+ ```
377
+
378
+ ## Best Practices Summary
379
+
380
+ 1. **Think ingestion-time, not query-time** - Move computations upstream
381
+ 2. **Index smartly** - Sorting keys should match filter patterns
382
+ 3. **Size appropriately** - Use the smallest viable data types
383
+ 4. **Filter first** - Reduce data before complex operations
384
+ 5. **JOIN carefully** - Consider alternatives and optimize when necessary
385
+ </endpoint_optimization_instructions>
386
+ """
@@ -0,0 +1,79 @@
1
+ import json
2
+ from pathlib import Path
3
+ from urllib.parse import urlparse
4
+
5
+ import click
6
+ from pydantic_ai import RunContext
7
+
8
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext
9
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
10
+
11
+
12
+ def analyze_file(ctx: RunContext[TinybirdAgentContext], fixture_pathname: str):
13
+ """Analyze a fixture data file present in the project folder
14
+
15
+ Args:
16
+ fixture_pathname (str): a path or an external url to a fixture file. Required.
17
+
18
+ Returns:
19
+ str: The content of the fixture data file.
20
+ """
21
+ try:
22
+ ctx.deps.thinking_animation.stop()
23
+ click.echo(FeedbackManager.highlight(message=f"» Analyzing {fixture_pathname}..."))
24
+ fixture_path = Path(ctx.deps.folder) / fixture_pathname.lstrip("/")
25
+
26
+ if not fixture_path.exists():
27
+ click.echo(FeedbackManager.error(message=f"No fixture data found for {fixture_pathname}."))
28
+ ctx.deps.thinking_animation.start()
29
+ return f"No fixture data found for {fixture_pathname}. Please check the path of the fixture and try again."
30
+
31
+ fixture_extension = fixture_path.suffix.lstrip(".")
32
+ response = ctx.deps.analyze_fixture(fixture_path=str(fixture_path), format=fixture_extension)
33
+ click.echo(FeedbackManager.success(message="✓ Done!\n"))
34
+ ctx.deps.thinking_animation.start()
35
+ # limit content to first 10 rows
36
+ data = response["preview"]["data"][:10]
37
+ columns = response["analysis"]["columns"]
38
+
39
+ return f"#Result of analysis of {fixture_pathname}:\n##Columns:\n{json.dumps(columns)}\n##Data sample:\n{json.dumps(data)}"
40
+ except Exception as e:
41
+ ctx.deps.thinking_animation.stop()
42
+ click.echo(FeedbackManager.error(message=f"Error analyzing {fixture_pathname}: {e}"))
43
+ ctx.deps.thinking_animation.start()
44
+ return f"Error analyzing {fixture_pathname}: {e}"
45
+
46
+
47
+ def analyze_url(ctx: RunContext[TinybirdAgentContext], fixture_url: str):
48
+ """Analyze a fixture file present in an external url
49
+
50
+ Args:
51
+ fixture_url (str): an external url to a fixture file. Required.
52
+
53
+ Returns:
54
+ str: The analysis with the columns and the first 10 rows of the fixture data file.
55
+ """
56
+ try:
57
+ ctx.deps.thinking_animation.stop()
58
+ is_url = urlparse(fixture_url).scheme in ("http", "https")
59
+ click.echo(FeedbackManager.highlight(message=f"» Analyzing {fixture_url}..."))
60
+ if not is_url:
61
+ click.echo(FeedbackManager.error(message=f"{fixture_url} is not a valid url."))
62
+ ctx.deps.thinking_animation.start()
63
+ return f"{fixture_url} is not a valid url. Please check the url and try again."
64
+
65
+ fixture_extension = fixture_url.split(".")[-1]
66
+
67
+ response = ctx.deps.analyze_fixture(fixture_path=fixture_url, format=fixture_extension)
68
+ click.echo(FeedbackManager.success(message="✓ Done!\n"))
69
+ ctx.deps.thinking_animation.start()
70
+ # limit content to first 10 rows
71
+ data = response["preview"]["data"][:10]
72
+ columns = response["analysis"]["columns"]
73
+
74
+ return f"#Result of analysis of URL {fixture_url}:\n##Columns:\n{json.dumps(columns)}\n##Data sample:\n{json.dumps(data)}"
75
+ except Exception as e:
76
+ ctx.deps.thinking_animation.stop()
77
+ click.echo(FeedbackManager.error(message=f"Error analyzing {fixture_url}: {e}"))
78
+ ctx.deps.thinking_animation.start()
79
+ return f"Error analyzing {fixture_url}: {e}"
@@ -5,7 +5,7 @@ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmat
5
5
  from tinybird.tb.modules.feedback_manager import FeedbackManager
6
6
 
7
7
 
8
- def append(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str) -> str:
8
+ def append_file(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str) -> str:
9
9
  """Append existing fixture to a datasource
10
10
 
11
11
  Args:
@@ -18,7 +18,7 @@ def append(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_
18
18
  try:
19
19
  ctx.deps.thinking_animation.stop()
20
20
  confirmation = show_confirmation(
21
- title=f"Append existing fixture for datasource {datasource_name}?",
21
+ title=f"Append fixture {fixture_pathname} to datasource {datasource_name}?",
22
22
  skip_confirmation=ctx.deps.dangerously_skip_permissions,
23
23
  )
24
24
 
@@ -45,3 +45,43 @@ def append(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_
45
45
  click.echo(FeedbackManager.error(message=e))
46
46
  ctx.deps.thinking_animation.start()
47
47
  return f"Error appending fixture {fixture_pathname} to {datasource_name}: {e}"
48
+
49
+
50
+ def append_url(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_url: str) -> str:
51
+ """Append existing fixture to a datasource
52
+
53
+ Args:
54
+ datasource_name: Name of the datasource to append fixture to
55
+ fixture_url: external url to the fixture file to append
56
+
57
+ Returns:
58
+ str: Message indicating the success or failure of the appending
59
+ """
60
+ try:
61
+ ctx.deps.thinking_animation.stop()
62
+ confirmation = show_confirmation(
63
+ title=f"Append URL {fixture_url} to datasource {datasource_name}?",
64
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
65
+ )
66
+
67
+ if confirmation == "review":
68
+ click.echo()
69
+ feedback = show_input(ctx.deps.workspace_name)
70
+ ctx.deps.thinking_animation.start()
71
+ return f"User did not confirm appending URL {fixture_url} and gave the following feedback: {feedback}"
72
+
73
+ if confirmation == "cancel":
74
+ ctx.deps.thinking_animation.start()
75
+ return f"User rejected appending URL {fixture_url}. Skip this step"
76
+
77
+ ctx.deps.thinking_animation.stop()
78
+ click.echo(FeedbackManager.highlight(message=f"\n» Appending {fixture_url} to {datasource_name}..."))
79
+ ctx.deps.append_data(datasource_name=datasource_name, path=fixture_url)
80
+ click.echo(FeedbackManager.success(message=f"✓ Data appended to {datasource_name}"))
81
+ ctx.deps.thinking_animation.start()
82
+ return f"Data appended to {datasource_name}"
83
+ except Exception as e:
84
+ ctx.deps.thinking_animation.stop()
85
+ click.echo(FeedbackManager.error(message=e))
86
+ ctx.deps.thinking_animation.start()
87
+ return f"Error appending URL {fixture_url} to {datasource_name}: {e}"
@@ -1,7 +1,7 @@
1
1
  import click
2
2
  from pydantic_ai import RunContext
3
3
 
4
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext
4
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
5
5
  from tinybird.tb.modules.feedback_manager import FeedbackManager
6
6
 
7
7
 
@@ -9,6 +9,21 @@ def deploy_check(ctx: RunContext[TinybirdAgentContext]) -> str:
9
9
  """Check that project can be deployed"""
10
10
  try:
11
11
  ctx.deps.thinking_animation.stop()
12
+ confirmation = show_confirmation(
13
+ title="Check that project can be deployed?",
14
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
15
+ )
16
+
17
+ if confirmation == "review":
18
+ click.echo()
19
+ feedback = show_input(ctx.deps.workspace_name)
20
+ ctx.deps.thinking_animation.start()
21
+ return f"User did not confirm deployment check and gave the following feedback: {feedback}"
22
+
23
+ if confirmation == "cancel":
24
+ ctx.deps.thinking_animation.start()
25
+ return "User cancelled deployment check. Stop deployment check."
26
+
12
27
  ctx.deps.deploy_check_project()
13
28
  ctx.deps.thinking_animation.start()
14
29
  return "Project can be deployed"
@@ -0,0 +1,61 @@
1
+ import click
2
+ import humanfriendly
3
+ from pydantic_ai import RunContext
4
+
5
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext
6
+ from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table
7
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
8
+
9
+
10
+ def execute_query(ctx: RunContext[TinybirdAgentContext], query: str, task: str, cloud: bool = True):
11
+ """Execute a query:
12
+
13
+ Args:
14
+ query (str): The query to execute. Required.
15
+ task (str): The purpose of the query. Required.
16
+ cloud (bool): Whether to execute the query on cloud or local. Optional.
17
+
18
+ Returns:
19
+ str: The result of the query.
20
+ """
21
+ try:
22
+ cloud_or_local = "cloud" if cloud else "local"
23
+ ctx.deps.thinking_animation.stop()
24
+
25
+ click.echo(FeedbackManager.highlight(message=f"» Executing query to {cloud_or_local}:\n{query}\n"))
26
+
27
+ is_templating = query.strip().startswith("%")
28
+ query_format = "FORMAT JSON"
29
+ if is_templating:
30
+ query = query.strip()
31
+ query = f"%\nSELECT * FROM ({query}) {query_format}"
32
+ else:
33
+ query = f"SELECT * FROM ({query}) {query_format}"
34
+
35
+ execute_query = ctx.deps.execute_cloud_query if cloud else ctx.deps.execute_local_query
36
+ result = execute_query(query=query)
37
+ stats = result["statistics"]
38
+ seconds = stats["elapsed"]
39
+ rows_read = humanfriendly.format_number(stats["rows_read"])
40
+ bytes_read = humanfriendly.format_size(stats["bytes_read"])
41
+
42
+ click.echo(FeedbackManager.info_query_stats(seconds=seconds, rows=rows_read, bytes=bytes_read))
43
+
44
+ if not result["data"]:
45
+ click.echo(FeedbackManager.info_no_rows())
46
+ else:
47
+ echo_safe_humanfriendly_tables_format_smart_table(
48
+ data=[d.values() for d in result["data"]], column_names=result["data"][0].keys()
49
+ )
50
+ ctx.deps.thinking_animation.start()
51
+ result["data"] = result["data"][:10]
52
+ return f"Result for task '{task}' in {cloud_or_local} environment: {result}. The user is being shown the full result in the console but this message only contains the first 10 rows."
53
+ except Exception as e:
54
+ error = str(e)
55
+ ctx.deps.thinking_animation.stop()
56
+ click.echo(FeedbackManager.error(message=error))
57
+ ctx.deps.thinking_animation.start()
58
+ if "not found" in error.lower() and cloud:
59
+ return f"Error executing query: {error}. Please run the query against Tinybird local instead of cloud."
60
+ else:
61
+ return f"Error executing query: {error}. Please try again."
@@ -0,0 +1,65 @@
1
+ from typing import Optional
2
+
3
+ import click
4
+ import humanfriendly
5
+ from pydantic_ai import RunContext
6
+
7
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext
8
+ from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table
9
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
10
+
11
+
12
+ def request_endpoint(
13
+ ctx: RunContext[TinybirdAgentContext],
14
+ endpoint_name: str,
15
+ params: Optional[dict[str, str]] = None,
16
+ cloud: bool = True,
17
+ ):
18
+ """Request an endpoint:
19
+
20
+ Args:
21
+ endpoint_name (str): The name of the endpoint to request. Required.
22
+ params (dict): The parameters to pass to the endpoint. Optional.
23
+ cloud (bool): Whether to request the endpoint on cloud or local. Optional.
24
+
25
+ Returns:
26
+ str: The result of the query.
27
+ """
28
+ try:
29
+ cloud_or_local = "cloud" if cloud else "local"
30
+ ctx.deps.thinking_animation.stop()
31
+
32
+ click.echo(
33
+ FeedbackManager.highlight(
34
+ message=f"» Calling endpoint {endpoint_name} in {cloud_or_local} environment with params {params}"
35
+ )
36
+ )
37
+
38
+ request_endpoint = ctx.deps.request_endpoint_cloud if cloud else ctx.deps.request_endpoint_local
39
+ result = request_endpoint(endpoint_name=endpoint_name, params=params)
40
+ stats = result["statistics"]
41
+ seconds = stats["elapsed"]
42
+ rows_read = humanfriendly.format_number(stats["rows_read"])
43
+ bytes_read = humanfriendly.format_size(stats["bytes_read"])
44
+
45
+ click.echo(FeedbackManager.success_print_pipe(pipe=endpoint_name))
46
+ click.echo(FeedbackManager.info_query_stats(seconds=seconds, rows=rows_read, bytes=bytes_read))
47
+
48
+ if not result["data"]:
49
+ click.echo(FeedbackManager.info_no_rows())
50
+ else:
51
+ echo_safe_humanfriendly_tables_format_smart_table(
52
+ data=[d.values() for d in result["data"][:5]], column_names=result["data"][0].keys()
53
+ )
54
+ click.echo("Showing first 5 results\n")
55
+ ctx.deps.thinking_animation.start()
56
+ return f"Result for endpoint {endpoint_name} with params {params} in {cloud_or_local} environment: {result}. Do not show result is already shown in the console."
57
+ except Exception as e:
58
+ error = str(e)
59
+ ctx.deps.thinking_animation.stop()
60
+ click.echo(FeedbackManager.error(message=error))
61
+ ctx.deps.thinking_animation.start()
62
+ if "not found" in error.lower() and cloud:
63
+ return f"Error executing query: {error}. Please run the query against Tinybird local instead of cloud."
64
+ else:
65
+ return f"Error executing query: {error}. Please try again."
@@ -41,8 +41,10 @@ class TinybirdAgentContext(BaseModel):
41
41
  mock_data: Callable[..., list[dict[str, Any]]]
42
42
  append_data: Callable[..., None]
43
43
  analyze_fixture: Callable[..., dict[str, Any]]
44
- execute_cloud_query: Callable[..., str]
45
- execute_local_query: Callable[..., str]
44
+ execute_cloud_query: Callable[..., dict[str, Any]]
45
+ execute_local_query: Callable[..., dict[str, Any]]
46
+ request_endpoint_cloud: Callable[..., dict[str, Any]]
47
+ request_endpoint_local: Callable[..., dict[str, Any]]
46
48
  dangerously_skip_permissions: bool
47
49
  token: str
48
50
  user_token: str
@@ -86,6 +86,10 @@ agent_mode_flag = os.environ.get("TB_AGENT_MODE", "false") == "true"
86
86
  help="Skip permissions check in agent mode.",
87
87
  hidden=True,
88
88
  )
89
+ @click.option(
90
+ "--p",
91
+ help="Run agent in print mode with the provided prompt and exit.",
92
+ )
89
93
  @click.version_option(version=VERSION)
90
94
  @click.pass_context
91
95
  def cli(
@@ -101,6 +105,7 @@ def cli(
101
105
  output: str,
102
106
  max_depth: int,
103
107
  dangerously_skip_permissions: bool,
108
+ p: Optional[str],
104
109
  ) -> None:
105
110
  """
106
111
  Use `OBFUSCATE_REGEX_PATTERN` and `OBFUSCATE_PATTERN_SEPARATOR` environment variables to define a regex pattern and a separator (in case of a single string with multiple regex) to obfuscate secrets in the CLI output.
@@ -202,9 +207,10 @@ def cli(
202
207
  ctx.ensure_object(dict)["output"] = output
203
208
 
204
209
  is_agent_mode = agent_mode_flag and ctx.invoked_subcommand is None
210
+ is_print_mode = p is not None
205
211
 
206
- if is_agent_mode:
207
- run_agent(config, project, dangerously_skip_permissions)
212
+ if is_agent_mode or is_print_mode:
213
+ run_agent(config, project, dangerously_skip_permissions, prompt=p)
208
214
 
209
215
 
210
216
  @cli.command(hidden=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev252
3
+ Version: 0.0.1.dev254
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -17,7 +17,7 @@ tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1w
17
17
  tinybird/datafile/parse_connection.py,sha256=tRyn2Rpr1TeWet5BXmMoQgaotbGdYep1qiTak_OqC5E,1825
18
18
  tinybird/datafile/parse_datasource.py,sha256=ssW8QeFSgglVFi3sDZj_HgkJiTJ2069v2JgqnH3CkDE,1825
19
19
  tinybird/datafile/parse_pipe.py,sha256=xf4m0Tw44QWJzHzAm7Z7FwUoUUtr7noMYjU1NiWnX0k,3880
20
- tinybird/tb/__cli__.py,sha256=tux3rAKuHbXOtIaG9CZQxDsezngoOjC_cZmI7OXF1UY,247
20
+ tinybird/tb/__cli__.py,sha256=dY306yA2ppVsNM4k4b-zkQx3D8E5suyaU1zBkami7R0,247
21
21
  tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
22
22
  tinybird/tb/cli.py,sha256=FdDFEIayjmsZEVsVSSvRiVYn_FHOVg_zWQzchnzfWho,1008
23
23
  tinybird/tb/client.py,sha256=pJbdkWMXGAqKseNAvdsRRnl_c7I-DCMB0dWCQnG82nU,54146
@@ -25,7 +25,7 @@ tinybird/tb/config.py,sha256=mhMTGnMB5KcxGoh3dewIr2Jjsa6pHE183gCPAQWyp6o,3973
25
25
  tinybird/tb/modules/build.py,sha256=efD-vamK1NPaDo9R86Hn8be2DYoW0Hh5bZiH7knK5dk,7790
26
26
  tinybird/tb/modules/build_common.py,sha256=rWhemU8bk0ZE2eiwZDaTmV9cPabDGGlyc2WnRxfhT0M,12859
27
27
  tinybird/tb/modules/cicd.py,sha256=0KLKccha9IP749QvlXBmzdWv1On3mFwMY4DUcJlBxiE,7326
28
- tinybird/tb/modules/cli.py,sha256=DnNCC1HBe42CNCYIa8FR7YfbUNo9Mbd4ATTVShK8f6Q,16573
28
+ tinybird/tb/modules/cli.py,sha256=1kErLFhxgMWldbE7P4-bkPUcug8ymRyXDHRG9-vGb_4,16755
29
29
  tinybird/tb/modules/common.py,sha256=jTTaDDHrZREt--032XhP6GkbfFwC79YJ5aH1Sl7bmbo,81925
30
30
  tinybird/tb/modules/config.py,sha256=gK7rgaWTDd4ZKCrNEg_Uemr26EQjqWt6TjyQKujxOws,11462
31
31
  tinybird/tb/modules/connection.py,sha256=-MY56NUAai6EMC4-wpi7bT0_nz_SA8QzTmHkV7HB1IQ,17810
@@ -68,26 +68,28 @@ tinybird/tb/modules/watch.py,sha256=No0bK1M1_3CYuMaIgylxf7vYFJ72lTJe3brz6xQ-mJo,
68
68
  tinybird/tb/modules/workspace.py,sha256=Q_8HcxMsNg8QG9aBlwcWS2umrDP5IkTIHqqz3sfmGuc,11341
69
69
  tinybird/tb/modules/workspace_members.py,sha256=5JdkJgfuEwbq-t6vxkBhYwgsiTDxF790wsa6Xfif9nk,8608
70
70
  tinybird/tb/modules/agent/__init__.py,sha256=i3oe3vDIWWPaicdCM0zs7D7BJ1W0k7th93ooskHAV00,54
71
- tinybird/tb/modules/agent/agent.py,sha256=Uxo2FNNu-YW-J-AbZizZe0eKYKdZRwbPJCgA9EsXlgs,21208
71
+ tinybird/tb/modules/agent/agent.py,sha256=lemqCQyYYnoSqp3gVPMAc0J7b4OrLy8sCpUk1_Vy9bo,22150
72
72
  tinybird/tb/modules/agent/animations.py,sha256=4WOC5_2BracttmMCrV0H91tXfWcUzQHBUaIJc5FA7tE,3490
73
73
  tinybird/tb/modules/agent/banner.py,sha256=KX_e467uiy1gWOZ4ofTZt0GCFGQqHQ_8Ob27XLQqda0,3053
74
74
  tinybird/tb/modules/agent/memory.py,sha256=H6SJK--2L5C87B7AJd_jMqsq3sCvFvZwZXmajuT0GBE,1171
75
75
  tinybird/tb/modules/agent/models.py,sha256=LW1D27gjcd_jwFmghEzteCgToDfodX2B6B5S8BYbysw,735
76
- tinybird/tb/modules/agent/prompts.py,sha256=wbe6vUnm-fskceWgP13R5VW1v_YF7_wLDe-wBN6rlWw,6998
77
- tinybird/tb/modules/agent/utils.py,sha256=fOliFWZ1A6_SBjwreJDid53cMFjH1Ah4pe5DumPSolE,26111
76
+ tinybird/tb/modules/agent/prompts.py,sha256=-2Gazjrh3NGjSqtw9yZ8Up-tQLDD-kKGj7rwPSk71oY,13377
77
+ tinybird/tb/modules/agent/utils.py,sha256=me5-kflBxV4N4psBSxI3vAfmFvAtsSnboc7ND3M9omw,26249
78
78
  tinybird/tb/modules/agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- tinybird/tb/modules/agent/tools/append.py,sha256=cBdKBhUW0kooIbywtZNd_bhxbbMyf5NUKHwanAl5BmA,2042
79
+ tinybird/tb/modules/agent/tools/analyze.py,sha256=7oxJ3waCS24Qja_k5GUB59_XiHTG9pCewZogOXhH0cA,3495
80
+ tinybird/tb/modules/agent/tools/append.py,sha256=brd7OFSFJCC3-k6_1p2Qlcg_v2cDlpL_iBUtWtpiqfw,3821
80
81
  tinybird/tb/modules/agent/tools/build.py,sha256=LhzJMx6tbxC7gogIrxhfKJc-SDgoSR-FC6IunfaCdn8,758
81
82
  tinybird/tb/modules/agent/tools/create_datafile.py,sha256=wcPcChACTIFKw0lKFTlhm0sWJKhQkMLPLnGNpKyeETA,2962
82
83
  tinybird/tb/modules/agent/tools/deploy.py,sha256=WrsSlaufKGOBx0S13uoMQQH2DnKue5LQ231Rx4RXh2I,1443
83
- tinybird/tb/modules/agent/tools/deploy_check.py,sha256=VqMYC7l3_cihmmM_pi8w1t8rJ3P0xDc7pHs_st9k-9Q,684
84
+ tinybird/tb/modules/agent/tools/deploy_check.py,sha256=LBE8aENYvCEaxbVTVKMSI2NGGiHeh-y60_MPlfrcvFk,1331
85
+ tinybird/tb/modules/agent/tools/execute_query.py,sha256=s6QCIe8iD44_XZWgN-zYvWtXPEgJb2kGx6zgXCJbhKc,2642
84
86
  tinybird/tb/modules/agent/tools/explore.py,sha256=ihALc_kBcsjrKT3hZyicqyIowB0g_K3AtNNi-5uz9-8,412
85
87
  tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=_3wAvDykJitIOb5BRnP7wCy6y06y1qlULHLWB-MvS2M,1705
86
88
  tinybird/tb/modules/agent/tools/get_openapi_definition.py,sha256=9cQ-SUeB1NVhPJN1s8aQh9KQxqI9-DEEW1Ot5r2JbOk,1575
87
89
  tinybird/tb/modules/agent/tools/mock.py,sha256=Omog_gdEdm8YuBXNrJdHwxHqjL_ji9UIr75mALF4ozI,3408
88
90
  tinybird/tb/modules/agent/tools/plan.py,sha256=pr6LnItz6vlOeCG8GE459ExsrBEG0KLx-g02SZGNjXU,1217
89
91
  tinybird/tb/modules/agent/tools/preview_datafile.py,sha256=e9q5fR0afApcrntzFrnuHmd10ex7MG_GM6T0Pwc9bRI,850
90
- tinybird/tb/modules/agent/tools/read_fixture_data.py,sha256=rvTdVlZsu3rQTSWqXzpFt4LEwnBcMLIT8hlI5C7MVN4,1430
92
+ tinybird/tb/modules/agent/tools/request_endpoint.py,sha256=iwzjYLtX_4YS7b9KzRwPGtVkx2UNujMAXv8m7mm8Fac,2683
91
93
  tinybird/tb/modules/datafile/build.py,sha256=NFKBrusFLU0WJNCXePAFWiEDuTaXpwc0lHlOQWEJ43s,51117
92
94
  tinybird/tb/modules/datafile/build_common.py,sha256=2yNdxe49IMA9wNvl25NemY2Iaz8L66snjOdT64dm1is,4511
93
95
  tinybird/tb/modules/datafile/build_datasource.py,sha256=Ra8pVQBDafbFRUKlhpgohhTsRyp_ADKZJVG8Gd69idY,17227
@@ -108,8 +110,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
108
110
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
109
111
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
110
112
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
111
- tinybird-0.0.1.dev252.dist-info/METADATA,sha256=5RXjrDqF3SKasGIPyA3n9a5ZbKyFwrNxwxP-SwnVprk,1733
112
- tinybird-0.0.1.dev252.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
113
- tinybird-0.0.1.dev252.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
114
- tinybird-0.0.1.dev252.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
115
- tinybird-0.0.1.dev252.dist-info/RECORD,,
113
+ tinybird-0.0.1.dev254.dist-info/METADATA,sha256=OJ8Hn_avSbp70iyOkjiWretCRbTvI-jUk514rgWiA40,1733
114
+ tinybird-0.0.1.dev254.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
115
+ tinybird-0.0.1.dev254.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
116
+ tinybird-0.0.1.dev254.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
117
+ tinybird-0.0.1.dev254.dist-info/RECORD,,
@@ -1,36 +0,0 @@
1
- import json
2
- from pathlib import Path
3
-
4
- import click
5
- from pydantic_ai import RunContext
6
-
7
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext
8
- from tinybird.tb.modules.feedback_manager import FeedbackManager
9
-
10
-
11
- def read_fixture_data(ctx: RunContext[TinybirdAgentContext], fixture_pathname: str):
12
- """Read fixture data in the project folder
13
-
14
- Args:
15
- fixture_pathname (str): a path to a fixture file. Required.
16
-
17
- Returns:
18
- str: The content of the fixture data file.
19
- """
20
- ctx.deps.thinking_animation.stop()
21
- click.echo(FeedbackManager.highlight(message=f"» Analyzing {fixture_pathname}..."))
22
- fixture_path = Path(ctx.deps.folder) / fixture_pathname.lstrip("/")
23
-
24
- if not fixture_path.exists():
25
- click.echo(FeedbackManager.error(message=f"No fixture data found for {fixture_pathname}."))
26
- ctx.deps.thinking_animation.start()
27
- return f"No fixture data found for {fixture_pathname}. Please check the name of the fixture and try again."
28
-
29
- response = ctx.deps.analyze_fixture(fixture_path=str(fixture_path))
30
- click.echo(FeedbackManager.success(message="✓ Done!\n"))
31
- ctx.deps.thinking_animation.start()
32
- # limit content to first 10 rows
33
- data = response["preview"]["data"][:10]
34
- columns = response["analysis"]["columns"]
35
-
36
- return f"#Result of analysis of {fixture_pathname}:\n##Columns:\n{json.dumps(columns)}\n##Data sample:\n{json.dumps(data)}"