tinybird 0.0.1.dev254__py3-none-any.whl → 0.0.1.dev256__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/prompts.py CHANGED
@@ -689,7 +689,6 @@ materialized_pipe_instructions = """
689
689
  - TYPE MATERIALIZED is the type of the pipe and it is mandatory for materialized pipes.
690
690
  - The content of the .pipe file must follow the materialized_pipe_content format.
691
691
  - Use State modifier for the aggregated columns in the pipe.
692
- - Keep the SQL query simple and avoid using complex queries with joins, subqueries, etc.
693
692
  </materialized_pipe_instructions>
694
693
  <materialized_pipe_content>
695
694
  NODE daily_sales
@@ -812,11 +811,11 @@ TYPE endpoint
812
811
  """
813
812
 
814
813
  pipe_instructions = """
814
+ Follow these instructions when creating or updating any type of .pipe file:
815
815
  <pipe_file_instructions>
816
816
  - The pipe names must be unique.
817
817
  - Nodes do NOT use the same name as the Pipe they belong to. So if the pipe name is "my_pipe", the nodes must be named different like "my_pipe_node_1", "my_pipe_node_2", etc.
818
818
  - Node names MUST be different from the resource names in the project.
819
- - Avoid more than one node per pipe unless it is really necessary or requested by the user.
820
819
  - No indentation is allowed for property names: DESCRIPTION, NODE, SQL, TYPE, etc.
821
820
  - Allowed TYPE values are: endpoint, copy, materialized, sink.
822
821
  - Add always the output node in the TYPE section or in the last node of the pipe.
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.dev254'
8
- __revision__ = '269e345'
7
+ __version__ = '0.0.1.dev256'
8
+ __revision__ = '78e1011'
@@ -26,13 +26,14 @@ from tinybird.prompts import (
26
26
  from tinybird.tb.client import TinyB
27
27
  from tinybird.tb.modules.agent.animations import ThinkingAnimation
28
28
  from tinybird.tb.modules.agent.banner import display_banner
29
- from tinybird.tb.modules.agent.memory import clear_history
29
+ from tinybird.tb.modules.agent.memory import clear_history, clear_messages, load_messages, save_messages
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
33
  endpoint_optimization_instructions,
34
34
  plan_instructions,
35
35
  resources_prompt,
36
+ sql_agent_instructions,
36
37
  sql_instructions,
37
38
  )
38
39
  from tinybird.tb.modules.agent.tools.analyze import analyze_file, analyze_url
@@ -41,6 +42,7 @@ from tinybird.tb.modules.agent.tools.build import build
41
42
  from tinybird.tb.modules.agent.tools.create_datafile import create_datafile
42
43
  from tinybird.tb.modules.agent.tools.deploy import deploy
43
44
  from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
45
+ from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
44
46
  from tinybird.tb.modules.agent.tools.execute_query import execute_query
45
47
  from tinybird.tb.modules.agent.tools.get_endpoint_stats import get_endpoint_stats
46
48
  from tinybird.tb.modules.agent.tools.get_openapi_definition import get_openapi_definition
@@ -53,7 +55,7 @@ from tinybird.tb.modules.build_common import process as build_process
53
55
  from tinybird.tb.modules.common import _analyze, _get_tb_client
54
56
  from tinybird.tb.modules.config import CLIConfig
55
57
  from tinybird.tb.modules.deployment_common import create_deployment
56
- from tinybird.tb.modules.exceptions import CLIBuildException, CLIMockException
58
+ from tinybird.tb.modules.exceptions import CLIBuildException, CLIDeploymentException, CLIMockException
57
59
  from tinybird.tb.modules.feedback_manager import FeedbackManager
58
60
  from tinybird.tb.modules.local_common import get_tinybird_local_client
59
61
  from tinybird.tb.modules.login_common import login
@@ -76,7 +78,8 @@ class TinybirdAgent:
76
78
  self.host = host
77
79
  self.dangerously_skip_permissions = dangerously_skip_permissions
78
80
  self.project = project
79
- self.messages: list[ModelMessage] = []
81
+ # we load the last 5 messages to manage token usage
82
+ self.messages: list[ModelMessage] = load_messages()[-5:]
80
83
  self.agent = Agent(
81
84
  model=create_model(user_token, host, workspace_id),
82
85
  deps_type=TinybirdAgentContext,
@@ -121,6 +124,7 @@ You have access to the following tools:
121
124
  12. `get_openapi_definition` - Get the OpenAPI definition for all endpoints that are built/deployed to Tinybird Cloud or Local.
122
125
  13. `execute_query` - Execute a query against Tinybird Cloud or Local.
123
126
  13. `request_endpoint` - Request an endpoint against Tinybird Cloud or Local.
127
+ 14. `diff_resource` - Diff the content of a resource in Tinybird Cloud vs Tinybird Local vs Project local file.
124
128
 
125
129
  # When creating or updating datafiles:
126
130
  1. Use `plan` tool to plan the creation or update of resources.
@@ -172,6 +176,7 @@ IMPORTANT: Every time you finish a plan and start a new resource creation or upd
172
176
  {copy_pipe_instructions}
173
177
 
174
178
  # Working with SQL queries:
179
+ {sql_agent_instructions}
175
180
  {sql_instructions}
176
181
 
177
182
  # Working with connections files:
@@ -182,6 +187,23 @@ Kafka: {kafka_connection_example}
182
187
  S3: {s3_connection_example}
183
188
  GCS: {gcs_connection_example}
184
189
 
190
+ # When executing a query or requesting/testing an endpoint:
191
+ - You need to be sure that the selected resource is updated to the last version in the environment you are working on.
192
+ - Use `diff_resource` tool to compare the content of the resource to compare the differences between environments.
193
+ - Project local file is the source of truth.
194
+ - If the resource is not present or updated to the last version in Tinybird Local, it means you need to build the project.
195
+ - If the resource is not present or updated to the last version in Tinybird Cloud, it means you need to deploy the project.
196
+
197
+ # How to use apppend tools:
198
+ - Use append as part of the creation of a new landing datasource if the user provided a file or an external url
199
+ - Use append if user explicitly asks for it
200
+ - Do not append data if user requests to test an endpoint
201
+
202
+ # How to use `mock` tool:
203
+ - Use `mock` tool as part of the creation of a new landing datasource if the user did not provided a file or an external url
204
+ - Use `mock` tool if user explicitly asks for it
205
+ - Do not use `mock` tool if user requests to test an endpoint.
206
+
185
207
  # Info
186
208
  Today is {datetime.now().strftime("%Y-%m-%d")}
187
209
  """,
