tinybird 0.0.1.dev190__tar.gz → 0.0.1.dev191__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 (110) hide show
  1. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/prompts.py +19 -0
  3. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/__cli__.py +2 -2
  4. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/cli.py +12 -17
  5. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/common.py +6 -2
  6. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datasource.py +57 -16
  7. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/feedback_manager.py +3 -0
  8. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/info.py +8 -3
  9. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird.egg-info/PKG-INFO +1 -1
  10. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/setup.cfg +0 -0
  11. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/__cli__.py +0 -0
  12. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/ch_utils/constants.py +0 -0
  13. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/ch_utils/engine.py +0 -0
  14. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/check_pypi.py +0 -0
  15. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/client.py +0 -0
  16. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/config.py +0 -0
  17. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/connectors.py +0 -0
  18. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/context.py +0 -0
  19. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/datafile.py +0 -0
  20. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/datatypes.py +0 -0
  21. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/feedback_manager.py +0 -0
  22. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/git_settings.py +0 -0
  23. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/sql.py +0 -0
  24. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/sql_template.py +0 -0
  25. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/sql_template_fmt.py +0 -0
  26. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/sql_toolset.py +0 -0
  27. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/syncasync.py +0 -0
  28. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/check_pypi.py +0 -0
  29. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/cli.py +0 -0
  30. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/client.py +0 -0
  31. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/config.py +0 -0
  32. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/build.py +0 -0
  33. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/cicd.py +0 -0
  34. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/config.py +0 -0
  35. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/connection.py +0 -0
  36. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/copy.py +0 -0
  37. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/create.py +0 -0
  38. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/build.py +0 -0
  39. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/build_common.py +0 -0
  40. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  41. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  42. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/common.py +0 -0
  43. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/diff.py +0 -0
  44. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  45. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/fixture.py +0 -0
  46. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/format_common.py +0 -0
  47. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  48. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  49. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  50. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  51. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  52. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/playground.py +0 -0
  53. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/datafile/pull.py +0 -0
  54. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/deployment.py +0 -0
  55. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/deprecations.py +0 -0
  56. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/dev_server.py +0 -0
  57. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/endpoint.py +0 -0
  58. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/exceptions.py +0 -0
  59. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/infra.py +0 -0
  60. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/job.py +0 -0
  61. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/llm.py +0 -0
  62. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/llm_utils.py +0 -0
  63. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/local.py +0 -0
  64. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/local_common.py +0 -0
  65. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/login.py +0 -0
  66. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/logout.py +0 -0
  67. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/materialization.py +0 -0
  68. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/mock.py +0 -0
  69. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/open.py +0 -0
  70. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/pipe.py +0 -0
  71. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/project.py +0 -0
  72. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/regions.py +0 -0
  73. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/secret.py +0 -0
  74. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/shell.py +0 -0
  75. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/table.py +0 -0
  76. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/telemetry.py +0 -0
  77. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/test.py +0 -0
  78. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  79. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  80. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/token.py +0 -0
  81. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/watch.py +0 -0
  82. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/workspace.py +0 -0
  83. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb/modules/workspace_members.py +0 -0
  84. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli.py +0 -0
  85. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/auth.py +0 -0
  86. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/branch.py +0 -0
  87. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/cicd.py +0 -0
  88. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/cli.py +0 -0
  89. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/common.py +0 -0
  90. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/config.py +0 -0
  91. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/connection.py +0 -0
  92. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/datasource.py +0 -0
  93. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/exceptions.py +0 -0
  94. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/fmt.py +0 -0
  95. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/job.py +0 -0
  96. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/pipe.py +0 -0
  97. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/regions.py +0 -0
  98. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/tag.py +0 -0
  99. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/telemetry.py +0 -0
  100. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/test.py +0 -0
  101. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  102. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  103. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/workspace.py +0 -0
  104. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  105. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird/tornado_template.py +0 -0
  106. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird.egg-info/SOURCES.txt +0 -0
  107. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird.egg-info/dependency_links.txt +0 -0
  108. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird.egg-info/entry_points.txt +0 -0
  109. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/tinybird.egg-info/requires.txt +0 -0
  110. {tinybird-0.0.1.dev190 → tinybird-0.0.1.dev191}/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.dev190
3
+ Version: 0.0.1.dev191
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -1010,3 +1010,22 @@ Current README.md file:
1010
1010
  <readme>[readme content here]</readme>
1011
1011
  </readme_instructions>
