tinybird 0.0.1.dev286__py3-none-any.whl → 0.0.1.dev288__py3-none-any.whl

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

Potentially problematic release.


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

tinybird/tb/__cli__.py CHANGED
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/forward/commands'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '0.0.1.dev286'
8
- __revision__ = '3e1e6a3'
7
+ __version__ = '0.0.1.dev288'
8
+ __revision__ = '6cd5b6c'
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import hashlib
2
3
  import shlex
3
4
  import subprocess
4
5
  import sys
@@ -35,6 +36,7 @@ from tinybird.tb.modules.agent.prompts import (
35
36
  load_custom_project_rules,
36
37
  resources_prompt,
37
38
  secrets_prompt,
39
+ service_datasources_prompt,
38
40
  )
39
41
  from tinybird.tb.modules.agent.testing_agent import TestingAgent
40
42
  from tinybird.tb.modules.agent.tools.analyze import analyze_file, analyze_url
@@ -46,7 +48,7 @@ from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
46
48
  from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
47
49
  from tinybird.tb.modules.agent.tools.get_endpoint_stats import get_endpoint_stats
48
50
  from tinybird.tb.modules.agent.tools.get_openapi_definition import get_openapi_definition
49
- from tinybird.tb.modules.agent.tools.plan import plan
51
+ from tinybird.tb.modules.agent.tools.plan import complete_plan, plan
50
52
  from tinybird.tb.modules.agent.tools.secret import create_or_update_secrets
51
53
  from tinybird.tb.modules.agent.utils import AgentRunCancelled, TinybirdAgentContext, show_confirmation, show_input
52
54
  from tinybird.tb.modules.build_common import process as build_process
@@ -88,6 +90,7 @@ class TinybirdAgent:
88
90
  self.dangerously_skip_permissions = dangerously_skip_permissions or prompt_mode
89
91
  self.project = project
90
92
  self.thinking_animation = ThinkingAnimation()
93
+ self.confirmed_plan_id: Optional[str] = None
91
94
  if prompt_mode:
92
95
  self.messages: list[ModelMessage] = get_last_messages_from_last_user_prompt()
93
96
  else:
@@ -108,6 +111,7 @@ class TinybirdAgent:
108
111
  takes_ctx=True,
109
112
  ),