@@ -208,6 +230,7 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
208
230
  ),
209
231
  Tool(execute_query, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
210
232
  Tool(request_endpoint, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
233
+ Tool(diff_resource, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
211
234
  ],
212
235
  )
213
236
 
@@ -233,10 +256,16 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
233
256
  mock_data=partial(mock_data, project=project, config=config),
234
257
  append_data=partial(append_data, config=config),
235
258
  analyze_fixture=partial(analyze_fixture, config=config),
236
- execute_cloud_query=partial(execute_cloud_query, config=config),
237
- execute_local_query=partial(execute_local_query, config=config),
259
+ execute_query_cloud=partial(execute_query_cloud, config=config),
260
+ execute_query_local=partial(execute_query_local, config=config),
238
261
  request_endpoint_cloud=partial(request_endpoint_cloud, config=config),
239
262
  request_endpoint_local=partial(request_endpoint_local, config=config),
263
+ get_datasource_datafile_cloud=partial(get_datasource_datafile_cloud, config=config),
264
+ get_datasource_datafile_local=partial(get_datasource_datafile_local, config=config),
265
+ get_pipe_datafile_cloud=partial(get_pipe_datafile_cloud, config=config),
266
+ get_pipe_datafile_local=partial(get_pipe_datafile_local, config=config),
267
+ get_connection_datafile_cloud=partial(get_connection_datafile_cloud, config=config),
268
+ get_connection_datafile_local=partial(get_connection_datafile_local, config=config),
240
269
  get_project_files=project.get_project_files,
241
270
  folder=folder,
242
271
  thinking_animation=thinking_animation,
@@ -250,6 +279,7 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
250
279
  )
251
280
  new_messages = result.new_messages()
252
281
  self.messages.extend(new_messages)
282
+ save_messages(new_messages)
253
283
  thinking_animation.stop()
254
284
  usage = result.usage()
255
285
  request_tokens = usage.request_tokens or 0
