tinybird 0.0.1.dev282__tar.gz → 0.0.1.dev284__tar.gz

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.

Files changed (148) hide show
  1. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/ch_utils/constants.py +1 -0
  3. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/datafile/common.py +12 -8
  4. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/__cli__.py +2 -2
  5. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/agent.py +39 -13
  6. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/explore_agent.py +5 -0
  7. tinybird-0.0.1.dev284/tinybird/tb/modules/agent/mock_agent.py +206 -0
  8. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/datafile.py +1 -1
  9. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/deploy.py +6 -1
  10. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/deploy_check.py +3 -0
  11. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/execute_query.py +10 -12
  12. tinybird-0.0.1.dev284/tinybird/tb/modules/agent/tools/mock.py +135 -0
  13. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/request_endpoint.py +10 -6
  14. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird.egg-info/PKG-INFO +1 -1
  15. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird.egg-info/SOURCES.txt +1 -0
  16. tinybird-0.0.1.dev282/tinybird/tb/modules/agent/tools/mock.py +0 -87
  17. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/setup.cfg +0 -0
  18. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/__cli__.py +0 -0
  19. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/ch_utils/engine.py +0 -0
  20. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/check_pypi.py +0 -0
  21. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/client.py +0 -0
  22. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/config.py +0 -0
  23. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/connectors.py +0 -0
  24. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/context.py +0 -0
  25. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/datafile/exceptions.py +0 -0
  26. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/datafile/parse_connection.py +0 -0
  27. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/datafile/parse_datasource.py +0 -0
  28. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/datafile/parse_pipe.py +0 -0
  29. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/datatypes.py +0 -0
  30. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/feedback_manager.py +0 -0
  31. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/git_settings.py +0 -0
  32. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/prompts.py +0 -0
  33. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/sql.py +0 -0
  34. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/sql_template.py +0 -0
  35. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/sql_template_fmt.py +0 -0
  36. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/sql_toolset.py +0 -0
  37. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/syncasync.py +0 -0
  38. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/check_pypi.py +0 -0
  39. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/cli.py +0 -0
  40. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/client.py +0 -0
  41. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/config.py +0 -0
  42. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/__init__.py +0 -0
  43. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/animations.py +0 -0
  44. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/banner.py +0 -0
  45. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/command_agent.py +0 -0
  46. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/compactor.py +0 -0
  47. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/memory.py +0 -0
  48. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/models.py +0 -0
  49. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/prompts.py +0 -0
  50. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/testing_agent.py +0 -0
  51. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/__init__.py +0 -0
  52. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/analyze.py +0 -0
  53. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/append.py +0 -0
  54. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/build.py +0 -0
  55. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/diff_resource.py +0 -0
  56. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/get_endpoint_stats.py +0 -0
  57. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/get_openapi_definition.py +0 -0
  58. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/plan.py +0 -0
  59. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/run_command.py +0 -0
  60. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/secret.py +0 -0
  61. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/tools/test.py +0 -0
  62. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/agent/utils.py +0 -0
  63. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/build.py +0 -0
  64. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/build_common.py +0 -0
  65. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/cicd.py +0 -0
  66. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/cli.py +0 -0
  67. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/common.py +0 -0
  68. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/config.py +0 -0
  69. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/connection.py +0 -0
  70. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/copy.py +0 -0
  71. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/create.py +0 -0
  72. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/build.py +0 -0
  73. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/build_common.py +0 -0
  74. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  75. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  76. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/diff.py +0 -0
  77. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/fixture.py +0 -0
  78. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/format_common.py +0 -0
  79. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  80. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  81. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  82. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/playground.py +0 -0
  83. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datafile/pull.py +0 -0
  84. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/datasource.py +0 -0
  85. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/deployment.py +0 -0
  86. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/deployment_common.py +0 -0
  87. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/deprecations.py +0 -0
  88. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/dev_server.py +0 -0
  89. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/endpoint.py +0 -0
  90. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/exceptions.py +0 -0
  91. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/feedback_manager.py +0 -0
  92. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/info.py +0 -0
  93. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/infra.py +0 -0
  94. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/job.py +0 -0
  95. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/llm.py +0 -0
  96. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/llm_utils.py +0 -0
  97. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/local.py +0 -0
  98. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/local_common.py +0 -0
  99. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/login.py +0 -0
  100. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/login_common.py +0 -0
  101. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/logout.py +0 -0
  102. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/materialization.py +0 -0
  103. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/mock.py +0 -0
  104. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/mock_common.py +0 -0
  105. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/open.py +0 -0
  106. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/pipe.py +0 -0
  107. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/project.py +0 -0
  108. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/regions.py +0 -0
  109. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/secret.py +0 -0
  110. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/secret_common.py +0 -0
  111. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/shell.py +0 -0
  112. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/sink.py +0 -0
  113. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/table.py +0 -0
  114. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/telemetry.py +0 -0
  115. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/test.py +0 -0
  116. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/test_common.py +0 -0
  117. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  118. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  119. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/token.py +0 -0
  120. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/watch.py +0 -0
  121. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/workspace.py +0 -0
  122. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb/modules/workspace_members.py +0 -0
  123. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli.py +0 -0
  124. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/auth.py +0 -0
  125. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/branch.py +0 -0
  126. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/cicd.py +0 -0
  127. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/cli.py +0 -0
  128. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/common.py +0 -0
  129. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/config.py +0 -0
  130. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/connection.py +0 -0
  131. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/datasource.py +0 -0
  132. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/exceptions.py +0 -0
  133. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/fmt.py +0 -0
  134. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/job.py +0 -0
  135. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/pipe.py +0 -0
  136. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/regions.py +0 -0
  137. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/tag.py +0 -0
  138. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/telemetry.py +0 -0
  139. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/test.py +0 -0
  140. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  141. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  142. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/workspace.py +0 -0
  143. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  144. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird/tornado_template.py +0 -0
  145. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird.egg-info/dependency_links.txt +0 -0
  146. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird.egg-info/entry_points.txt +0 -0
  147. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird.egg-info/requires.txt +0 -0
  148. {tinybird-0.0.1.dev282 → tinybird-0.0.1.dev284}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev282
3
+ Version: 0.0.1.dev284
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -257,4 +257,5 @@ VALID_QUERY_FORMATS = (
257
257
  "Native",
258
258
  "RowBinaryWithNamesAndTypes",
259
259
  "TabSeparated",
260
+ "JSONCompactEachRowWithNamesAndTypes",
260
261
  )
@@ -184,7 +184,7 @@ class CopyParameters(Parameters):
184
184
 
185
185
  class MaterializedParameters(Parameters):
186
186
  MANDATORY_ATTRIBUTES = PipeParameters.MANDATORY_ATTRIBUTES.union({"datasource"})
187
- ACCEPTED_ATTRIBUTES = PipeParameters.ACCEPTED_ATTRIBUTES.union(MANDATORY_ATTRIBUTES)
187
+ ACCEPTED_ATTRIBUTES = PipeParameters.ACCEPTED_ATTRIBUTES.union(MANDATORY_ATTRIBUTES).union({"deployment_method"})
188
188
 
189
189
 
190
190
  class SinkParameters(Parameters):
@@ -294,6 +294,13 @@ class Datafile:
294
294
  def set_kind(self, kind: DatafileKind):
295
295
  self.kind = kind
296
296
 
297
+ def validate_standard_node(self, node: Dict[str, Any]):
298
+ for key in node.keys():
299
+ if key not in PipeParameters.valid_params():
300
+ raise DatafileValidationError(
301
+ f"Standard node {repr(node['name'])} has an invalid attribute ({PipeParameters.canonical_name(key)})"
302
+ )
303
+
297
304
  def validate_copy_node(self, node: Dict[str, Any]):
298
305
  if missing := [param for param in CopyParameters.required_params() if param not in node]:
299
306
  raise DatafileValidationError(
@@ -412,13 +419,6 @@ class Datafile:
412
419
 
413
420
  def validate(self):
414
421
  if self.kind == DatafileKind.pipe:
415
- # TODO(eclbg):
416
- # [x] node names are unique
417
- # [x] SQL in all nodes
418
- # [x] Materialized nodes have target datasource
419
- # [x] Only one materialized node
420
- # [x] Only one node of any specific type
421
- # (rbarbadillo): there's a HUGE amount of validations in api_pipes.py, we should somehow merge them
422
422
  non_standard_nodes_count = 0
423
423
  for node in self.nodes:
424
424
  node_type = node.get("type", "").lower()
@@ -432,10 +432,13 @@ class Datafile:
432
432
  self.validate_copy_node(node)
433
433
  if node_type == PipeNodeTypes.DATA_SINK:
434
434
  self.validate_sink_node(node)
435
+ if node_type in {PipeNodeTypes.STANDARD, ""}:
436
+ self.validate_standard_node(node)
435
437
  if node_type not in VALID_PIPE_NODE_TYPES:
436
438
  raise DatafileValidationError(
437
439
  f"Invalid node '{repr(node['name'])}' of type ({node_type}). Allowed node types: {VISIBLE_PIPE_NODE_TYPES}"
438
440
  )
441
+
439
442
  for token in self.tokens:
440
443
  if token["permission"].upper() != "READ":
441
444
  raise DatafileValidationError(
@@ -1983,6 +1986,7 @@ def parse(
1983
1986
  "include": include,
1984
1987
  "sql": sql("sql"),
1985
1988
  "version": version,
1989
+ "deployment_method": assign_var("deployment_method", allowed_values={"alter"}),
1986
1990
  "export_connection_name": assign_var("export_connection_name"),
1987
1991
  "export_schedule": assign_var("export_schedule"),
1988
1992
  "export_bucket_uri": assign_var("export_bucket_uri"),
@@ -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.dev282'
8
- __revision__ = '77eb85f'
7
+ __version__ = '0.0.1.dev284'
8
+ __revision__ = '8beb9ee'
@@ -28,6 +28,7 @@ from tinybird.tb.modules.agent.memory import (
28
28
  get_last_messages_from_last_user_prompt,
29
29
  save_messages,
30
30
  )
31
+ from tinybird.tb.modules.agent.mock_agent import MockAgent
31
32
  from tinybird.tb.modules.agent.models import create_model
32
33
  from tinybird.tb.modules.agent.prompts import (
33
34
  agent_system_prompt,
@@ -45,7 +46,6 @@ from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
45
46
  from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
46
47
  from tinybird.tb.modules.agent.tools.get_endpoint_stats import get_endpoint_stats
47
48
  from tinybird.tb.modules.agent.tools.get_openapi_definition import get_openapi_definition
48
- from tinybird.tb.modules.agent.tools.mock import mock
49
49
  from tinybird.tb.modules.agent.tools.plan import plan
50
50
  from tinybird.tb.modules.agent.tools.secret import create_or_update_secrets
51
51
  from tinybird.tb.modules.agent.utils import AgentRunCancelled, TinybirdAgentContext, show_confirmation, show_input
@@ -111,7 +111,6 @@ class TinybirdAgent:
111
111
  Tool(build, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
112
112
  Tool(deploy, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
113
113
  Tool(deploy_check, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
114
- Tool(mock, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
115
114
  Tool(analyze_file, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
116
115
  Tool(analyze_url, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
117
116
  Tool(append_file, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
@@ -166,6 +165,16 @@ class TinybirdAgent:
166
165
  workspace_id=workspace_id,
167
166
  project=self.project,
168
167
  )
168
+ self.mock_agent = MockAgent(
169
+ dangerously_skip_permissions=self.dangerously_skip_permissions,
170
+ prompt_mode=prompt_mode,
171
+ thinking_animation=self.thinking_animation,
172
+ token=self.token,
173
+ user_token=self.user_token,
174
+ host=self.host,
175
+ workspace_id=workspace_id,
176
+ project=self.project,
177
+ )
169
178
 
170
179
  @self.agent.tool
171
180
  def manage_tests(ctx: RunContext[TinybirdAgentContext], task: str) -> str:
@@ -210,6 +219,25 @@ class TinybirdAgent:
210
219
  result = self.explore_agent.run(task, deps=ctx.deps, usage=ctx.usage)
211
220
  return result.output or "No result returned"
212
221
 
222
+ @self.agent.tool
223
+ def mock(
224
+ ctx: RunContext[TinybirdAgentContext], datasource_name: str, rows: int, data_format: str, task: str
225
+ ) -> str:
226
+ """Generate mock data for a datasource.
227
+
228
+ Args:
229
+ datasource_name (str): The datasource name to generate mock data for. Required.
230
+ rows (int): Number of rows to create. If not provided, the default is 10. Required.
231
+ data_format (str): Format of the mock data to create. Options: ndjson, csv. Required.
232
+ task (str): Extra details about how to generate the mock data (nested json if any, sample row to help with the generation, etc). Required.
233
+
234
+ Returns:
235
+ str: The result of the mock data generation.
236
+ """
237
+ user_input = f"Datasource name: {datasource_name}\nRows: {rows}\nData format: {data_format}\nTask: {task}"
238
+ result = self.mock_agent.run(user_input, deps=ctx.deps, usage=ctx.usage)
239
+ return result.output or "No result returned"
240
+
213
241
  @self.agent.instructions
214
242
  def get_local_host(ctx: RunContext[TinybirdAgentContext]) -> str:
215
243
  return f"""
@@ -355,20 +383,18 @@ class TinybirdAgent:
355
383
  workspace_id = config.get("id", "")
356
384
  workspace = client.workspace(workspace_id, with_organization=True, version="v1")
357
385
  limits_data = client.organization_limits(workspace["organization"]["id"])
358
- ai_requests_limits = limits_data.get("limits", {}).get("ai_requests", {})
359
- current_ai_requests = ai_requests_limits.get("quantity") or 0
360
- max_ai_requests = ai_requests_limits.get("max") or 0
361
- remaining_requests = max(max_ai_requests - current_ai_requests, 0)
362
- current_ai_requests = min(max_ai_requests, current_ai_requests)
363
- if not max_ai_requests:
386
+ ai_credits_limits = limits_data.get("limits", {}).get("ai_credits", {})
387
+ current_ai_credits = ai_credits_limits.get("quantity") or 0
388
+ ai_credits = ai_credits_limits.get("max") or 0
389
+ remaining_credits = max(ai_credits - current_ai_credits, 0)
390
+ current_ai_credits = min(ai_credits, current_ai_credits)
391
+ if not ai_credits:
364
392
  return
365
- warning_threshold = max_ai_requests * 0.8
366
- message_color = (
367
- FeedbackManager.warning if current_ai_requests >= warning_threshold else FeedbackManager.gray
368
- )
393
+ warning_threshold = ai_credits * 0.8
394
+ message_color = FeedbackManager.warning if current_ai_credits >= warning_threshold else FeedbackManager.gray
369
395
  click.echo(
370
396
  message_color(
371
- message=f"{remaining_requests} requests left ({current_ai_requests}/{max_ai_requests}). You can continue using Tinybird Code. Limits will be enforced soon."
397
+ message=f"{remaining_credits} credits left ({current_ai_credits}/{ai_credits}). You can continue using Tinybird Code. Limits will be enforced soon."
372
398
  )
373
399
  )
374
400
  except Exception:
@@ -50,6 +50,11 @@ You can do the following:
50
50
  - Executing SQL queries against Tinybird Cloud or Tinybird Local.
51
51
  - Requesting endpoints in Tinybird Cloud or Tinybird Local.
52
52
  - Visualizing data as a chart using execute_query tool with the `script` parameter.
53
+
54
+ IMPORTANT: Use always the last environment used in previous queries or endpoint requests (cloud_or_local: str). If you don't have any information about the last environment, use None.
55
+ IMPORTANT: If some resource is not found in a environment, you can use the `diff_resource` tool to check the status across environments.
56
+
57
+ Once you finish the task, return a valid response for the task to complete.
53
58
  """,
54
59
  tone_and_style_instructions,
55
60
  explore_data_instructions,
@@ -0,0 +1,206 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic_ai import Agent, RunContext, Tool
4
+ from pydantic_ai.messages import ModelMessage
5
+ from pydantic_ai.usage import Usage
6
+
7
+ from tinybird.tb.modules.agent.animations import ThinkingAnimation
8
+ from tinybird.tb.modules.agent.models import create_model
9
+ from tinybird.tb.modules.agent.prompts import resources_prompt
10
+ from tinybird.tb.modules.agent.tools.mock import generate_mock_fixture
11
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext
12
+ from tinybird.tb.modules.project import Project
13
+
14
+ mock_sql_instructions = """
15
+ ## When generating the SQL query to generate mock data
16
+
17
+ Given the schema for a Tinybird datasource, return a valid clickhouse sql query to generate some random data that matches that schema.
18
+ Response format MUST be just a valid clickhouse sql query.
19
+
20
+ <example>
21
+ <example_datasource_schema>
22
+ SCHEMA >
23
+ experience_gained Int16 `json:$.experience_gained`,
24
+ level Int16 `json:$.level`,
25
+ monster_kills Int16 `json:$.monster_kills`,
26
+ player_id String `json:$.player_id`,
27
+ pvp_kills Int16 `json:$.pvp_kills`,
28
+ quest_completions Int16 `json:$.quest_completions`,
29
+ timestamp DateTime `json:$.timestamp`
30
+ </example_datasource_schema>
31
+ <example_output>
32
+
33
+ SELECT
34
+ rand() % 1000 AS experience_gained, -- Random experience gained between 0 and 999
35
+ 1 + rand() % 100 AS level, -- Random level between 1 and 100
36
+ rand() % 500 AS monster_kills, -- Random monster kills between 0 and 499
37
+ concat('player_', toString(rand() % 10000)) AS player_id, -- Random player IDs like "player_1234"
38
+ rand() % 50 AS pvp_kills, -- Random PvP kills between 0 and 49
39
+ rand() % 200 AS quest_completions, -- Random quest completions between 0 and 199
40
+ now() - rand() % 86400 AS timestamp -- Random timestamp within the last day
41
+ FROM numbers(10)
42
+ </example_output>
43
+ </example>
44
+
45
+ <instructions>
46
+ - The query MUST return a random sample of data that matches the schema.
47
+ - The query MUST return a valid clickhouse sql query.
48
+ - The query MUST be valid for clickhouse and Tinybird.
49
+ - FROM numbers([number_of_rows]) part is mandatory.
50
+ - If json paths are present (e.g. `userAgent` String `json:$.userAgent`), rely on the json paths to generate the sample record.
51
+ - If the schema has nested json paths (e.g. `json:$.location.country`), generate nested JSON objects accordingly.
52
+ - Use recent dates to avoid generating dates that are too far in the past.
53
+ - Do NOT include ```clickhouse or ```sql or any other wrapping text to the sql query.
54
+ - Do NOT use any of these functions: elementAt
55
+ - Do NOT add a semicolon at the end of the query
56
+ - Do NOT add any FORMAT at the end of the query, because it will be added later by Tinybird.
57
+ - Do not use any function that is not present in the list of general functions, character insensitive functions and aggregate functions.
58
+ - If the function is not present in the list, the sql query will fail, so avoid at all costs to use any function that is not present in the list.
59
+ </instructions>
60
+
61
+ <more_examples>
62
+ # Examples with different schemas, like an array field or a nested JSON field:
63
+
64
+ ## Example schema with an array field:
65
+
66
+ ### Schema:
67
+
68
+ SCHEMA >
69
+ `order_id` UInt64 `json:$.order_id`,
70
+ `customer_id` UInt64 `json:$.customer_id`,
71
+ `order_date` DateTime `json:$.order_date`,
72
+ `total_amount` Float64 `json:$.total_amount`,
73
+ `items` Array(String) `json:$.items[:]` // This is an array field
74
+
75
+ ### Desired final output of the query:
76
+ {
77
+ "order_id": 123456,
78
+ "customer_id": 7890,
79
+ "order_date": "2024-11-30T10:30:00.000Z",
80
+ "total_amount": 150.0,
81
+ "items": ["item1", "item2", "item3"]
82
+ }
83
+
84
+ ### Example SQL output with an array field:
85
+
86
+ SELECT
87
+ concat('ord_', toString(rand() % 10000)) AS order_id,
88
+ concat('cust_', toString(rand() % 10000)) AS customer_id,
89
+ now() - rand() % 86400 AS order_date,
90
+ rand() % 1000 AS total_amount,
91
+ arrayMap(x -> concat('item_', toString(x)), range(1, rand() % 5 + 1)) AS items
92
+ FROM numbers(ROWS)
93
+
94
+ ## Example schema with nested JSON paths:
95
+
96
+ ### Schema:
97
+
98
+ SCHEMA >
99
+ `timestamp` DateTime `json:$.timestamp`,
100
+ `location_country` String `json:$.location.country`,
101
+ `location_region` String `json:$.location.region`,
102
+ `location_city` String `json:$.location.city`,
103
+ `location_latitude` String `json:$.location.latitude`,
104
+ `location_longitude` String `json:$.location.longitude`
105
+
106
+ ### Important: Understanding JSON paths
107
+ When you see json paths like `json:$.location.country`, it means the data should be structured as nested JSON:
108
+ - `json:$.location.country` → the country field is nested inside the location object
109
+ - `json:$.location.region` → the region field is nested inside the location object
110
+
111
+ ### Desired final output structure:
112
+ {
113
+ "timestamp": "2024-11-30T10:30:00.000Z",
114
+ "location": {
115
+ "country": "United States",
116
+ "region": "California",
117
+ "city": "San Francisco",
118
+ "latitude": "37.7749",
119
+ "longitude": "-122.4194"
120
+ }
121
+ }
122
+
123
+ ### Example SQL output for nested JSON paths:
124
+
125
+ SELECT
126
+ timestamp,
127
+ CAST(concat('{
128
+ "country": "', country, '",
129
+ "region": "', region, '",
130
+ "city": "', city, '",
131
+ "latitude": "', latitude, '",
132
+ "longitude": "', longitude, '"
133
+ }'), 'JSON') AS location
134
+ FROM
135
+ (
136
+ SELECT
137
+ now() - rand() % 86400 AS timestamp,
138
+ ['United States', 'Canada', 'United Kingdom', 'Germany', 'France'][(rand() % 5) + 1] AS country,
139
+ ['California', 'Texas', 'New York', 'Ontario', 'London'][(rand() % 5) + 1] AS region,
140
+ ['San Francisco', 'Los Angeles', 'New York', 'Toronto', 'London'][(rand() % 5) + 1] AS city,
141
+ toString(round(rand() * 180 - 90, 4)) AS latitude,
142
+ toString(round(rand() * 360 - 180, 4)) AS longitude
143
+ FROM numbers(10)
144
+ )
145
+ </more_examples>
146
+ """
147
+
148
+
149
+ class MockAgent:
150
+ def __init__(
151
+ self,
152
+ dangerously_skip_permissions: bool,
153
+ prompt_mode: bool,
154
+ token: str,
155
+ user_token: str,
156
+ host: str,
157
+ workspace_id: str,
158
+ project: Project,
159
+ thinking_animation: ThinkingAnimation,
160
+ ):
161
+ self.dangerously_skip_permissions = dangerously_skip_permissions or prompt_mode
162
+ self.token = token
163
+ self.user_token = user_token
164
+ self.host = host
165
+ self.workspace_id = workspace_id
166
+ self.project = project
167
+ self.thinking_animation = thinking_animation
168
+ self.messages: list[ModelMessage] = []
169
+ self.agent = Agent(
170
+ deps_type=TinybirdAgentContext,
171
+ instructions=[
172
+ f"""
173
+ You are part of Tinybird Code, an agentic CLI that can help users to work with Tinybird.
174
+ You are a sub-agent of the main Tinybird Code agent. You are responsible for generating mock data for a datasource.
175
+ You will be given a datasource name and you will use `generate_mock_fixture` tool to generate a sql query to execute to generate the mock data.
176
+ When finish return the result of the mock data generation: the path of the fixture file, the number of rows generated, the datasource name and if the data was appended to the datasource.
177
+
178
+ # Info
179
+ Today is {datetime.now().strftime("%Y-%m-%d")}
180
+ """,
181
+ mock_sql_instructions,
182
+ ],
183
+ tools=[
184
+ Tool(
185
+ generate_mock_fixture,
186
+ docstring_format="google",
187
+ require_parameter_descriptions=True,
188
+ takes_ctx=True,
189
+ ),
190
+ ],
191
+ )
192
+
193
+ @self.agent.instructions
194
+ def get_project_files(ctx: RunContext[TinybirdAgentContext]) -> str:
195
+ return resources_prompt(self.project)
196
+
197
+ def run(self, task: str, deps: TinybirdAgentContext, usage: Usage):
198
+ result = self.agent.run_sync(
199
+ task,
200
+ deps=deps,
201
+ usage=usage,
202
+ message_history=self.messages,
203
+ model=create_model(self.user_token, self.host, self.workspace_id, run_id=deps.run_id),
204
+ )
205
+ self.messages.extend(result.new_messages())
206
+ return result
@@ -19,7 +19,7 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
19
19
  def create_datafile(
20
20
  ctx: RunContext[TinybirdAgentContext], name: str, type: str, description: str, content: str, pathname: str
21
21
  ) -> str:
22
- """Given a resource representation, create or update a datafile in the project folder
22
+ """Given a resource representation, create or update a datafile (.datasource, .connection, .pipe) in the project folder
23
23
 
24
24
  Args:
25
25
  name (str): The name of the datafile. Required.
@@ -35,7 +35,12 @@ def deploy(ctx: RunContext[TinybirdAgentContext], allow_destructive_operations:
35
35
  ctx.deps.thinking_animation.start()
36
36
  return f"User did not confirm deployment and gave the following feedback: {feedback}"
37
37
 
38
- click.echo(FeedbackManager.highlight(message="» Deploying project..."))
38
+ allow_destructive_operations_flag = " --allow-destructive-operations" if allow_destructive_operations else ""
39
+ click.echo(
40
+ FeedbackManager.highlight(
41
+ message=f"» Running command: tb --cloud deploy{allow_destructive_operations_flag}"
42
+ )
43
+ )
39
44
  ctx.deps.deploy_project(allow_destructive_operations=allow_destructive_operations)
40
45
  click.echo(FeedbackManager.success(message="✓ Project deployed successfully"))
41
46
  ctx.deps.thinking_animation.start()
@@ -1,7 +1,9 @@
1
+ import click
1
2
  from pydantic_ai import RunContext
2
3
 
3
4
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
4
5
  from tinybird.tb.modules.exceptions import CLIDeploymentException
6
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
5
7
 
6
8
 
7
9
  def deploy_check(ctx: RunContext[TinybirdAgentContext]) -> str:
@@ -18,6 +20,7 @@ def deploy_check(ctx: RunContext[TinybirdAgentContext]) -> str:
18
20
  ctx.deps.thinking_animation.start()
19
21
  return f"User did not confirm deployment check and gave the following feedback: {feedback}"
20
22
 
23
+ click.echo(FeedbackManager.highlight(message="» Running command: tb --cloud deploy --check"))
21
24
  ctx.deps.deploy_check_project()
22
25
  ctx.deps.thinking_animation.start()
23
26
  return "Project can be deployed"
@@ -8,7 +8,7 @@ import click
8
8
  import humanfriendly
9
9
  from pydantic_ai import RunContext
10
10
 
11
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext
11
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_env_options
12
12
  from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_pretty_table
13
13
  from tinybird.tb.modules.feedback_manager import FeedbackManager
14
14
 
@@ -35,23 +35,24 @@ def execute_query(
35
35
  ctx: RunContext[TinybirdAgentContext],
36
36
  query: str,
37
37
  task: str,
38
- cloud: Optional[bool] = None,
38
+ cloud_or_local: Optional[str] = None,
39
39
  script: Optional[str] = None,
40
40
  export_format: Optional[str] = None,
41
+ explanation_why_not_know_about_last_environment: Optional[str] = None,
41
42
  ):
42
43
  """Execute a query and return the result as a table, chart or exported file.
43
44
 
44
45
  Args:
45
46
  query (str): The query to execute. Required.
46
47
  task (str): The purpose of the query. Required.
47
- cloud (bool): Whether to execute the query on cloud or local. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
48
+ cloud_or_local (str): Whether to execute the query on cloud or local. Use the last environment used in previous queries or endpoint requests. If you don't have any information about the last environment, use None. Options: cloud, local.
48
49
  script (str): Python script using plotext to render the query results as a chart. The script will have access to 'data' (list of dicts), 'meta' (list of column info dicts), 'terminal_width' and 'terminal_height' variables. Always use plt.theme("clear") for transparent background and plt.plot_size(terminal_width, terminal_height) for proper sizing. For bar charts, use the simple versions: plt.simple_bar(), plt.simple_multiple_bar(), and plt.simple_stacked_bar(). Optional.
49
50
  export_format (str): The format to export the query results to. Options: csv, json, ndjson. Optional.
51
+ explanation_why_not_know_about_last_environment (str): Why you don't know about the last environment used in previous queries or endpoint requests. Required.
50
52
 
51
53
  Returns:
52
54
  str: The result of the query.
53
55
  """
54
-
55
56
  try:
56
57
  for forbidden_command in forbidden_commands:
57
58
  if forbidden_command in query.lower():
@@ -61,24 +62,21 @@ def execute_query(
61
62
  if query.lower().startswith(forbidden_command):
62
63
  return f"Error executing query: {forbidden_command} is not allowed."
63
64
 
64
- # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
65
- if cloud is None:
65
+ # Handle cloud_or_local parameter - ask user if uncertain and not in dangerous skip mode
66
+ if cloud_or_local is None:
66
67
  if ctx.deps.dangerously_skip_permissions:
67
68
  # Default to local when in dangerous skip mode
68
- cloud = False
69
+ cloud_or_local = "local"
69
70
  else:
70
71
  # Ask the user to choose execution mode
71
- from tinybird.tb.modules.agent.utils import show_env_options
72
72
 
73
73
  cloud = show_env_options(ctx)
74
74
  if cloud is None:
75
75
  return "Query execution cancelled by user."
76
+ cloud_or_local = "cloud" if cloud else "local"
76
77
 
77
- cloud_or_local = "cloud" if cloud else "local"
78
78
  ctx.deps.thinking_animation.stop()
79
-
80
79
  click.echo(FeedbackManager.highlight(message=f"» Executing query to {cloud_or_local}:\n{query}\n"))
81
-
82
80
  is_templating = query.strip().startswith("%")
83
81
  query_format = "JSON"
84
82
  if export_format == "csv":
@@ -94,7 +92,7 @@ def execute_query(
94
92
  else:
95
93
  query = f"SELECT * FROM ({query}) FORMAT {query_format}"
96
94
 
97
- execute_query = ctx.deps.execute_query_cloud if cloud else ctx.deps.execute_query_local
95
+ execute_query = ctx.deps.execute_query_cloud if cloud_or_local == "cloud" else ctx.deps.execute_query_local
98
96
  result = execute_query(query=query)
99
97
  if export_format:
100
98
  file_extension = f".{export_format}"
@@ -0,0 +1,135 @@
1
+ import click
2
+ from pydantic_ai import RunContext
3
+
4
+ from tinybird.tb.modules.agent.utils import (
5
+ AgentRunCancelled,
6
+ TinybirdAgentContext,
7
+ create_terminal_box,
8
+ show_confirmation,
9
+ show_input,
10
+ )
11
+ from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_pretty_table, format_data_to_ndjson
12
+ from tinybird.tb.modules.datafile.fixture import persist_fixture
13
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
14
+
15
+
16
+ def generate_mock_fixture(
17
+ ctx: RunContext[TinybirdAgentContext], datasource_name: str, sql: str, data_format: str, rows: int, task: str
18
+ ) -> str:
19
+ """Given a datasource name and a sql query to execute, generate a fixture file with mock data and append it to the datasource.
20
+
21
+ Args:
22
+ datasource_name (str): Name of the datasource to create mock data for. Required.
23
+ sql (str): SQL query to execute to generate the mock data. Required.
24
+ data_format (str): Format of the mock data to create. Options: ndjson, csv. Required.
25
+ rows (int): Number of rows to create. If not provided, the default is 10. Required.
26
+ task (str): Extra details about how to generate the mock data (nested json if any, sample row to help with the generation, etc). You can use this to fix issues with the mock data generation. Required.
27
+
28
+ Returns:
29
+ str: Message indicating the success or failure of the mock data generation
30
+ """
31
+ try:
32
+ ctx.deps.thinking_animation.stop()
33
+
34
+ click.echo(FeedbackManager.highlight(message=f"» Generating mock data for datasource '{datasource_name}'..."))
35
+ try:
36
+ sql_format = "JSON" if data_format == "ndjson" else "CSV"
37
+ sql = f"SELECT * FROM ({sql}) LIMIT {rows} FORMAT {sql_format}"
38
+ result = ctx.deps.execute_query_local(query=sql)
39
+ except Exception as e:
40
+ click.echo(
41
+ FeedbackManager.error(message="✗ Failed to generate a valid SQL query for generating mock data.\n{e}")
42
+ )
43
+ ctx.deps.thinking_animation.start()
44
+ return f"Failed to generate a valid sql for generating mock data for datasource '{datasource_name}'. SQL: {sql}\nError: {e}"
45
+
46
+ preview_content = ""
47
+ if sql_format == "JSON":
48
+ data = result.get("data", [])[:rows]
49
+ preview_content = str(format_data_to_ndjson(data[:10]))
50
+
51
+ if len(data) != rows:
52
+ raise Exception(
53
+ f"Failed to generate a valid sql for generating mock data for datasource '{datasource_name}'. Rows generated: {len(data)} != {rows}. SQL: {sql}\nError: {result}"
54
+ )
55
+
56
+ error_response = result.get("error", None)
57
+ if error_response:
58
+ raise Exception(
59
+ f"Failed to generate a valid sql for generating mock data for datasource '{datasource_name}'. SQL: {sql}\nError: {error_response}"
60
+ )
61
+
62
+ else:
63
+ data = result
64
+ preview_content = str(data[:1000])
65
+
66
+ if isinstance(data, dict):
67
+ data = data.get("data", [])[:rows]
68
+ preview_content = str(format_data_to_ndjson(data[:10]))
69
+
70
+ content = create_terminal_box(preview_content, title=f"fixtures/{datasource_name}.{data_format}")
71
+ click.echo(content)
72
+ click.echo("Showing a preview of the file.\n")
73
+ confirmation = show_confirmation(
74
+ title=f"Create fixture file for datasource '{datasource_name}'?",
75
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
76
+ )
77
+
78
+ if confirmation == "review":
79
+ feedback = show_input(ctx.deps.workspace_name)
80
+ ctx.deps.thinking_animation.start()
81
+ return f"User did not confirm mock data for datasource '{datasource_name}' in Tinybird Local and gave the following feedback: {feedback}"
82
+
83
+ fixture_path = persist_fixture(datasource_name, data, ctx.deps.folder, format=data_format)
84
+ fixture_path_name = f"fixtures/{fixture_path.name}"
85
+ click.echo(FeedbackManager.success(message=f"✓ {fixture_path_name} created"))
86
+ confirmation = show_confirmation(
87
+ title=f"Append {fixture_path_name} to datasource '{datasource_name}'?",
88
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
89
+ )
90
+ if confirmation == "review":
91
+ feedback = show_input(ctx.deps.workspace_name)
92
+ ctx.deps.thinking_animation.start()
93
+ return f"Mock data was generated in {fixture_path_name} but user did not confirm appending {fixture_path_name} to datasource '{datasource_name}' in Tinybird Local and gave the following feedback: {feedback}"
94
+
95
+ ctx.deps.append_data_local(datasource_name=datasource_name, path=str(fixture_path))
96
+ click.echo(FeedbackManager.success(message=f"✓ Data appended to datasource '{datasource_name}'"))
97
+ ctx.deps.thinking_animation.start()
98
+ return f"Mock data generated in {fixture_path_name} and appended to datasource '{datasource_name}' in Tinybird Local"
99
+ except AgentRunCancelled as e:
100
+ raise e
101
+ except Exception as e:
102
+ ctx.deps.thinking_animation.stop()
103
+ error_message = str(e)
104
+ click.echo(FeedbackManager.error(message=error_message))
105
+ try:
106
+ if "in quarantine" in error_message:
107
+ click.echo(
108
+ FeedbackManager.highlight(message=f"» Looking for errors in {datasource_name}_quarantine...")
109
+ )
110
+ query = f"select * from {datasource_name}_quarantine order by insertion_date desc limit 5 FORMAT JSON"
111
+ quarantine_result = ctx.deps.execute_query_local(query=query)
112
+ quarantine_data = quarantine_result["data"] or []
113
+ quarantine_meta = quarantine_result["meta"] or []
114
+ column_names = [c["name"] for c in quarantine_meta]
115
+ echo_safe_humanfriendly_tables_format_pretty_table(
116
+ data=[d.values() for d in quarantine_data], column_names=column_names
117
+ )
118
+ click.echo(
119
+ FeedbackManager.info(
120
+ message=f"These are the first 5 rows of the quarantine table for datasource '{datasource_name}':"
121
+ )
122
+ )
123
+ error_message = (
124
+ error_message
125
+ + 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."
126
+ )
127
+
128
+ except Exception as quarantine_error:
129
+ error_message = error_message + f"\nError accessing to {datasource_name}_quarantine: {quarantine_error}"
130
+
131
+ if "must be created first with 'mode=create'" in error_message:
132
+ error_message = error_message + "\nBuild the project again."
133
+
134
+ ctx.deps.thinking_animation.start()
135
+ return f"Error generating mock data for datasource '{datasource_name}' in Tinybird Local: {error_message}"