110
113
  Tool(plan, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
114
+ Tool(complete_plan, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
111
115
  Tool(build, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
112
116
  Tool(deploy, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
113
117
  Tool(deploy_check, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
@@ -285,6 +289,10 @@ class TinybirdAgent:
285
289
  def get_project_files(ctx: RunContext[TinybirdAgentContext]) -> str:
286
290
  return resources_prompt(self.project)
287
291
 
292
+ @self.agent.instructions
293
+ def get_service_datasources(ctx: RunContext[TinybirdAgentContext]) -> str:
294
+ return service_datasources_prompt()
295
+
288
296
  @self.agent.instructions
289
297
  def get_secrets(ctx: RunContext[TinybirdAgentContext]) -> str:
290
298
  return secrets_prompt(self.project)
@@ -292,15 +300,25 @@ class TinybirdAgent:
292
300
  def add_message(self, message: ModelMessage) -> None:
293
301
  self.messages.append(message)
294
302
 
303
+ def start_plan(self, plan) -> str:
304
+ self.confirmed_plan_id = hashlib.sha256(plan.encode()).hexdigest()[:16]
305
+ return self.confirmed_plan_id
306
+
307
+ def cancel_plan(self) -> Optional[str]:
308
+ plan_id = self.confirmed_plan_id
309
+ self.confirmed_plan_id = None
310
+ return plan_id
311
+
312
+ def get_plan(self) -> Optional[str]:
313
+ return self.confirmed_plan_id
314
+
295
315
  def _build_agent_deps(self, config: dict[str, Any], run_id: Optional[str] = None) -> TinybirdAgentContext:
296
- client = TinyB(token=self.token, host=self.host)
297
316
  project = self.project
298
317
  folder = self.project.folder
299
318
  local_client = get_tinybird_local_client(config, test=False, silent=False)
300
319
  test_client = get_tinybird_local_client(config, test=True, silent=True)
301
320
  return TinybirdAgentContext(
302
321
  # context does not support the whole client, so we need to pass only the functions we need
303
- explore_data=client.explore_data,
304
322
  build_project=partial(build_project, project=project, config=config),
305
323
  deploy_project=partial(deploy_project, project=project, config=config),
306
324
  deploy_check_project=partial(deploy_check_project, project=project, config=config),
@@ -333,6 +351,9 @@ class TinybirdAgent:
333
351
  local_host=local_client.host,
334
352
  local_token=local_client.token,
335
353
  run_id=run_id,
354
+ get_plan=self.get_plan,
355
+ start_plan=self.start_plan,
356
+ cancel_plan=self.cancel_plan,
336
357
  )
337
358
 
338
359
  def run(self, user_prompt: str, config: dict[str, Any]) -> None:
@@ -386,8 +407,8 @@ class TinybirdAgent:
386
407
  ai_credits_limits = limits_data.get("limits", {}).get("ai_credits", {})
387
408
  current_ai_credits = ai_credits_limits.get("quantity") or 0
388
409
  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)
410
+ remaining_credits = round(max(ai_credits - current_ai_credits, 0), 2)
411
+ current_ai_credits = round(min(ai_credits, current_ai_credits), 2)
391
412
  if not ai_credits:
392
413
  return
393
414
  warning_threshold = ai_credits * 0.8
@@ -566,6 +587,7 @@ def run_agent(
566
587
  ]
567
588
  )
568
589
  )
590
+ agent.cancel_plan()
569
591
  continue
570
592
  except KeyboardInterrupt:
571
593
  click.echo(FeedbackManager.info(message="Goodbye!"))
@@ -10,6 +10,7 @@ from tinybird.tb.modules.agent.models import create_model
10
10
  from tinybird.tb.modules.agent.prompts import (
11
11
  explore_data_instructions,
12
12
  resources_prompt,
13
+ service_datasources_prompt,
13
14
  tone_and_style_instructions,
14
15
  )
15
16
  from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
@@ -75,6 +76,10 @@ Once you finish the task, return a valid response for the task to complete.
75
76
  def get_project_files(ctx: RunContext[TinybirdAgentContext]) -> str:
76
77
  return resources_prompt(self.project)
77
78
 
79
+ @self.agent.instructions
80
+ def get_service_datasources(ctx: RunContext[TinybirdAgentContext]) -> str:
81
+ return service_datasources_prompt()
82
+
78
83
  def run(self, task: str, deps: TinybirdAgentContext, usage: Usage):
79
84
  result = self.agent.run_sync(
80
85
  task,
@@ -12,6 +12,7 @@ from tinybird.prompts import (
12
12
  pipe_instructions,
13
13
  sink_pipe_instructions,
14
14
  )
15
+ from tinybird.service_datasources import get_organization_service_datasources, get_tinybird_service_datasources
15
16
  from tinybird.tb.modules.project import Project
16
17
 
17
18
  available_commands = [
@@ -118,11 +119,6 @@ sql_instructions = """
118
119
  - When you use defined function with a paremeter inside, do NOT add quotes around the parameter:
119
120
  <invalid_defined_function_with_parameter>{% if defined('my_param') %}</invalid_defined_function_with_parameter>
120
121
  <valid_defined_function_without_parameter>{% if defined(my_param) %}</valid_defined_function_without_parameter>
121
- - Use datasource names as table names when doing SELECT statements.
122
- - Do not use pipe names as table names.
123
- - The available datasource names to use in the SQL are the ones present in the existing_resources section or the ones you will create.
124
- - Use node names as table names only when nodes are present in the same file.
125
- - Do not reference the current node name in the SQL.
126
122
  - SQL queries only accept SELECT statements with conditions, aggregations, joins, etc.
127
123
  - ONLY SELECT statements are allowed in any sql query.
128
124
  - When using functions try always ClickHouse functions first, then SQL functions.
@@ -133,7 +129,7 @@ sql_instructions = """
133
129
  datafile_instructions = """
134
130
  <datafile_instructions>
135
131
  - Endpoint files will be created under the `/endpoints` folder.
136
- - Materialized pipe files will be created under the `/materialized` folder.
132
+ - Materialized pipe files will be created under the `/materializations` folder.
137
133
  - Sink pipe files will be created under the `/sinks` folder.
138
134
  - Copy pipe files will be created under the `/copies` folder.
139
135
  - Connection files will be created under the `/connections` folder.
@@ -184,7 +180,7 @@ def resources_prompt(project: Project) -> str:
184
180
  "content": file_path.read_text(),
185
181
  }
186
182
  resources.append(resource)
187
- resources_content = format_as_xml(resources, root_tag="resources", item_tag="resource")
183
+ resources_content += format_as_xml(resources, root_tag="resources", item_tag="resource")
188
184
  else:
189
185
  resources_content += "No resources found"
190
186
 
@@ -198,7 +194,7 @@ def resources_prompt(project: Project) -> str:
198
194
  "name": file_path.stem,
199
195
  }
200
196
  fixtures.append(fixture)
201
- fixture_content = format_as_xml(fixtures, root_tag="fixtures", item_tag="fixture")
197
+ fixture_content += format_as_xml(fixtures, root_tag="fixtures", item_tag="fixture")
202
198
 
203
199
  else:
204
200
  fixture_content += "No fixture files found"
@@ -206,6 +202,50 @@ def resources_prompt(project: Project) -> str:
206
202
  return resources_content + "\n" + fixture_content
207
203
 
208
204
 
205
+ def service_datasources_prompt() -> str:
206
+ def build_content(ds: dict[str, Any]) -> str:
207
+ content = "DESCRIPTION >\n"
208
+ content += f" {ds.get('description', 'No description')}\n"
209
+
210
+ content += "SCHEMA >\n"
211
+ for column in ds.get("columns", []):
212
+ content += f" `{column.get('name', '')}` {column.get('type', '')}\n"
213
+
214
+ if engine := ds.get("engine", {}).get("engine", ""):
215
+ content += f"ENGINE {engine}\n"
216
+ if sorting_key := ds.get("engine", {}).get("sorting_key", ""):
217
+ content += f"ENGINE_SORTING_KEY {sorting_key}\n"
218
+ if partition_key := ds.get("engine", {}).get("partition_key", ""):
219
+ content += f"ENGINE_PARTITION_KEY {partition_key}\n"
220
+
221
+ return content
222
+
223
+ skip_datasources = ["tinybird.bi_stats", "tinybird.bi_stats_rt", "tinybird.releases_log", "tinybird.hook_log"]
224
+ service_datasources = [
225
+ {"name": ds["name"], "content": build_content(ds)}
226
+ for ds in get_tinybird_service_datasources()
227
+ if ds["name"] not in skip_datasources
228
+ ]
229
+ content = "# Service datasources:\n"
230
+ content += format_as_xml(
231
+ service_datasources, root_tag="workspace_service_datasources", item_tag="service_datasource"
232
+ )
233
+ content += "\n#Organization service datasources:\n"
234
+ skip_datasources = ["organization.bi_stats", "organization.bi_stats_rt"]
235
+ org_service_datasources = [
236
+ {"name": ds["name"], "content": build_content(ds)}
237
+ for ds in get_organization_service_datasources()
238
+ if ds["name"] not in skip_datasources
239
+ ]
240
+ content += format_as_xml(
241
+ org_service_datasources,
242
+ root_tag="organization_service_datasources",
243
+ item_tag="service_datasource",
244
+ )
245
+
246
+ return content
247
+
248
+
209
249
  def secrets_prompt(project: Project) -> str:
210
250
  """Generate a prompt showing available secrets from .env.local file."""
211
251
  secrets = project.get_secrets()
@@ -224,7 +264,7 @@ def secrets_prompt(project: Project) -> str:
224
264
  secrets_list.append(secret)
225
265
 
226
266
  if secrets_list:
227
- secrets_content = format_as_xml(secrets_list, root_tag="secrets", item_tag="secret")
267
+ secrets_content += format_as_xml(secrets_list, root_tag="secrets", item_tag="secret")
228
268
 
229
269
  return secrets_content
230
270
 
@@ -245,7 +285,7 @@ def tests_files_prompt(project: Project) -> str:
245
285
  "content": file_path.read_text(),
246
286
  }
247
287
  resources.append(resource)
248
- resources_content = format_as_xml(resources, root_tag="resources", item_tag="resource")
288
+ resources_content += format_as_xml(resources, root_tag="resources", item_tag="resource")
249
289
  else:
250
290
  resources_content += "No resources found"
251
291
 
@@ -260,7 +300,7 @@ def tests_files_prompt(project: Project) -> str:
260
300
  "content": file_path.read_text(),
261
301
  }
262
302
  tests.append(test)
263
- test_content = format_as_xml(tests, root_tag="tests", item_tag="test")
303
+ test_content += format_as_xml(tests, root_tag="tests", item_tag="test")
264
304
  else:
265
305
  test_content += "No test files found"
266
306
 
@@ -836,6 +876,7 @@ You have access to the following tools:
836
876
  15. `run_command` - Run a command using the Tinybird CLI.
837
877
  16. `diff_resource` - Diff the content of a resource in Tinybird Cloud vs Tinybird Local vs Project local file.
838
878
  17. `rename_datafile_or_fixture` - Rename a datafile or fixture.
879
+ 18. `complete_plan` - Complete a plan.
839
880
 
840
881
  # When creating, updating, or deleting files:
841
882
  1. Use `plan` tool to plan the creation, update, rename, or deletion of resources.
@@ -845,6 +886,7 @@ You have access to the following tools:
845
886
  5. If the file was created or removed successfully, report the result to the user.
846
887
  6. If the file was not created or removed, finish the process and just wait for a new user prompt.
847
888
  7. If the file was created or removed successfully, but the build failed, try to fix the error and repeat the process.
889
+ 8. If the plan is completed or cancelled, use the `complete_plan` tool to complete the plan.
848
890
 
849
891
  # When creating a landing datasource given a .ndjson file:
850
892
  - If the user does not specify anything about the desired schema, create a schema like this (sorting key not needed in this case)
@@ -957,6 +999,14 @@ They can be run on a schedule, or executed on demand.
957
999
  {sql_agent_instructions}
958
1000
  {sql_instructions}
959
1001
 
1002
+ ## Referencing tables in SQL queries:
1003
+ The following resources can be used as tables in SQL queries:
1004
+ - Datasources (.datasource files)
1005
+ - Materialized views (.datasource files target of .pipe files with `TYPE MATERIALIZED` defined)
1006
+ - Endpoints (.pipe files with `TYPE ENDPOINT` defined)
1007
+ - Default pipes (.pipe files with no `TYPE` defined)
1008
+ - Node names present in the same .pipe file
1009
+
960
1010
  {secrets_instructions}
961
1011
 
962
1012
  {external_tables_instructions}
@@ -53,9 +53,10 @@ def append_file(
53
53
  return "Append operation cancelled by user."
54
54
 
55
55
  cloud_or_local = "Cloud" if cloud else "Local"
56
+ active_plan = ctx.deps.get_plan() is not None and not cloud
56
57
  confirmation = show_confirmation(
57
58
  title=f"Append fixture {fixture_pathname} to datasource '{datasource_name}' in Tinybird {cloud_or_local}?",
58
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
59
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
59
60
  )
60
61
 
61
62
  if confirmation == "review":
@@ -112,9 +113,10 @@ def append_url(
112
113
  return "Append operation cancelled by user."
113
114
 
114
115
  cloud_or_local = "Cloud" if cloud else "Local"
116
+ active_plan = ctx.deps.get_plan() is not None and not cloud
115
117
  confirmation = show_confirmation(
116
118
  title=f"Append URL {fixture_url} to datasource '{datasource_name}' in Tinybird {cloud_or_local}?",
117
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
119
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
118
120
  )
119
121
 
120
122
  if confirmation == "review":
@@ -50,9 +50,10 @@ def create_datafile(
50
50
  content = create_terminal_box(resource.content, title=resource.pathname)
51
51
  click.echo(content)
52
52
  action = "Create" if not exists else "Update"
53
+ active_plan = ctx.deps.get_plan() is not None
53
54
  confirmation = show_confirmation(
54
55
  title=f"{action} '{resource.pathname}'?",
55
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
56
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
56
57
  )
57
58
 
58
59
  if confirmation == "review":
@@ -146,9 +147,10 @@ def rename_datafile_or_fixture(ctx: RunContext[TinybirdAgentContext], path: str,
146
147
  """
147
148
  try:
148
149
  ctx.deps.thinking_animation.stop()
150
+ active_plan = ctx.deps.get_plan() is not None
149
151
  confirmation = show_confirmation(
150
152
  title=f"Rename '{path}' to '{new_path}'?",
151
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
153
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
152
154
  )
153
155
 
154
156
  if confirmation == "review":
@@ -211,10 +213,10 @@ def remove_file(ctx: RunContext[TinybirdAgentContext], path: str) -> str:
211
213
  click.echo(FeedbackManager.error(message=f"Error: File {path} not found"))
212
214
  ctx.deps.thinking_animation.start()
213
215
  return f"Error: File {path} not found (double check the file path)"
214
-
216
+ active_plan = ctx.deps.get_plan() is not None
215
217
  confirmation = show_confirmation(
216
218
  title=f"Delete '{path}'?",
217
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
219
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
218
220
  )
219
221
 
220
222
  if confirmation == "review":
@@ -70,9 +70,10 @@ def generate_mock_fixture(
70
70
  content = create_terminal_box(preview_content, title=f"fixtures/{datasource_name}.{data_format}")
71
71
  click.echo(content)
72
72
  click.echo("Showing a preview of the file.\n")
73
+ active_plan = ctx.deps.get_plan() is not None
73
74
  confirmation = show_confirmation(
74
75
  title=f"Create fixture file for datasource '{datasource_name}'?",
75
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
76
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
76
77
  )
77
78
 
78
79
  if confirmation == "review":
@@ -85,7 +86,7 @@ def generate_mock_fixture(
85
86
  click.echo(FeedbackManager.success(message=f"✓ {fixture_path_name} created"))
86
87
  confirmation = show_confirmation(
87
88
  title=f"Append {fixture_path_name} to datasource '{datasource_name}'?",
88
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
89
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
89
90
  )
90
91
  if confirmation == "review":
91
92
  feedback = show_input(ctx.deps.workspace_name)
@@ -1,7 +1,45 @@
1
+ from typing import Literal
2
+
1
3
  import click
2
4
  from pydantic_ai import RunContext
3
5
 
4
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
6
+ from tinybird.tb.modules.agent.utils import (
7
+ AgentRunCancelled,
8
+ TinybirdAgentContext,
9
+ show_input,
10
+ show_options,
11
+ )
12
+
13
+ PlanConfirmationResult = Literal["yes", "review", "yes_and_auto_implement"]
14
+
15
+
16
+ def show_plan_confirmation(skip_confirmation: bool = False) -> PlanConfirmationResult:
17
+ if skip_confirmation:
18
+ return "yes"
19
+
20
+ title = "Do you want to continue with the plan?"
21
+ while True:
22
+ result = show_options(
23
+ options=[
24
+ "Yes, continue",
25
+ "Yes, continue and implement all",
26
+ "No, tell Tinybird Code what to do",
27
+ "Cancel",
28
+ ],
29
+ title=title,
30
+ )
31
+
32
+ if result is None: # Cancelled
33
+ raise AgentRunCancelled(f"User cancelled the operation: {title}")
34
+
35
+ if result.startswith("Yes, continue and implement all"):
36
+ return "yes_and_auto_implement"
37
+ if result.startswith("Yes"):
38
+ return "yes"
39
+ elif result.startswith("No"):
40
+ return "review"
41
+
42
+ raise AgentRunCancelled(f"User cancelled the operation: {title}")
5
43
 
6
44
 
7
45
  def plan(ctx: RunContext[TinybirdAgentContext], plan: str) -> str:
@@ -15,15 +53,34 @@ def plan(ctx: RunContext[TinybirdAgentContext], plan: str) -> str:
15
53
  """
16
54
  ctx.deps.thinking_animation.stop()
17
55
  plan = plan.strip()
56
+
18
57
  click.echo(plan)
19
- confirmation = show_confirmation(
20
- title="Do you want to continue with the plan?", skip_confirmation=ctx.deps.dangerously_skip_permissions
21
- )
58
+ confirmation = show_plan_confirmation(skip_confirmation=ctx.deps.dangerously_skip_permissions)
22
59
 
23
60
  if confirmation == "review":
24
61
  feedback = show_input(ctx.deps.workspace_name)
25
62
  ctx.deps.thinking_animation.start()
63
+ ctx.deps.cancel_plan()
26
64
  return f"User did not confirm the proposed plan and gave the following feedback: {feedback}"
27
65
 
28
66
  ctx.deps.thinking_animation.start()
29
- return "User confirmed the plan. Implementing..."
67
+
68
+ if confirmation == "yes_and_auto_implement":
69
+ plan_id = ctx.deps.start_plan(plan=plan)
70
+ return f"User confirmed the plan {plan_id}. Implementing..."
71
+ else:
72
+ return "User confirmed the plan. Implementing..."
73
+
74
+
75
+ def complete_plan(ctx: RunContext[TinybirdAgentContext]) -> str:
76
+ """Given an ongoing plan, complete it
77
+
78
+ Args:
79
+ ctx (RunContext[TinybirdAgentContext]): The context of the agent.
80
+
81
+ Returns:
82
+ str: The result of the plan.
83
+ """
84
+
85
+ plan_id = ctx.deps.cancel_plan()
86
+ return f"Plan {plan_id} completed"
@@ -70,9 +70,10 @@ def create_or_update_secrets(ctx: RunContext[TinybirdAgentContext], secrets: dic
70
70
  preview_content = create_terminal_box(new_content, title=".env.local")
71
71
  click.echo(preview_content)
72
72
 
73
+ active_plan = ctx.deps.get_plan() is not None
73
74
  confirmation = show_confirmation(
74
75
  title=f"{action} {len(secrets)} secret(s) in .env.local?",
75
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
76
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
76
77
  )
77
78
 
78
79
  if confirmation == "review":
@@ -40,7 +40,6 @@ class TinybirdAgentContext(BaseModel):
40
40
  workspace_name: str
41
41
  thinking_animation: Any
42
42
  get_project_files: Callable[[], List[str]]
43
- explore_data: Callable[[str], str]
44
43
  build_project: Callable[..., None]
45
44
  build_project_test: Callable[..., None]
46
45
  deploy_project: Callable[..., None]
@@ -68,6 +67,9 @@ class TinybirdAgentContext(BaseModel):
68
67
  local_host: str
69
68
  local_token: str
70
69
  run_id: Optional[str] = None
70
+ get_plan: Callable[..., Optional[str]]
71
+ start_plan: Callable[..., str]
72
+ cancel_plan: Callable[..., Optional[str]]
71
73
 
72
74
 
73
75
  default_style = PromptStyle.from_dict(
@@ -779,10 +781,10 @@ def copy_fixture_to_project_folder_if_needed(
779
781
  if input_path.exists() and not _is_path_inside_project(input_path, project_folder):
780
782
  # Ask for confirmation to copy the file
781
783
  click.echo(FeedbackManager.highlight(message=f"» File {fixture_pathname} is outside the project folder."))
782
-
784
+ active_plan = ctx.deps.get_plan() is not None
783
785
  confirmation = show_confirmation(
784
786
  title=f"Copy {input_path.name} to project folder for analysis?",
785
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
787
+ skip_confirmation=ctx.deps.dangerously_skip_permissions or active_plan,
786
788
  )
787
789
 
788
790
  if confirmation == "review":
@@ -134,7 +134,11 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool, ingest_hi
134
134
  result = api_fetch(TINYBIRD_API_URL, headers=headers)
135
135
 
136
136
  last_deployment = result.get("deployment")
137
- if last_deployment.get("status") == "deleted":
137
+ if not last_deployment:
138
+ click.echo(FeedbackManager.error(message="Error parsing deployment from response"))
139
+ sys_exit("deployment_error", "Error parsing deployment from response")
140
+
141
+ if last_deployment and last_deployment.get("status") == "deleted":
138
142
  click.echo(FeedbackManager.success(message=f"✓ Deployment #{candidate_deployment.get('id')} is live!"))
139
143
  break
140
144
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev286
3
+ Version: 0.0.1.dev288
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -4,6 +4,7 @@ tinybird/datatypes.py,sha256=r4WCvspmrXTJHiPjjyOTiZyZl31FO3Ynkwq4LQsYm6E,11059
4
4
  tinybird/feedback_manager.py,sha256=XY8d83pRlq-LH7xHMApkaEebfXEWLjDzrGe1prpcTHE,69778
5
5
  tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
6
6
  tinybird/prompts.py,sha256=HoDv9TxPiP8v2XoGTWYxP133dK9CEbXVv4XE5IT339c,45483
7
+ tinybird/service_datasources.py,sha256=o6Az3T6OvcChR_7GXRu7sJH173KzGg1Gv-dNd0bI_vY,46085
7
8
  tinybird/sql.py,sha256=UZJLop6zA9tTPEaS-Fq7M-QyzmC5uV_tIeXZzkjnhso,48299
8
9
  tinybird/sql_template.py,sha256=kaF5pi-f2JiWSYXEF8JsU1OIxvdu2homHnw4MYjq0n8,101953
9
10
  tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
@@ -12,12 +13,12 @@ tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
12
13
  tinybird/tornado_template.py,sha256=jjNVDMnkYFWXflmT8KU_Ssbo5vR8KQq3EJMk5vYgXRw,41959
13
14
  tinybird/ch_utils/constants.py,sha256=v5-nkXHUhysu4i9Z4WVv0-sBbh6xSYUH5q5xHSY2xTI,4194
14
15
  tinybird/ch_utils/engine.py,sha256=4X1B-iuhdW_mxKnX_m3iCsxgP9RPVgR75g7yH1vsJ6A,40851
15
- tinybird/datafile/common.py,sha256=3aIbSseqHustB7MfnzoOvWL2giQSNn4aJ-52F8RV_wo,106053
16
+ tinybird/datafile/common.py,sha256=euJAKSu6GA67oIcRvrrBGsGI1nZtkSWM45ikjLUlJz0,106049
16
17
  tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
17
18
  tinybird/datafile/parse_connection.py,sha256=tRyn2Rpr1TeWet5BXmMoQgaotbGdYep1qiTak_OqC5E,1825
18
19
  tinybird/datafile/parse_datasource.py,sha256=ssW8QeFSgglVFi3sDZj_HgkJiTJ2069v2JgqnH3CkDE,1825
19
20
  tinybird/datafile/parse_pipe.py,sha256=xf4m0Tw44QWJzHzAm7Z7FwUoUUtr7noMYjU1NiWnX0k,3880
20
- tinybird/tb/__cli__.py,sha256=sBAzMXBgl1hlzFEO-dOjfolfF6ZWEOhhnxDURjX9Yb4,247
21
+ tinybird/tb/__cli__.py,sha256=i0Uj8XdvCB6yYSx3uE3zP-aqoSr6lSMqfDHHiLv2Euw,247
21
22
  tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
22
23
  tinybird/tb/cli.py,sha256=FdDFEIayjmsZEVsVSSvRiVYn_FHOVg_zWQzchnzfWho,1008
23
24
  tinybird/tb/client.py,sha256=IQRaInDjOwr9Fzaz3_xXc3aUGqh94tM2lew7IZbB9eM,53733
@@ -33,7 +34,7 @@ tinybird/tb/modules/copy.py,sha256=dPZkcIDvxjJrlQUIvToO0vsEEEs4EYumbNV77-BzNoU,4
33
34
  tinybird/tb/modules/create.py,sha256=pJxHXG69c9Z_21s-7VuJ3RZOF_nJU51LEwiAkvI3dZY,23251
34
35
  tinybird/tb/modules/datasource.py,sha256=kDFHdxckTnRosk2829icfltQvlJd8EY5c9oWB5eS5Xo,41797
35
36
  tinybird/tb/modules/deployment.py,sha256=v0layOmG0IMnuXc3RT39mpGfa5M8yPlrL9F089fJFCo,15964
36
- tinybird/tb/modules/deployment_common.py,sha256=2NJgoONEfhFpGIPeE_wULDuUkomxPsIu2gbHgL1qcw8,19653
37
+ tinybird/tb/modules/deployment_common.py,sha256=8Cc0VyKthmTnULiTKgciPyOGtf1kaRghC3Q00bZJbD4,19897
37
38
  tinybird/tb/modules/deprecations.py,sha256=rrszC1f_JJeJ8mUxGoCxckQTJFBCR8wREf4XXXN-PRc,4507
38
39
  tinybird/tb/modules/dev_server.py,sha256=57FCKuWpErwYUYgHspYDkLWEm9F4pbvVOtMrFXX1fVU,10129
39
40
  tinybird/tb/modules/endpoint.py,sha256=ksRj6mfDb9Xv63PhTkV_uKSosgysHElqagg3RTt21Do,11958
@@ -69,34 +70,34 @@ tinybird/tb/modules/watch.py,sha256=No0bK1M1_3CYuMaIgylxf7vYFJ72lTJe3brz6xQ-mJo,
69
70
  tinybird/tb/modules/workspace.py,sha256=tCP1zZMwBhLRGm22TGfpSd4cHvQLAS1o_azIXv_r6uw,11172
70
71
  tinybird/tb/modules/workspace_members.py,sha256=5JdkJgfuEwbq-t6vxkBhYwgsiTDxF790wsa6Xfif9nk,8608
71
72
  tinybird/tb/modules/agent/__init__.py,sha256=i3oe3vDIWWPaicdCM0zs7D7BJ1W0k7th93ooskHAV00,54
72
- tinybird/tb/modules/agent/agent.py,sha256=38X69i7zL93I30hFFMfl6cAwHDgzIO_PP2ImGJF-UDE,35247
73
+ tinybird/tb/modules/agent/agent.py,sha256=aCeZ1mhnd-WTEBHg_82lIpLPOVa6dXJ7uriEpU0N7yM,36100
73
74
  tinybird/tb/modules/agent/animations.py,sha256=4WOC5_2BracttmMCrV0H91tXfWcUzQHBUaIJc5FA7tE,3490
74
75
  tinybird/tb/modules/agent/banner.py,sha256=l6cO5Fi7lbVKp-GsBP8jf3IkjOWxg2jpAt9NBCy0WR8,4085
75
76
  tinybird/tb/modules/agent/command_agent.py,sha256=0Z08rQsir59zQAr-kkOvsKIFpIBsBSTGJJ1VgqqF5WA,3654
76
77
  tinybird/tb/modules/agent/compactor.py,sha256=BK5AxZFhrp3xWnsRnYaleiYoIWtVNc-_m650Hsopt8g,13841
77
- tinybird/tb/modules/agent/explore_agent.py,sha256=W5pp99wixVSyb66qEVwBv8rZWpJ7JgzjJ_sN4d9u4Gg,3903
78
+ tinybird/tb/modules/agent/explore_agent.py,sha256=gyD5uV5TJwV24eeQiSwhkgfNPb4mtbeH7t2qSdoc18U,4100
78
79
  tinybird/tb/modules/agent/memory.py,sha256=vBewB_64L_wHoT4tLT6UX2uxcHwSY880QZ26F9rPqXs,3793
79
80
  tinybird/tb/modules/agent/mock_agent.py,sha256=zbAZfAqdSLUtMr2VqO0erWpzjT2F1tTcuYjvHb-gvbA,8023
80
81
  tinybird/tb/modules/agent/models.py,sha256=eokO8XlY-kVJOsbqiVporGUAOCyKAXCO5xgTEK9SM6Y,2208
81
- tinybird/tb/modules/agent/prompts.py,sha256=F49-d_nRd7ai4ikizFHx0_NPGEZ46AgRtUUnxqZhPew,42164
82
+ tinybird/tb/modules/agent/prompts.py,sha256=a8ZnOZCTGRnxa_dlrTSmeaM9snOI3sMx0nwUdv7aVGI,44165
82
83
  tinybird/tb/modules/agent/testing_agent.py,sha256=AtwtJViH7805i7djyBgDb7SSUtDyJnw0TWJu6lBFsrg,2953
83
- tinybird/tb/modules/agent/utils.py,sha256=4jsQCAH2zBx13w20DOBrrDnQq9n2rKG9sGhBkJYiPzs,31744
84
+ tinybird/tb/modules/agent/utils.py,sha256=R1RcoPvjKh4OyuwLztWb74t2wBpV7JvNVLQ_JyClveM,31901
84
85
  tinybird/tb/modules/agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
86
  tinybird/tb/modules/agent/tools/analyze.py,sha256=CR5LXg4fou-zYEksqnjpJ0icvxJVoKnTctoI1NRvqCM,3873
86
- tinybird/tb/modules/agent/tools/append.py,sha256=6uByExBpb9rVzB0tebWyLFbfkjEPSNxIGFbZrJTaGVI,8056
87
+ tinybird/tb/modules/agent/tools/append.py,sha256=8UsGMzmv7GYbzp0gerOBqpxxocDbous_haSuwS3zuGU,8222
87
88
  tinybird/tb/modules/agent/tools/build.py,sha256=Hm-xDAP9ckMiKquT-DmDg5H0yxZefLOaWKANyoVSaEQ,846
88
- tinybird/tb/modules/agent/tools/datafile.py,sha256=cXLVKEXCL37UKGRRhVVMzDcIsU1IWPLoKciAOHTRtZY,10675
89
+ tinybird/tb/modules/agent/tools/datafile.py,sha256=kTob7G2TwCwIgwom0rERgXQ13rgPtZv3_ByLnrvpIdU,10881
89
90
  tinybird/tb/modules/agent/tools/deploy.py,sha256=6Vmm0lCG8XKE2iUF_ZJrOqXbTFhoe3anPzYCFehQ3_E,2027
90
91
  tinybird/tb/modules/agent/tools/deploy_check.py,sha256=pE3d9TPtXVKZjYbU0G6ORAGI86lN5K_4JKUriClERbM,1229
91
92
  tinybird/tb/modules/agent/tools/diff_resource.py,sha256=_9xHcDzCTKk_E1wKQbuktVqV6U9sA0kqYaBxWvtliX0,2613
92
93
  tinybird/tb/modules/agent/tools/execute_query.py,sha256=DL2jsZ0jaEqFIkGoiWfR-IUAwsgoF0D-_JUhq7xe4gA,9145
93
94
  tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=r2FrXg1L1s_Llr1tPdJ6k_gu6qw7qLsAXOkbz3eTk1g,2307
94
95
  tinybird/tb/modules/agent/tools/get_openapi_definition.py,sha256=4TIMO2XzHBMhpt9zIWRfjjPZbThT8r_iPS4CVHcItE0,2904
95
- tinybird/tb/modules/agent/tools/mock.py,sha256=kBRRdMZny7dsU2ncZrPGLT6APVfVfB8tXUJwbV7WVmE,7022
96
- tinybird/tb/modules/agent/tools/plan.py,sha256=2KHLNkr2f1RfkbAR4mCVsv94LGosXd8-ky7v6BB1OtQ,985
96
+ tinybird/tb/modules/agent/tools/mock.py,sha256=Seo4WcYNLL1-SmPXutoaX94_pfOdIb47JXo8dHtUVhg,7106
97
+ tinybird/tb/modules/agent/tools/plan.py,sha256=uAJEHZ-xXIq-EpURJYV7GUyY7IbIgactw9NWeCsIT9Y,2516
97
98
  tinybird/tb/modules/agent/tools/request_endpoint.py,sha256=bsLWrMn-ofJM3nn9vm8j_U8fdopVd3H5L0ii6ji-Kuw,4359
98
99
  tinybird/tb/modules/agent/tools/run_command.py,sha256=ypvIU0j1XVUWghqt-dpWHm3GQIYsZwE7kRHC3Wau_H0,1708
99
- tinybird/tb/modules/agent/tools/secret.py,sha256=wUeM-5CCjXiwLEF-H121VypOw3_77OMoZthJedPENl4,4254
100
+ tinybird/tb/modules/agent/tools/secret.py,sha256=8AGTZgHLPg1bxCA2cPMnb-zNutWEwn4emHo7kLjJC5w,4323
100
101
  tinybird/tb/modules/agent/tools/test.py,sha256=4XuEWVHLOTSO51Z9xJ08dTjk0j3IWY_JlPtSBO5aaUs,10373
101
102
  tinybird/tb/modules/datafile/build.py,sha256=NFKBrusFLU0WJNCXePAFWiEDuTaXpwc0lHlOQWEJ43s,51117
102
103
  tinybird/tb/modules/datafile/build_common.py,sha256=2yNdxe49IMA9wNvl25NemY2Iaz8L66snjOdT64dm1is,4511
@@ -118,8 +119,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
118
119
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
119
120
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
120
121
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
121
- tinybird-0.0.1.dev286.dist-info/METADATA,sha256=AgRm7oY7SSB_UAYtlzoAFL6f1_K-S_RPu06Ni6cOfkk,1845
122
- tinybird-0.0.1.dev286.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
123
- tinybird-0.0.1.dev286.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
124
- tinybird-0.0.1.dev286.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
125
- tinybird-0.0.1.dev286.dist-info/RECORD,,
122
+ tinybird-0.0.1.dev288.dist-info/METADATA,sha256=L0eOct16E7wiMuY7AZ7jIovDUt80Y1A3usyUbL_HN_0,1845
123
+ tinybird-0.0.1.dev288.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
124
+ tinybird-0.0.1.dev288.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
125
+ tinybird-0.0.1.dev288.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
126
+ tinybird-0.0.1.dev288.dist-info/RECORD,,