@@ -293,9 +323,12 @@ def run_agent(
293
323
  login(host, auth_host="https://cloud.tinybird.co", workspace=None, interactive=False, method="browser")
294
324
  click.echo()
295
325
  cli_config = CLIConfig.get_project_config()
326
+ config = {**config, **cli_config.to_dict()}
296
327
  token = cli_config.get_token()
297
328
  user_token = cli_config.get_user_token()
298
329
  host = cli_config.get_host()
330
+ workspace_id = cli_config.get("id", "")
331
+ workspace_name = cli_config.get("name", "")
299
332
 
300
333
  if not token or not host or not user_token:
301
334
  click.echo(
@@ -339,6 +372,7 @@ def run_agent(
339
372
  break
340
373
  elif user_input.lower() == "/clear":
341
374
  clear_history()
375
+ clear_messages()
342
376
  continue
343
377
  elif user_input.lower() == "/login":
344
378
  click.echo()
@@ -381,26 +415,25 @@ def build_project(config: dict[str, Any], project: Project, silent: bool = True,
381
415
 
382
416
  def deploy_project(config: dict[str, Any], project: Project) -> None:
383
417
  client = _get_tb_client(config["token"], config["host"])
384
- create_deployment(
385
- project=project,
386
- client=client,
387
- config=config,
388
- wait=True,
389
- auto=True,
390
- allow_destructive_operations=False,
391
- )
418
+ try:
419
+ create_deployment(
420
+ project=project,
421
+ client=client,
422
+ config=config,
423
+ wait=True,
424
+ auto=True,
425
+ allow_destructive_operations=False,
426
+ )
427
+ except SystemExit as e:
428
+ raise CLIDeploymentException(e.args[0])
392
429
 
393
430
 
394
431
  def deploy_check_project(config: dict[str, Any], project: Project) -> None:
395
432
  client = _get_tb_client(config["token"], config["host"])
396
- create_deployment(
397
- project=project,
398
- client=client,
399
- config=config,
400
- check=True,
401
- wait=True,
402
- auto=True,
403
- )
433
+ try:
434
+ create_deployment(project=project, client=client, config=config, check=True, wait=True, auto=True)
435
+ except SystemExit as e:
436
+ raise CLIDeploymentException(e.args[0])
404
437
 
405
438
 
406
439
  def append_data(config: dict[str, Any], datasource_name: str, path: str) -> None:
@@ -444,12 +477,12 @@ def analyze_fixture(config: dict[str, Any], fixture_path: str, format: str = "js
444
477
  return meta
445
478
 
446
479
 
447
- def execute_cloud_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> dict[str, Any]:
480
+ def execute_query_cloud(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> dict[str, Any]:
448
481
  client = _get_tb_client(config["token"], config["host"])
449
482
  return client.query(sql=query, pipeline=pipe_name)
450
483
 
451
484
 
452
- def execute_local_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> dict[str, Any]:
485
+ def execute_query_local(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> dict[str, Any]:
453
486
  local_client = get_tinybird_local_client(config, test=False, silent=True)
454
487
  return local_client.query(sql=query, pipeline=pipe_name)
455
488
 
@@ -466,3 +499,51 @@ def request_endpoint_local(
466
499
  ) -> dict[str, Any]:
467
500
  local_client = get_tinybird_local_client(config, test=False, silent=True)
468
501
  return local_client.pipe_data(endpoint_name, format="json", params=params)
502
+
503
+
504
+ def get_datasource_datafile_cloud(config: dict[str, Any], datasource_name: str) -> str:
505
+ try:
506
+ client = _get_tb_client(config["token"], config["host"])
507
+ return client.datasource_file(datasource_name)
508
+ except Exception:
509
+ return "Datasource not found"
510
+
511
+
512
+ def get_datasource_datafile_local(config: dict[str, Any], datasource_name: str) -> str:
513
+ try:
514
+ local_client = get_tinybird_local_client(config, test=False, silent=True)
515
+ return local_client.datasource_file(datasource_name)
516
+ except Exception:
517
+ return "Datasource not found"
518
+
519
+
520
+ def get_pipe_datafile_cloud(config: dict[str, Any], pipe_name: str) -> str:
521
+ try:
522
+ client = _get_tb_client(config["token"], config["host"])
523
+ return client.pipe_file(pipe_name)
524
+ except Exception:
525
+ return "Pipe not found"
526
+
527
+
528
+ def get_pipe_datafile_local(config: dict[str, Any], pipe_name: str) -> str:
529
+ try:
530
+ local_client = get_tinybird_local_client(config, test=False, silent=True)
531
+ return local_client.pipe_file(pipe_name)
532
+ except Exception:
533
+ return "Pipe not found"
534
+
535
+
536
+ def get_connection_datafile_cloud(config: dict[str, Any], connection_name: str) -> str:
537
+ try:
538
+ client = _get_tb_client(config["token"], config["host"])
539
+ return client.connection_file(connection_name)
540
+ except Exception:
541
+ return "Connection not found"
542
+
543
+
544
+ def get_connection_datafile_local(config: dict[str, Any], connection_name: str) -> str:
545
+ try:
546
+ local_client = get_tinybird_local_client(config, test=False, silent=True)
547
+ return local_client.connection_file(connection_name)
548
+ except Exception:
549
+ return "Connection not found"
@@ -1,7 +1,13 @@
1
+ import json
1
2
  from pathlib import Path
2
3
  from typing import Optional
3
4
 
5
+ import click
4
6
  from prompt_toolkit.history import FileHistory
7
+ from pydantic_ai.messages import ModelMessage, ModelMessagesTypeAdapter
8
+ from pydantic_core import to_jsonable_python
9
+
10
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
5
11
 
6
12
 
7
13
  def get_history_file_path():
@@ -39,3 +45,59 @@ def clear_history():
39
45
  """Clear the history file"""
40
46
  history_file = get_history_file_path()
41
47
  history_file.unlink(missing_ok=True)
48
+
49
+
50
+ def clear_messages():
51
+ """Clear the messages file"""
52
+ messages_file = get_messages_file_path()
53
+ messages_file.unlink(missing_ok=True)
54
+
55
+
56
+ def get_messages_file_path():
57
+ """Get the history file path based on current working directory"""
58
+ # Get current working directory
59
+ cwd = Path.cwd()
60
+
61
+ # Get user's home directory
62
+ home = Path.home()
63
+
64
+ # Calculate relative path from home to current directory
65
+ try:
66
+ relative_path = cwd.relative_to(home)
67
+ except ValueError:
68
+ # If current directory is not under home, use absolute path components
69
+ relative_path = Path(*cwd.parts[1:]) if cwd.is_absolute() else cwd
70
+
71
+ # Create history directory structure
72
+ history_dir = home / ".tinybird" / "projects" / relative_path
73
+ history_dir.mkdir(parents=True, exist_ok=True)
74
+
75
+ # Return history file path
76
+ return history_dir / "messages.json"
77
+
78
+
79
+ def load_messages() -> list[ModelMessage]:
80
+ try:
81
+ messages_file = get_messages_file_path()
82
+ messages_file.touch()
83
+ if not messages_file.exists():
84
+ messages_file.touch(exist_ok=True)
85
+ messages_file.write_text("[]")
86
+ return []
87
+ with open(messages_file, "r") as f:
88
+ messages_json = json.loads(f.read() or "[]")
89
+ return ModelMessagesTypeAdapter.validate_python(messages_json)
90
+ except Exception as e:
91
+ click.echo(FeedbackManager.error(message=f"Could not load previous messages: {e}"))
92
+ messages_file.unlink(missing_ok=True)
93
+ return []
94
+
95
+
96
+ def save_messages(new_messages: list[ModelMessage]):
97
+ messages_file = get_messages_file_path()
98
+ messages_file.touch(exist_ok=True)
99
+ messages = load_messages()
100
+ messages.extend(new_messages)
101
+ messages_json = to_jsonable_python(messages)
102
+ with open(messages_file, "w") as f:
103
+ f.write(json.dumps(messages_json))
@@ -1,4 +1,7 @@
1
1
  from pathlib import Path
2
+ from typing import Any
3
+
4
+ from pydantic_ai import format_as_xml
2
5
 
3
6
  from tinybird.tb.modules.project import Project
4
7
 
@@ -73,9 +76,10 @@ sql_instructions = """
73
76
  - Use node names as table names only when nodes are present in the same file.
74
77
  - Do not reference the current node name in the SQL.
75
78
  - SQL queries only accept SELECT statements with conditions, aggregations, joins, etc.
76
- - Do NOT use CREATE TABLE, INSERT INTO, CREATE DATABASE, etc.
79
+ - Do NOT use CREATE TABLE, INSERT INTO, CREATE DATABASE, SHOW TABLES, etc.
77
80
  - Use ONLY SELECT statements in the SQL section.
78
81
  - INSERT INTO is not supported in SQL section.
82
+ - Do NOT query system.<table_name> tables.
79
83
  - When using functions try always ClickHouse functions first, then SQL functions.
80
84
  - Parameters are never quoted in any case.
81
85
  - Use the following syntax in the SQL section for the iceberg table function: iceberg('s3://bucket/path/to/table', {{tb_secret('aws_access_key_id')}}, {{tb_secret('aws_secret_access_key')}})
@@ -101,38 +105,32 @@ def resources_prompt(project: Project) -> str:
101
105
 
102
106
  resources_content = "# Existing resources in the project:\n"
103
107
  if files:
104
- paths = [Path(file_path) for file_path in files]
105
-
106
- resources_content += "\n".join(
107
- [
108
- f"""
109
- <resource>
110
- <path>{file_path.relative_to(project.folder)}</path>
111
- <type>{get_resource_type(file_path)}</type>
112
- <name>{file_path.stem}</name>
113
- <content>{file_path.read_text()}</content>
114
- </resource>
115
- """
116
- for file_path in paths
117
- ]
118
- )
108
+ resources: list[dict[str, Any]] = []
109
+ for filename in files:
110
+ file_path = Path(filename)
111
+ resource = {
112
+ "path": str(file_path.relative_to(project.folder)),
113
+ "type": get_resource_type(file_path),
114
+ "name": file_path.stem,
115
+ "content": file_path.read_text(),
116
+ }
117
+ resources.append(resource)
118
+ resources_content = format_as_xml(resources, root_tag="resources", item_tag="resource")
119
119
  else:
120
120
  resources_content += "No resources found"
121
121
 
122
122
  fixture_content = "# Fixture files in the project:\n"
123
123
  if fixture_files:
124
- paths = [Path(file_path) for file_path in fixture_files]
125
- fixture_content += "\n".join(
126
- [
127
- f"""
128
- <fixture>
129
- <path>{file_path.relative_to(project.folder)}</path>
130
- <name>{file_path.stem}</name>
131
- </fixture>
132
- """
133
- for file_path in paths
134
- ]
135
- )
124
+ fixtures: list[dict[str, Any]] = []
125
+ for filename in fixture_files:
126
+ file_path = Path(filename)
127
+ fixture = {
128
+ "path": str(file_path.relative_to(project.folder)),
129
+ "name": file_path.stem,
130
+ }
131
+ fixtures.append(fixture)
132
+ fixture_content = format_as_xml(fixtures, root_tag="fixtures", item_tag="fixture")
133
+
136
134
  else:
137
135
  fixture_content += "No fixture files found"
138
136
 
@@ -384,3 +382,115 @@ SQL >
384
382
  5. **JOIN carefully** - Consider alternatives and optimize when necessary
385
383
  </endpoint_optimization_instructions>
386
384
  """
385
+
386
+
387
+ sql_agent_instructions = """
388
+ # SQL Best Practices Rules
389
+
390
+ ## Core Principles
391
+ 1. **The best data is the data you don't write** - Don't save unnecessary data
392
+ 2. **The second best data is the one you don't read** - Filter as early as possible
393
+ 3. **Sequential reads are much faster** - Use proper indexes and sorting keys
394
+ 4. **The less data you process after read, the better** - Select only needed columns
395
+ 5. **Perform complex operations later in the processing pipeline** - Filter before joins/aggregations
396
+
397
+ ## SQL Query Rules
398
+
399
+ ### 1. Filter Placement Rules
400
+ - **ALWAYS** apply WHERE filters before ORDER BY clauses
401
+ - **ALWAYS** apply WHERE filters before GROUP BY operations
402
+ - **ALWAYS** filter data at the earliest possible point in the query
403
+ - **NEVER** sort data before filtering it
404
+
405
+ ### 2. Column Selection Rules
406
+ - **NEVER** use SELECT * in production queries
407
+ - **ALWAYS** specify only the columns you need
408
+ - **ALWAYS** minimize the number of columns retrieved to reduce memory usage
409
+
410
+ ### 3. Sorting and Index Rules
411
+ - **ALWAYS** filter by ENGINE_SORTING_KEY columns first (typically date/time columns)
412
+ - **ALWAYS** order filtering conditions from most to least selective
413
+ - **ALWAYS** use columns in ENGINE_SORTING_KEY for WHERE clauses when possible
414
+ - **NEVER** use functions on indexed columns in WHERE clauses (e.g., avoid DATE_FORMAT, EXTRACT)
415
+
416
+ ### 4. Join Optimization Rules
417
+ - **ALWAYS** pre-filter data before JOIN operations
418
+ - **NEVER** join tables with more than 1 million rows without filtering
419
+ - **ALWAYS** filter the right-side table in joins using subqueries
420
+ - **PREFERRED** pattern for large joins:
421
+ ```sql
422
+ -- Good: Pre-filter right table
423
+ FROM left_table AS left
424
+ INNER JOIN (
425
+ SELECT id, column FROM right_table
426
+ WHERE id IN (SELECT id FROM left_table)
427
+ ) AS right ON left.id = right.id
428
+ ```
429
+
430
+ ### 5. Aggregation Rules
431
+ - **NEVER** use nested aggregate functions (e.g., MAX(AVG(column)))
432
+ - **ALWAYS** use subqueries instead of nested aggregates
433
+ - **ALWAYS** filter data before GROUP BY operations
434
+ - **ALWAYS** perform aggregations as late as possible in the query
435
+
436
+ ### 6. Complex Operations Order
437
+ - **ALWAYS** follow this operation order:
438
+ 1. Filter (WHERE)
439
+ 2. Select only needed columns
440
+ 3. Join (if necessary)
441
+ 4. Group/Aggregate (if necessary)
442
+ 5. Sort (ORDER BY)
443
+ 6. Limit
444
+
445
+ ### 7. Aggregate Function Rules
446
+ - **ALWAYS** use -Merge combinators (countMerge, avgMerge, etc.) when querying AggregateFunction columns
447
+ - **ALWAYS** apply -Merge functions as late as possible in the pipeline
448
+ - **NEVER** select AggregateFunction columns without the appropriate -Merge combinator
449
+
450
+ ### 8. Performance Rules
451
+ - **AVOID** full table scans - always include WHERE clauses
452
+ - **AVOID** reading more than 1GB of data in a single query
453
+ - **AVOID** operations that load large datasets into memory
454
+ - **MINIMIZE** the number of rows processed at each step
455
+
456
+ ### 9. Memory Optimization Rules
457
+ - **REDUCE** column count when hitting memory limits
458
+ - **AVOID** cross JOINs that generate excessive rows
459
+ - **FILTER** before massive GROUP BY operations
460
+ - **CHUNK** large populate operations (they run in 1M row chunks)
461
+
462
+ ### 10. Query Pattern Examples
463
+
464
+ **BAD Pattern - Filtering after sorting:**
465
+ ```sql
466
+ SELECT * FROM table ORDER BY date WHERE condition = true
467
+ ```
468
+
469
+ **GOOD Pattern - Filtering before sorting:**
470
+ ```sql
471
+ SELECT column1, column2 FROM table WHERE condition = true ORDER BY date
472
+ ```
473
+
474
+ **BAD Pattern - Nested aggregates:**
475
+ ```sql
476
+ SELECT MAX(AVG(amount)) FROM table
477
+ ```
478
+
479
+ **GOOD Pattern - Using subquery:**
480
+ ```sql
481
+ SELECT MAX(avg_amount) FROM (SELECT AVG(amount) as avg_amount FROM table)
482
+ ```
483
+
484
+ **BAD Pattern - Unfiltered join:**
485
+ ```sql
486
+ SELECT * FROM small_table JOIN huge_table ON small_table.id = huge_table.id
487
+ ```
488
+
489
+ **GOOD Pattern - Pre-filtered join:**
490
+ ```sql
491
+ SELECT needed_columns
492
+ FROM small_table
493
+ JOIN (SELECT id, col FROM huge_table WHERE id IN (SELECT id FROM small_table)) filtered
494
+ ON small_table.id = filtered.id
495
+ ```
496
+ """
@@ -41,10 +41,12 @@ def append_file(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fix
41
41
  ctx.deps.thinking_animation.start()
42
42
  return f"Data appended to {datasource_name}"
43
43
  except Exception as e:
44
+ error_message = str(e)
44
45
  ctx.deps.thinking_animation.stop()
45
- click.echo(FeedbackManager.error(message=e))
46
+ click.echo(FeedbackManager.error(message=error_message))
47
+ error_message = handle_quarantine_error(ctx, error_message, datasource_name)
46
48
  ctx.deps.thinking_animation.start()
47
- return f"Error appending fixture {fixture_pathname} to {datasource_name}: {e}"
49
+ return f"Error appending fixture {fixture_pathname} to {datasource_name}: {error_message}"
48
50
 
49
51
 
50
52
  def append_url(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_url: str) -> str:
@@ -81,7 +83,28 @@ def append_url(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixt
81
83
  ctx.deps.thinking_animation.start()
82
84
  return f"Data appended to {datasource_name}"
83
85
  except Exception as e:
86
+ error_message = str(e)
84
87
  ctx.deps.thinking_animation.stop()
85
- click.echo(FeedbackManager.error(message=e))
88
+ click.echo(FeedbackManager.error(message=error_message))
89
+ error_message = handle_quarantine_error(ctx, error_message, datasource_name)
86
90
  ctx.deps.thinking_animation.start()
87
- return f"Error appending URL {fixture_url} to {datasource_name}: {e}"
91
+ return f"Error appending URL {fixture_url} to {datasource_name}: {error_message}"
92
+
93
+
94
+ def handle_quarantine_error(ctx: RunContext[TinybirdAgentContext], error_message: str, datasource_name: str) -> str:
95
+ try:
96
+ if "in quarantine" in error_message:
97
+ click.echo(FeedbackManager.highlight(message=f"\n» Looking for errors in {datasource_name}_quarantine..."))
98
+ query = (
99
+ f"select * from {datasource_name}_quarantine order by insertion_date desc limit 5 FORMAT CSVWithNames"
100
+ )
101
+ quarantine_data = ctx.deps.execute_query_local(query=query)
102
+ error_message = (
103
+ error_message
104
+ + f"\nThese are the first 5 rows of the quarantine table for datasource '{datasource_name}':\n{quarantine_data}. Use again `mock` tool but add this issue to the context."
105
+ )
106
+
107
+ except Exception as quarantine_error:
108
+ error_message = error_message + f"\nError accessing to {datasource_name}_quarantine: {quarantine_error}"
109
+
110
+ return error_message
@@ -30,7 +30,5 @@ def deploy(ctx: RunContext[TinybirdAgentContext]) -> str:
30
30
  ctx.deps.thinking_animation.start()
31
31
  return "Project deployed successfully"
32
32
  except Exception as e:
33
- ctx.deps.thinking_animation.stop()
34
- click.echo(FeedbackManager.error(message=e))
35
33
  ctx.deps.thinking_animation.start()
36
34
  return f"Error depoying project: {e}"
@@ -2,7 +2,6 @@ import click
2
2
  from pydantic_ai import RunContext
3
3
 
4
4
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
5
- from tinybird.tb.modules.feedback_manager import FeedbackManager
6
5
 
7
6
 
8
7
  def deploy_check(ctx: RunContext[TinybirdAgentContext]) -> str:
@@ -28,7 +27,5 @@ def deploy_check(ctx: RunContext[TinybirdAgentContext]) -> str:
28
27
  ctx.deps.thinking_animation.start()
29
28
  return "Project can be deployed"
30
29
  except Exception as e:
31
- ctx.deps.thinking_animation.stop()
32
- click.echo(FeedbackManager.error(message=e))
33
30
  ctx.deps.thinking_animation.start()
34
31
  return f"Project cannot be deployed: {e}"
@@ -0,0 +1,51 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+ from pydantic_ai import RunContext
5
+
6
+ from tinybird.tb.modules.agent.utils import Datafile, TinybirdAgentContext
7
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
8
+
9
+
10
+ def diff_resource(ctx: RunContext[TinybirdAgentContext], resource: Datafile) -> str:
11
+ """Diff the content of a resource in Tinybird Cloud vs Tinybird Local vs Project local file
12
+
13
+ Args:
14
+ resource (Datafile): The resource to diff. Required.
15
+
16
+ Returns:
17
+ Datafile: The diff of the resource.
18
+ """
19
+ try:
20
+ ctx.deps.thinking_animation.stop()
21
+ click.echo(
22
+ FeedbackManager.highlight(message=f"\n» Comparing content of {resource.pathname} with Tinybird Cloud")
23
+ )
24
+ resource.pathname = resource.pathname.removeprefix("/")
25
+ project_file_path = Path(ctx.deps.folder) / resource.pathname
26
+ if not project_file_path.exists():
27
+ raise Exception(f"Resource {resource.pathname} not found in project")
28
+
29
+ project_file_content = project_file_path.read_text()
30
+ if resource.type == "datasource":
31
+ cloud_content = ctx.deps.get_datasource_datafile_cloud(datasource_name=resource.name)
32
+ local_content = ctx.deps.get_datasource_datafile_local(datasource_name=resource.name)
33
+ elif resource.type == "connection":
34
+ cloud_content = ctx.deps.get_connection_datafile_cloud(connection_name=resource.name)
35
+ local_content = ctx.deps.get_connection_datafile_local(connection_name=resource.name)
36
+ elif resource.type in ["endpoint", "materialized", "sink", "copy"]:
37
+ cloud_content = ctx.deps.get_pipe_datafile_cloud(pipe_name=resource.name)
38
+ local_content = ctx.deps.get_pipe_datafile_local(pipe_name=resource.name)
39
+ else:
40
+ raise Exception(f"{resource.type} is not a valid extension")
41
+
42
+ needs_to_build = project_file_content != local_content
43
+ needs_to_deploy = project_file_content != cloud_content
44
+ ctx.deps.thinking_animation.start()
45
+ diff = f"# Diff of resource {resource.name}:\n"
46
+ diff += f"## Tinybird Cloud: {'Deploy needed. Resource does not exist or needs to be updated. Run `deploy` tool to deploy the resource.' if needs_to_deploy else 'Nothing to deploy.'}\n"
47
+ diff += f"## Tinybird Local: {'Build needed. Resource does not exist or needs to be updated. Run `build` tool to build the resource.' if needs_to_build else 'Nothing to build.'}\n"
48
+ return diff
49
+ except Exception as e:
50
+ ctx.deps.thinking_animation.start()
51
+ return f"Could not diff resource {resource.pathname}: {e}"
@@ -3,7 +3,7 @@ import humanfriendly
3
3
  from pydantic_ai import RunContext
4
4
 
5
5
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext
6
- from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table
6
+ from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_pretty_table
7
7
  from tinybird.tb.modules.feedback_manager import FeedbackManager
8
8
 
9
9
 
@@ -32,7 +32,7 @@ def execute_query(ctx: RunContext[TinybirdAgentContext], query: str, task: str,
32
32
  else:
33
33
  query = f"SELECT * FROM ({query}) {query_format}"
34
34
 
35
- execute_query = ctx.deps.execute_cloud_query if cloud else ctx.deps.execute_local_query
35
+ execute_query = ctx.deps.execute_query_cloud if cloud else ctx.deps.execute_query_local
36
36
  result = execute_query(query=query)
37
37
  stats = result["statistics"]
38
38
  seconds = stats["elapsed"]
@@ -44,9 +44,10 @@ def execute_query(ctx: RunContext[TinybirdAgentContext], query: str, task: str,
44
44
  if not result["data"]:
45
45
  click.echo(FeedbackManager.info_no_rows())
46
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()
47
+ echo_safe_humanfriendly_tables_format_pretty_table(
48
+ data=[d.values() for d in result["data"][:10]], column_names=result["data"][0].keys()
49
49
  )
50
+ click.echo("Showing first 10 results\n")
50
51
  ctx.deps.thinking_animation.start()
51
52
  result["data"] = result["data"][:10]
52
53
  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."
@@ -42,7 +42,7 @@ def get_endpoint_stats(
42
42
  FORMAT JSON
43
43
  """
44
44
 
45
- execute_query = ctx.deps.execute_cloud_query if cloud else ctx.deps.execute_local_query
45
+ execute_query = ctx.deps.execute_query_cloud if cloud else ctx.deps.execute_query_local
46
46
 
47
47
  result = execute_query(query=query)
48
48
  click.echo(FeedbackManager.success(message="✓ Done!"))
@@ -60,7 +60,7 @@ def mock(
60
60
  FeedbackManager.highlight(message=f"\n» Looking for errors in {datasource_name}_quarantine...")
61
61
  )
62
62
  query = f"select * from {datasource_name}_quarantine order by insertion_date desc limit 5 FORMAT CSVWithNames"
63
- quarantine_data = ctx.deps.execute_local_query(query=query)
63
+ quarantine_data = ctx.deps.execute_query_local(query=query)
64
64
  error_message = (
65
65
  error_message
66
66
  + f"\nThese are the first 5 rows of the quarantine table for datasource '{datasource_name}':\n{quarantine_data}. Use again `mock` tool but add this issue to the context."
@@ -16,7 +16,7 @@ def preview_datafile(name: str, type: str, description: str, content: str, pathn
16
16
  """
17
17
 
18
18
  return Datafile(
19
- type=type,
19
+ type=type.lower(),
20
20
  name=name,
21
21
  content=content,
22
22
  description=description,
@@ -5,7 +5,7 @@ import humanfriendly
5
5
  from pydantic_ai import RunContext
6
6
 
7
7
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext
8
- from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table
8
+ from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_pretty_table
9
9
  from tinybird.tb.modules.feedback_manager import FeedbackManager
10
10
 
11
11
 
@@ -48,10 +48,10 @@ def request_endpoint(
48
48
  if not result["data"]:
49
49
  click.echo(FeedbackManager.info_no_rows())
50
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()
51
+ echo_safe_humanfriendly_tables_format_pretty_table(
52
+ data=[d.values() for d in result["data"][:10]], column_names=result["data"][0].keys()
53
53
  )
54
- click.echo("Showing first 5 results\n")
54
+ click.echo("Showing first 10 results\n")
55
55
  ctx.deps.thinking_animation.start()
56
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
57
  except Exception as e:
@@ -41,10 +41,16 @@ 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[..., dict[str, Any]]
45
- execute_local_query: Callable[..., dict[str, Any]]
44
+ execute_query_cloud: Callable[..., dict[str, Any]]
45
+ execute_query_local: Callable[..., dict[str, Any]]
46
46
  request_endpoint_cloud: Callable[..., dict[str, Any]]
47
47
  request_endpoint_local: Callable[..., dict[str, Any]]
48
+ get_datasource_datafile_cloud: Callable[..., str]
49
+ get_datasource_datafile_local: Callable[..., str]
50
+ get_pipe_datafile_cloud: Callable[..., str]
51
+ get_pipe_datafile_local: Callable[..., str]
52
+ get_connection_datafile_cloud: Callable[..., str]
53
+ get_connection_datafile_local: Callable[..., str]
48
54
  dangerously_skip_permissions: bool
49
55
  token: str
50
56
  user_token: str
@@ -124,6 +124,23 @@ def echo_safe_humanfriendly_tables_format_smart_table(data: Iterable[Any], colum
124
124
  raise exc
125
125
 
126
126
 
127
+ def echo_safe_humanfriendly_tables_format_pretty_table(data: Iterable[Any], column_names: List[str]) -> None:
128
+ """
129
+ There is a bug in the humanfriendly library: it breaks to render the small table for small terminals
130
+ (`format_robust_table`) if we call format_smart_table with an empty dataset. This catches the error and prints
131
+ what we would call an empty "robust_table".
132
+ """
133
+ try:
134
+ click.echo(humanfriendly.tables.format_pretty_table(data, column_names=column_names))
135
+ except ValueError as exc:
136
+ if str(exc) == "max() arg is an empty sequence":
137
+ click.echo("------------")
138
+ click.echo("Empty")
139
+ click.echo("------------")
140
+ else:
141
+ raise exc
142
+
143
+
127
144
  def echo_safe_format_table(data: Iterable[Any], columns) -> None:
128
145
  """
129
146
  There is a bug in the humanfriendly library: it breaks to render the small table for small terminals
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev254
3
+ Version: 0.0.1.dev256
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -3,7 +3,7 @@ tinybird/context.py,sha256=FfqYfrGX_I7PKGTQo93utaKPDNVYWelg4Hsp3evX5wM,1291
3
3
  tinybird/datatypes.py,sha256=r4WCvspmrXTJHiPjjyOTiZyZl31FO3Ynkwq4LQsYm6E,11059
4
4
  tinybird/feedback_manager.py,sha256=1INQFfRfuMCb9lfB8KNf4r6qC2khW568hoHjtk-wshI,69305
5
5
  tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
6
- tinybird/prompts.py,sha256=4VmdaMX7oUFoqjseXe8QuF9wTtIabbDkdwVGmd34S7s,45502
6
+ tinybird/prompts.py,sha256=9zXOYI8TzFAyp_bolqtxhtaqg0c1u4AJolFM0o3aYiY,45393
7
7
  tinybird/sql.py,sha256=BufnOgclQokDyihtuXesOwHBsebN6wRXIxO5wKRkOwE,48299
8
8
  tinybird/sql_template.py,sha256=AezE1o6_BzbHFi0J9OIqTrXQ5WvoX5eNVq4QCbFjGcs,100338
9
9
  tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
@@ -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=dY306yA2ppVsNM4k4b-zkQx3D8E5suyaU1zBkami7R0,247
20
+ tinybird/tb/__cli__.py,sha256=BW9MQs4o1rm0FvIF2XLehXnuzpfC7A9JS2Fu2hJCJ2Y,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
@@ -26,7 +26,7 @@ tinybird/tb/modules/build.py,sha256=efD-vamK1NPaDo9R86Hn8be2DYoW0Hh5bZiH7knK5dk,
26
26
  tinybird/tb/modules/build_common.py,sha256=rWhemU8bk0ZE2eiwZDaTmV9cPabDGGlyc2WnRxfhT0M,12859
27
27
  tinybird/tb/modules/cicd.py,sha256=0KLKccha9IP749QvlXBmzdWv1On3mFwMY4DUcJlBxiE,7326
28
28
  tinybird/tb/modules/cli.py,sha256=1kErLFhxgMWldbE7P4-bkPUcug8ymRyXDHRG9-vGb_4,16755
29
- tinybird/tb/modules/common.py,sha256=jTTaDDHrZREt--032XhP6GkbfFwC79YJ5aH1Sl7bmbo,81925
29
+ tinybird/tb/modules/common.py,sha256=tj6DR2yOqMMQ0PILwFGXmMogxdrbQCgj36HdSM611rs,82657
30
30
  tinybird/tb/modules/config.py,sha256=gK7rgaWTDd4ZKCrNEg_Uemr26EQjqWt6TjyQKujxOws,11462
31
31
  tinybird/tb/modules/connection.py,sha256=-MY56NUAai6EMC4-wpi7bT0_nz_SA8QzTmHkV7HB1IQ,17810
32
32
  tinybird/tb/modules/copy.py,sha256=dPZkcIDvxjJrlQUIvToO0vsEEEs4EYumbNV77-BzNoU,4404
@@ -68,28 +68,29 @@ 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=lemqCQyYYnoSqp3gVPMAc0J7b4OrLy8sCpUk1_Vy9bo,22150
71
+ tinybird/tb/modules/agent/agent.py,sha256=mCQRrrheH7mEgRg6BF19lSLt2XzaD_mw6mhyugTQLyw,26412
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
- tinybird/tb/modules/agent/memory.py,sha256=H6SJK--2L5C87B7AJd_jMqsq3sCvFvZwZXmajuT0GBE,1171
74
+ tinybird/tb/modules/agent/memory.py,sha256=O6Kumn9AyKxcTkhI45yjAUZ3ZIAibLOcNWoiEuLYeqY,3245
75
75
  tinybird/tb/modules/agent/models.py,sha256=LW1D27gjcd_jwFmghEzteCgToDfodX2B6B5S8BYbysw,735
76
- tinybird/tb/modules/agent/prompts.py,sha256=-2Gazjrh3NGjSqtw9yZ8Up-tQLDD-kKGj7rwPSk71oY,13377
77
- tinybird/tb/modules/agent/utils.py,sha256=me5-kflBxV4N4psBSxI3vAfmFvAtsSnboc7ND3M9omw,26249
76
+ tinybird/tb/modules/agent/prompts.py,sha256=qyAv3H1x9qctlYQSel0DHxLlRJM2_8HTg7M-foSoR0k,17567
77
+ tinybird/tb/modules/agent/utils.py,sha256=5mUnc4LZATHLzQZThotNErzZdHQDwK3eur6W4NZULWA,26561
78
78
  tinybird/tb/modules/agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
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
+ tinybird/tb/modules/agent/tools/append.py,sha256=ekG7OxOzPjjjAjYUONAfp_PWlzyLT1GDfQhECgO-DYY,5065
81
81
  tinybird/tb/modules/agent/tools/build.py,sha256=LhzJMx6tbxC7gogIrxhfKJc-SDgoSR-FC6IunfaCdn8,758
82
82
  tinybird/tb/modules/agent/tools/create_datafile.py,sha256=wcPcChACTIFKw0lKFTlhm0sWJKhQkMLPLnGNpKyeETA,2962
83
- tinybird/tb/modules/agent/tools/deploy.py,sha256=WrsSlaufKGOBx0S13uoMQQH2DnKue5LQ231Rx4RXh2I,1443
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
83
+ tinybird/tb/modules/agent/tools/deploy.py,sha256=dxNmzz8vCLIPNrfwyzof74gJK5cF-GfKHUfQ1vEX4hg,1347
84
+ tinybird/tb/modules/agent/tools/deploy_check.py,sha256=9roUI7FOvhNXbvi1iilS1RNYso4-WsNDVR9m7rmeOy4,1170
85
+ tinybird/tb/modules/agent/tools/diff_resource.py,sha256=euKo_mD9FZHC1X6yESv3D_CXMzBMB8EWHLeZjIRqjJg,2637
86
+ tinybird/tb/modules/agent/tools/execute_query.py,sha256=hMRahWZkyP9qa-nMGzY0Z7MjDa9sb5O9V1evmA_V_w8,2702
86
87
  tinybird/tb/modules/agent/tools/explore.py,sha256=ihALc_kBcsjrKT3hZyicqyIowB0g_K3AtNNi-5uz9-8,412
87
- tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=_3wAvDykJitIOb5BRnP7wCy6y06y1qlULHLWB-MvS2M,1705
88
+ tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=E9yPi9LwnpsTyjFd8EaiSNvDGVPkFSNqp_tZxg_pWqs,1705
88
89
  tinybird/tb/modules/agent/tools/get_openapi_definition.py,sha256=9cQ-SUeB1NVhPJN1s8aQh9KQxqI9-DEEW1Ot5r2JbOk,1575
89
- tinybird/tb/modules/agent/tools/mock.py,sha256=Omog_gdEdm8YuBXNrJdHwxHqjL_ji9UIr75mALF4ozI,3408
90
+ tinybird/tb/modules/agent/tools/mock.py,sha256=4gEAPZCdTPo1w-fbryWiEx3hPXOK2ZfW0MpXY7smpcI,3408
90
91
  tinybird/tb/modules/agent/tools/plan.py,sha256=pr6LnItz6vlOeCG8GE459ExsrBEG0KLx-g02SZGNjXU,1217
91
- tinybird/tb/modules/agent/tools/preview_datafile.py,sha256=e9q5fR0afApcrntzFrnuHmd10ex7MG_GM6T0Pwc9bRI,850
92
- tinybird/tb/modules/agent/tools/request_endpoint.py,sha256=iwzjYLtX_4YS7b9KzRwPGtVkx2UNujMAXv8m7mm8Fac,2683
92
+ tinybird/tb/modules/agent/tools/preview_datafile.py,sha256=Gbao_FxhXstnUnngVQxztpizjugyfx1rOXTkw7Yabls,858
93
+ tinybird/tb/modules/agent/tools/request_endpoint.py,sha256=Jl64ln0Jspu_rmp3ycZabj-2IXkmWFSZxoCdcavRpQo,2687
93
94
  tinybird/tb/modules/datafile/build.py,sha256=NFKBrusFLU0WJNCXePAFWiEDuTaXpwc0lHlOQWEJ43s,51117
94
95
  tinybird/tb/modules/datafile/build_common.py,sha256=2yNdxe49IMA9wNvl25NemY2Iaz8L66snjOdT64dm1is,4511
95
96
  tinybird/tb/modules/datafile/build_datasource.py,sha256=Ra8pVQBDafbFRUKlhpgohhTsRyp_ADKZJVG8Gd69idY,17227
@@ -110,8 +111,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
110
111
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
111
112
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
112
113
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
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,,
114
+ tinybird-0.0.1.dev256.dist-info/METADATA,sha256=QFATRwPa6QPBUcV7rO4TwGvOBOyt5Dd2VmFjlnFUdV0,1733
115
+ tinybird-0.0.1.dev256.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
116
+ tinybird-0.0.1.dev256.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
117
+ tinybird-0.0.1.dev256.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
118
+ tinybird-0.0.1.dev256.dist-info/RECORD,,