1012
1012
  """
1013
+
1014
+
1015
+ def quarantine_prompt(datasource_definition: str) -> str:
1016
+ return f"""
1017
+ - You are an expert in Tinybird.
1018
+ - You are given a list of rows that went to quarantine during ingestion because of data quality issues.
1019
+ - Return the errors in a human readable format so the user can understand what is the problem and fix it.
1020
+ - Be concise and to the point.
1021
+ - Do not mention clickhouse tables. Refer to data sources instead.
1022
+ - The possible fixes recommended fixes are:
1023
+ - Changing a column type in the datasource definition: tell the user what to change in the datasource file, then build again before appending the data.
1024
+ - Changing the data because of the wrong data type: tell the user what to change in the data file, then append the data again.
1025
+ - The format of the response always inside the tag <quarantine_errors>[response]</quarantine_errors>
1026
+ - Do not use markdown format in the response, because it is a CLI output.
1027
+ - The datasource definition is the following:
1028
+ <datasource_definition>
1029
+ {datasource_definition}
1030
+ </datasource_definition>
1031
+ """
@@ -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.dev190'
8
- __revision__ = '8a00bcf'
7
+ __version__ = '0.0.1.dev191'
8
+ __revision__ = 'c8535c7'
@@ -28,7 +28,9 @@ from tinybird.tb.modules.common import (
28
28
  CLIException,
29
29
  _get_tb_client,
30
30
  coro,
31
+ echo_json,
31
32
  echo_safe_format_table,
33
+ force_echo,
32
34
  getenv_bool,
33
35
  try_update_config_with_remote,
34
36
  )
@@ -72,7 +74,7 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
72
74
  @click.option("--cloud/--local", is_flag=True, default=False, help="Run against cloud or local.")
73
75
  @click.option("--staging", is_flag=True, default=False, help="Run against a staging deployment.")
74
76
  @click.option(
75
- "--output", type=click.Choice(["json", "human"], case_sensitive=False), default="human", help="Output format"
77
+ "--output", type=click.Choice(["human", "json", "csv"], case_sensitive=False), default="human", help="Output format"
76
78
  )
77
79
  @click.option("--max-depth", type=int, default=3, help="Maximum depth of the project files.")
78
80
  @click.version_option(version=VERSION)
@@ -95,7 +97,7 @@ async def cli(
95
97
  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.
96
98
  """
97
99
  # We need to unpatch for our tests not to break
98
- if output == "json":
100
+ if output != "human":
99
101
  __hide_click_output()
100
102
  else:
101
103
  if show_tokens or not cloud or ctx.invoked_subcommand == "build":
@@ -195,17 +197,10 @@ async def pull(ctx: Context, force: bool, fmt: bool) -> None:
195
197
 
196
198
  @cli.command()
197
199
  @click.argument("query", required=False)
198
- @click.option("--rows_limit", default=100, help="Max number of rows retrieved")
200
+ @click.option("--rows-limit", default=100, help="Max number of rows retrieved")
199
201
  @click.option("--pipeline", default=None, help="The name of the pipe to run the SQL Query")
200
202
  @click.option("--pipe", default=None, help="The path to the .pipe file to run the SQL Query of a specific NODE")
201
203
  @click.option("--node", default=None, help="The NODE name")
202
- @click.option(
203
- "--format",
204
- "format_",
205
- type=click.Choice(["json", "csv", "human"], case_sensitive=False),
206
- default="human",
207
- help="Output format",
208
- )
209
204
  @click.option("--stats/--no-stats", default=False, help="Show query stats")
210
205
  @click.pass_context
211
206
  @coro
@@ -216,13 +211,13 @@ async def sql(
216
211
  pipeline: Optional[str],
217
212
  pipe: Optional[str],
218
213
  node: Optional[str],
219
- format_: str,
220
214
  stats: bool,
221
215
  ) -> None:
222
216
  """Run SQL query over data sources and pipes."""
223
-
224
217
  client = ctx.ensure_object(dict)["client"]
225
- req_format = "CSVWithNames" if format_ == "csv" else "JSON"
218
+ output = ctx.ensure_object(dict)["output"]
219
+
220
+ req_format = "CSVWithNames" if output == "csv" else "JSON"
226
221
  res = None
227
222
  try:
228
223
  if query:
@@ -275,11 +270,11 @@ async def sql(
275
270
  bytes_read = humanfriendly.format_size(stats_dict["bytes_read"])
276
271
  click.echo(FeedbackManager.info_query_stats(seconds=seconds, rows=rows_read, bytes=bytes_read))
277
272
 
278
- if format_ == "csv":
279
- click.echo(res)
273
+ if output == "csv":
274
+ force_echo(str(res))
280
275
  elif isinstance(res, dict) and "data" in res and res["data"]:
281
- if format_ == "json":
282
- click.echo(json.dumps(res, indent=8))
276
+ if output == "json":
277
+ echo_json(res, indent=8)
283
278
  else:
284
279
  dd = []
285
280
  for d in res["data"]:
@@ -2250,5 +2250,9 @@ def get_error_event(error: str) -> Tuple[str, str]:
2250
2250
  return error_event, silent_error_msg
2251
2251
 
2252
2252
 
2253
- def echo_json(data: Dict[str, Any]) -> None:
2254
- click.echo(json.dumps(data), force_output=True) # type: ignore
2253
+ def force_echo(string: str) -> None:
2254
+ click.echo(string, force_output=True) # type: ignore
2255
+
2256
+
2257
+ def echo_json(data: Dict[str, Any], indent: Union[None, int, str] = None) -> None:
2258
+ force_echo(json.dumps(data, indent=indent))
@@ -18,6 +18,7 @@ import humanfriendly
18
18
  import requests
19
19
  from click import Context
20
20
 
21
+ from tinybird.prompts import quarantine_prompt
21
22
  from tinybird.syncasync import sync_to_async
22
23
  from tinybird.tb.client import AuthNoTokenException, DoesNotExistException, TinyB
23
24
  from tinybird.tb.modules.cli import cli
@@ -42,7 +43,10 @@ from tinybird.tb.modules.datafile.common import get_name_version
42
43
  from tinybird.tb.modules.datafile.fixture import persist_fixture
43
44
  from tinybird.tb.modules.exceptions import CLIDatasourceException
44
45
  from tinybird.tb.modules.feedback_manager import FeedbackManager
46
+ from tinybird.tb.modules.llm import LLM
47
+ from tinybird.tb.modules.llm_utils import extract_xml
45
48
  from tinybird.tb.modules.project import Project
49
+ from tinybird.tb.modules.telemetry import add_telemetry_event
46
50
 
47
51
 
48
52
  @cli.group()
@@ -151,6 +155,7 @@ async def datasource_append(
151
155
  """
152
156
  env: str = ctx.ensure_object(dict)["env"]
153
157
  client: TinyB = ctx.obj["client"]
158
+ project: Project = ctx.ensure_object(dict)["project"]
154
159
 
155
160
  # If data is passed as argument, we detect if it's a JSON object, a URL or a file
156
161
  if data:
@@ -244,15 +249,10 @@ async def datasource_append(
244
249
 
245
250
  if events:
246
251
  click.echo(FeedbackManager.highlight(message=f"\n» Sending events to {datasource_name}"))
247
- try:
248
- json_data = json.loads(events)
249
- except Exception:
250
- raise CLIDatasourceException(FeedbackManager.error(message="Invalid events data"))
251
-
252
252
  response = await sync_to_async(requests.post)(
253
253
  f"{client.host}/v0/events?name={datasource_name}",
254
254
  headers={"Authorization": f"Bearer {client.token}"},
255
- json=json_data,
255
+ data=events,
256
256
  )
257
257
 
258
258
  try:
@@ -269,21 +269,32 @@ async def datasource_append(
269
269
  )
270
270
  )
271
271
  if quarantined_rows > 0:
272
- raise CLIDatasourceException(
272
+ click.echo(
273
273
  FeedbackManager.error(
274
- message=f"{quarantined_rows} row{'' if quarantined_rows == 1 else 's'} went to quarantine"
274
+ message=f"{quarantined_rows} row{'' if quarantined_rows == 1 else 's'} went to quarantine"
275
275
  )
276
276
  )
277
+ await analyze_quarantine(datasource_name, project, client)
278
+ return
277
279
  else:
278
280
  click.echo(FeedbackManager.highlight(message=f"\n» Appending data to {datasource_name}"))
279
- await push_data(
280
- client,
281
- datasource_name,
282
- data,
283
- mode="append",
284
- concurrency=concurrency,
285
- silent=True,
286
- )
281
+ try:
282
+ await push_data(
283
+ client,
284
+ datasource_name,
285
+ data,
286
+ mode="append",
287
+ concurrency=concurrency,
288
+ silent=True,
289
+ )
290
+ except Exception as e:
291
+ is_quarantined = "quarantine" in str(e)
292
+ click.echo(FeedbackManager.error(message="✗ " + str(e)))
293
+ if is_quarantined:
294
+ await analyze_quarantine(datasource_name, project, client)
295
+ return
296
+ else:
297
+ raise e
287
298
  click.echo(FeedbackManager.success(message="✓ Rows appended!"))
288
299
 
289
300
 
@@ -902,3 +913,33 @@ def generate_short_id():
902
913
 
903
914
  def generate_kafka_group_id(topic: str):
904
915
  return f"{topic}_{int(datetime.timestamp(datetime.now()))}"
916
+
917
+
918
+ async def analyze_quarantine(datasource_name: str, project: Project, client: TinyB):
919
+ config = CLIConfig.get_project_config()
920
+ res = await client.query(
921
+ f"SELECT * FROM {datasource_name}_quarantine ORDER BY insertion_date DESC LIMIT 1 FORMAT JSON"
922
+ )
923
+ quarantine_data = res["data"]
924
+ error_message = json.dumps(res["data"])
925
+ user_token = config.get_user_token()
926
+ click.echo(FeedbackManager.gray(message=f"\n» Analyzing errors in {datasource_name}_quarantine..."))
927
+ if user_token:
928
+ llm = LLM(user_token=user_token, host=config.get_client().host)
929
+ ds_filenames = project.get_datasource_files()
930
+ datasource_definition = next(
931
+ (Path(f).read_text() for f in ds_filenames if f.endswith(f"{datasource_name}.datasource")), ""
932
+ )
933
+ response_llm = llm.ask(
934
+ system_prompt=quarantine_prompt(datasource_definition),
935
+ prompt=f"The quarantine errors are:\n{json.dumps(quarantine_data)}",
936
+ )
937
+ response = extract_xml(response_llm, "quarantine_errors")
938
+ error_message += "\n" + response
939
+ click.echo(response)
940
+ else:
941
+ echo_safe_humanfriendly_tables_format_smart_table(
942
+ data=[d.values() for d in res["data"]], column_names=res["data"][0].keys()
943
+ )
944
+
945
+ add_telemetry_event("datasource_error", error=f"quarantine_error: {error_message}")
@@ -437,6 +437,9 @@ class FeedbackManager:
437
437
  error_tag_not_found = error_message("Tag {tag_name} not found.")
438
438
  error_build_failed = error_message("Build failed")
439
439
  error_request_failed = error_message("Request failed with status code {status_code}, please try again later.")
440
+ error_invalid_output_format = error_message(
441
+ "Invalid output format for this command. Supported formats are: {formats}"
442
+ )
440
443
 
441
444
  info_incl_relative_path = info_message("** Relative path {path} does not exist, skipping.")
442
445
  info_ignoring_incl_file = info_message(
@@ -6,7 +6,7 @@ import click
6
6
  from tinybird.tb.client import TinyB
7
7
  from tinybird.tb.config import get_display_cloud_host
8
8
  from tinybird.tb.modules.cli import CLIConfig, cli
9
- from tinybird.tb.modules.common import coro, echo_json, format_robust_table
9
+ from tinybird.tb.modules.common import coro, echo_json, force_echo, format_robust_table
10
10
  from tinybird.tb.modules.feedback_manager import FeedbackManager
11
11
  from tinybird.tb.modules.local_common import TB_LOCAL_ADDRESS, get_tinybird_local_config
12
12
  from tinybird.tb.modules.project import Project
@@ -19,14 +19,19 @@ async def info(ctx: click.Context) -> None:
19
19
  """Get information about the project that is currently being used"""
20
20
  ctx_config = ctx.ensure_object(dict)["config"]
21
21
  project: Project = ctx.ensure_object(dict)["project"]
22
- is_json = ctx.ensure_object(dict)["output"] == "json"
22
+ output = ctx.ensure_object(dict)["output"]
23
+
24
+ if output not in {"human", "json"}:
25
+ force_echo(FeedbackManager.error_invalid_output_format(formats=", ".join(["human", "json"])))
26
+ return
27
+
23
28
  click.echo(FeedbackManager.highlight(message="» Tinybird Cloud:"))
24
29
  cloud_table, cloud_columns = await get_cloud_info(ctx_config)
25
30
  click.echo(FeedbackManager.highlight(message="\n» Tinybird Local:"))
26
31
  local_table, local_columns = await get_local_info(ctx_config)
27
32
  click.echo(FeedbackManager.highlight(message="\n» Project:"))
28
33
  project_table, project_columns = await get_project_info(project.folder)
29
- if is_json:
34
+ if output == "json":
30
35
  cloud_data = {}
31
36
  if cloud_columns and cloud_table and isinstance(cloud_table, list) and len(cloud_table) > 0:
32
37
  cloud_data = {column: cloud_table[0][i] for i, column in enumerate(cloud_columns)}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev190
3
+ Version: 0.0.1.dev191
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird