tinybird 0.0.1.dev248__py3-none-any.whl → 0.0.1.dev250__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/ch_utils/engine.py +3 -2
- tinybird/prompts.py +1 -1
- tinybird/sql_template.py +5 -1
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/modules/agent/agent.py +65 -26
- tinybird/tb/modules/agent/animations.py +26 -7
- tinybird/tb/modules/agent/models.py +2 -1
- tinybird/tb/modules/agent/prompts.py +4 -2
- tinybird/tb/modules/agent/tools/append.py +13 -22
- tinybird/tb/modules/agent/tools/create_datafile.py +29 -25
- tinybird/tb/modules/agent/tools/deploy.py +16 -25
- tinybird/tb/modules/agent/tools/get_endpoint_stats.py +51 -0
- tinybird/tb/modules/agent/tools/mock.py +14 -25
- tinybird/tb/modules/agent/tools/plan.py +13 -23
- tinybird/tb/modules/agent/tools/read_fixture_data.py +8 -0
- tinybird/tb/modules/agent/utils.py +45 -1
- tinybird/tb/modules/deployment.py +22 -1
- {tinybird-0.0.1.dev248.dist-info → tinybird-0.0.1.dev250.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev248.dist-info → tinybird-0.0.1.dev250.dist-info}/RECORD +22 -21
- {tinybird-0.0.1.dev248.dist-info → tinybird-0.0.1.dev250.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev248.dist-info → tinybird-0.0.1.dev250.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev248.dist-info → tinybird-0.0.1.dev250.dist-info}/top_level.txt +0 -0
tinybird/ch_utils/engine.py
CHANGED
|
@@ -134,8 +134,9 @@ class TableDetails:
|
|
|
134
134
|
_version = self.details.get("version", None)
|
|
135
135
|
return _version
|
|
136
136
|
|
|
137
|
-
def is_replicated(self):
|
|
138
|
-
|
|
137
|
+
def is_replicated(self) -> bool:
|
|
138
|
+
engine: Optional[str] = self.details.get("engine", None)
|
|
139
|
+
return engine is not None and "Replicated" in engine
|
|
139
140
|
|
|
140
141
|
def is_mergetree_family(self) -> bool:
|
|
141
142
|
return self.engine is not None and "mergetree" in self.engine.lower()
|
tinybird/prompts.py
CHANGED
|
@@ -815,7 +815,7 @@ pipe_instructions = """
|
|
|
815
815
|
<pipe_file_instructions>
|
|
816
816
|
- The pipe names must be unique.
|
|
817
817
|
- Nodes do NOT use the same name as the Pipe they belong to. So if the pipe name is "my_pipe", the nodes must be named different like "my_pipe_node_1", "my_pipe_node_2", etc.
|
|
818
|
-
-
|
|
818
|
+
- Node names MUST be different from the resource names in the project.
|
|
819
819
|
- Avoid more than one node per pipe unless it is really necessary or requested by the user.
|
|
820
820
|
- No indentation is allowed for property names: DESCRIPTION, NODE, SQL, TYPE, etc.
|
|
821
821
|
- Allowed TYPE values are: endpoint, copy, materialized, sink.
|
tinybird/sql_template.py
CHANGED
|
@@ -1388,13 +1388,14 @@ _namespace = {
|
|
|
1388
1388
|
"close": None,
|
|
1389
1389
|
"print": None,
|
|
1390
1390
|
"input": None,
|
|
1391
|
-
"id": None,
|
|
1392
1391
|
}
|
|
1393
1392
|
|
|
1394
1393
|
|
|
1395
1394
|
reserved_vars = set(["_tt_tmp", "_tt_append", "isinstance", "str", "error", "custom_error", *list(vars(builtins))])
|
|
1396
1395
|
for p in DEFAULT_PARAM_NAMES: # we handle these in an specific manner
|
|
1397
1396
|
reserved_vars.discard(p) # `format` is part of builtins
|
|
1397
|
+
# Allow 'id' to be used as a template parameter - https://gitlab.com/tinybird/analytics/-/issues/19119
|
|
1398
|
+
reserved_vars.discard("id")
|
|
1398
1399
|
error_vars = ["error", "custom_error"]
|
|
1399
1400
|
|
|
1400
1401
|
|
|
@@ -1463,6 +1464,9 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
|
|
|
1463
1464
|
return Expression(f"-- disable {feature}\n")
|
|
1464
1465
|
|
|
1465
1466
|
namespace.update(_namespace)
|
|
1467
|
+
# This is to fix the issue https://gitlab.com/tinybird/analytics/-/issues/19119
|
|
1468
|
+
# We need to do the override here because if we modify the _namespace, we would filter out parameters from the users
|
|
1469
|
+
namespace.update({"id": None})
|
|
1466
1470
|
namespace.update(kwargs)
|
|
1467
1471
|
namespace.update(
|
|
1468
1472
|
{
|
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.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev250'
|
|
8
|
+
__revision__ = '1c47b89'
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
+
import shlex
|
|
1
2
|
import subprocess
|
|
2
3
|
import sys
|
|
3
4
|
from datetime import datetime
|
|
4
5
|
from functools import partial
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import Any, Optional
|
|
7
8
|
|
|
8
9
|
import click
|
|
9
|
-
from prompt_toolkit import prompt
|
|
10
|
-
from prompt_toolkit.cursor_shapes import CursorShape
|
|
11
|
-
from prompt_toolkit.styles import Style
|
|
12
10
|
from pydantic_ai import Agent, Tool
|
|
13
11
|
from pydantic_ai.messages import ModelMessage
|
|
14
12
|
|
|
@@ -28,7 +26,7 @@ from tinybird.prompts import (
|
|
|
28
26
|
from tinybird.tb.client import TinyB
|
|
29
27
|
from tinybird.tb.modules.agent.animations import ThinkingAnimation
|
|
30
28
|
from tinybird.tb.modules.agent.banner import display_banner
|
|
31
|
-
from tinybird.tb.modules.agent.memory import clear_history
|
|
29
|
+
from tinybird.tb.modules.agent.memory import clear_history
|
|
32
30
|
from tinybird.tb.modules.agent.models import create_model, model_costs
|
|
33
31
|
from tinybird.tb.modules.agent.prompts import (
|
|
34
32
|
datafile_instructions,
|
|
@@ -42,11 +40,12 @@ from tinybird.tb.modules.agent.tools.create_datafile import create_datafile
|
|
|
42
40
|
from tinybird.tb.modules.agent.tools.deploy import deploy
|
|
43
41
|
from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
|
|
44
42
|
from tinybird.tb.modules.agent.tools.explore import explore_data
|
|
43
|
+
from tinybird.tb.modules.agent.tools.get_endpoint_stats import get_endpoint_stats
|
|
45
44
|
from tinybird.tb.modules.agent.tools.mock import mock
|
|
46
45
|
from tinybird.tb.modules.agent.tools.plan import plan
|
|
47
46
|
from tinybird.tb.modules.agent.tools.preview_datafile import preview_datafile
|
|
48
47
|
from tinybird.tb.modules.agent.tools.read_fixture_data import read_fixture_data
|
|
49
|
-
from tinybird.tb.modules.agent.utils import TinybirdAgentContext
|
|
48
|
+
from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_input
|
|
50
49
|
from tinybird.tb.modules.build_common import process as build_process
|
|
51
50
|
from tinybird.tb.modules.common import _analyze, _get_tb_client
|
|
52
51
|
from tinybird.tb.modules.config import CLIConfig
|
|
@@ -60,14 +59,23 @@ from tinybird.tb.modules.project import Project
|
|
|
60
59
|
|
|
61
60
|
|
|
62
61
|
class TinybirdAgent:
|
|
63
|
-
def __init__(
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
token: str,
|
|
65
|
+
user_token: str,
|
|
66
|
+
host: str,
|
|
67
|
+
workspace_id: str,
|
|
68
|
+
project: Project,
|
|
69
|
+
dangerously_skip_permissions: bool,
|
|
70
|
+
):
|
|
64
71
|
self.token = token
|
|
72
|
+
self.user_token = user_token
|
|
65
73
|
self.host = host
|
|
66
74
|
self.dangerously_skip_permissions = dangerously_skip_permissions
|
|
67
75
|
self.project = project
|
|
68
76
|
self.messages: list[ModelMessage] = []
|
|
69
77
|
self.agent = Agent(
|
|
70
|
-
model=create_model(
|
|
78
|
+
model=create_model(user_token, host, workspace_id),
|
|
71
79
|
deps_type=TinybirdAgentContext,
|
|
72
80
|
system_prompt=f"""
|
|
73
81
|
You are a Tinybird Code, an agentic CLI that can help users to work with Tinybird.
|
|
@@ -105,6 +113,7 @@ You have access to the following tools:
|
|
|
105
113
|
8. `mock` - Create mock data for a landing datasource.
|
|
106
114
|
9. `read_fixture_data` - Read a fixture data file present in the project folder.
|
|
107
115
|
10. `append` - Append existing fixture to a datasource.
|
|
116
|
+
11. `get_endpoint_stats` - Get metrics of the requests to an endpoint.
|
|
108
117
|
|
|
109
118
|
# When creating or updating datafiles:
|
|
110
119
|
1. Use `plan` tool to plan the creation or update of resources.
|
|
@@ -116,6 +125,24 @@ You have access to the following tools:
|
|
|
116
125
|
7. If the datafile was not created, finish the process and just wait for a new user prompt.
|
|
117
126
|
8. If the datafile was created successfully, but the built failed, try to fix the error and repeat the process.
|
|
118
127
|
|
|
128
|
+
# When creating a landing datasource given a .ndjson file:
|
|
129
|
+
- If the user does not specify anything about the desired schema, create a schema like this:
|
|
130
|
+
SCHEMA >
|
|
131
|
+
`data` String `json:$`
|
|
132
|
+
- Use always json paths with .ndjson files.
|
|
133
|
+
|
|
134
|
+
# When user wants to optimize an endpoint:
|
|
135
|
+
First check if the query is optimized. E.g is filtering by a column present in the sorting key.
|
|
136
|
+
Avoid when possible to not update the landing datasource.
|
|
137
|
+
Check endpoint stats to analyze how the endpoint is performing. Use `get_endpoint_stats` tool to get the stats.
|
|
138
|
+
When your data is in Tinybird, you can create intermediate data sources to preprocess data and make the endpoints faster. This can be done by using materialized views or copy pipes.
|
|
139
|
+
- Copy pipes capture the result of a pipe at a specific point in time and write it to a target data source. They can run on a schedule or run on demand, making them ideal for event-sourced snapshots, data experimentation, and deduplication with snapshots.
|
|
140
|
+
- Materialized views continuously re-evaluate a query as new events are inserted, maintaining an always up-to-date derived dataset. Unlike copy pipes which create point-in-time snapshots, materialized views provide real-time transformations of your data.
|
|
141
|
+
Each approach has its own strengths and use cases:
|
|
142
|
+
- Use copy pipes when you need scheduled or on-demand snapshots of your data.
|
|
143
|
+
- Use materialized views when you need continuous, real-time transformations.
|
|
144
|
+
Finally, update the existing endpoint itself, do not add a new one.
|
|
145
|
+
|
|
119
146
|
IMPORTANT: If the user cancels some of the steps or there is an error in file creation, DO NOT continue with the plan. Stop the process and wait for the user before using any other tool.
|
|
120
147
|
IMPORTANT: Every time you finish a plan and start a new resource creation or update process, create a new plan before starting with the changes.
|
|
121
148
|
|
|
@@ -165,6 +192,9 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
|
|
|
165
192
|
Tool(mock, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
|
|
166
193
|
Tool(read_fixture_data, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
|
|
167
194
|
Tool(append, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
|
|
195
|
+
Tool(
|
|
196
|
+
get_endpoint_stats, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True
|
|
197
|
+
),
|
|
168
198
|
],
|
|
169
199
|
)
|
|
170
200
|
|
|
@@ -176,7 +206,7 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
|
|
|
176
206
|
user_prompt = f"{user_prompt}\n\n{resources_prompt(project)}"
|
|
177
207
|
client = TinyB(token=self.token, host=self.host)
|
|
178
208
|
folder = self.project.folder
|
|
179
|
-
|
|
209
|
+
click.echo()
|
|
180
210
|
thinking_animation = ThinkingAnimation(message="Chirping", delay=0.15)
|
|
181
211
|
thinking_animation.start()
|
|
182
212
|
result = self.agent.run_sync(
|
|
@@ -190,6 +220,8 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
|
|
|
190
220
|
mock_data=partial(mock_data, project=project, config=config),
|
|
191
221
|
append_data=partial(append_data, config=config),
|
|
192
222
|
analyze_fixture=partial(analyze_fixture, config=config),
|
|
223
|
+
execute_cloud_query=partial(execute_cloud_query, config=config),
|
|
224
|
+
execute_local_query=partial(execute_local_query, config=config),
|
|
193
225
|
get_project_files=project.get_project_files,
|
|
194
226
|
folder=folder,
|
|
195
227
|
thinking_animation=thinking_animation,
|
|
@@ -209,21 +241,23 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
|
|
|
209
241
|
request_tokens * model_costs["input_cost_per_token"]
|
|
210
242
|
+ response_tokens * model_costs["output_cost_per_token"]
|
|
211
243
|
)
|
|
212
|
-
click.echo()
|
|
213
244
|
click.echo(result.output)
|
|
214
245
|
click.echo("\n")
|
|
215
246
|
click.echo(f"Input tokens: {request_tokens}")
|
|
216
247
|
click.echo(f"Output tokens: {response_tokens}")
|
|
217
248
|
click.echo(f"Total tokens: {total_tokens}")
|
|
218
249
|
click.echo(f"Cost: ${cost:.6f}")
|
|
250
|
+
click.echo("\n")
|
|
219
251
|
|
|
220
252
|
|
|
221
253
|
def run_agent(config: dict[str, Any], project: Project, dangerously_skip_permissions: bool):
|
|
222
254
|
token = config.get("token", None)
|
|
223
255
|
host = config.get("host", None)
|
|
224
|
-
|
|
256
|
+
user_token = config.get("user_token", None)
|
|
257
|
+
workspace_id = config.get("id", None)
|
|
258
|
+
workspace_name = config.get("name", None)
|
|
225
259
|
try:
|
|
226
|
-
if not token or not host:
|
|
260
|
+
if not token or not host or not workspace_id or not user_token:
|
|
227
261
|
yes = click.confirm(
|
|
228
262
|
FeedbackManager.warning(
|
|
229
263
|
message="Tinybird Code requires authentication. Do you want to authenticate now? [Y/n]"
|
|
@@ -238,16 +272,17 @@ def run_agent(config: dict[str, Any], project: Project, dangerously_skip_permiss
|
|
|
238
272
|
click.echo()
|
|
239
273
|
cli_config = CLIConfig.get_project_config()
|
|
240
274
|
token = cli_config.get_token()
|
|
275
|
+
user_token = cli_config.get_user_token()
|
|
241
276
|
host = cli_config.get_host()
|
|
242
277
|
|
|
243
|
-
if not token or not host:
|
|
278
|
+
if not token or not host or not user_token:
|
|
244
279
|
click.echo(
|
|
245
280
|
FeedbackManager.error(message="Tinybird Code requires authentication. Run 'tb login' first.")
|
|
246
281
|
)
|
|
247
282
|
return
|
|
248
283
|
|
|
249
284
|
display_banner()
|
|
250
|
-
agent = TinybirdAgent(token, host, project, dangerously_skip_permissions)
|
|
285
|
+
agent = TinybirdAgent(token, user_token, host, workspace_id, project, dangerously_skip_permissions)
|
|
251
286
|
click.echo()
|
|
252
287
|
click.echo(FeedbackManager.info(message="Describe what you want to create and I'll help you build it"))
|
|
253
288
|
click.echo(FeedbackManager.info(message="Run /help for more commands"))
|
|
@@ -262,18 +297,12 @@ def run_agent(config: dict[str, Any], project: Project, dangerously_skip_permiss
|
|
|
262
297
|
try:
|
|
263
298
|
while True:
|
|
264
299
|
try:
|
|
265
|
-
user_input =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
"prompt": "#40a8a8 bold",
|
|
272
|
-
"": "", # Normal color for user input
|
|
273
|
-
}
|
|
274
|
-
),
|
|
275
|
-
)
|
|
276
|
-
|
|
300
|
+
user_input = show_input(workspace_name)
|
|
301
|
+
if user_input.startswith("tb "):
|
|
302
|
+
cmd_parts = shlex.split(user_input)
|
|
303
|
+
subprocess.run(cmd_parts)
|
|
304
|
+
click.echo()
|
|
305
|
+
continue
|
|
277
306
|
if user_input.lower() in ["/exit", "/quit"]:
|
|
278
307
|
click.echo(FeedbackManager.info(message="Goodbye!"))
|
|
279
308
|
break
|
|
@@ -378,3 +407,13 @@ def analyze_fixture(config: dict[str, Any], fixture_path: str) -> dict[str, Any]
|
|
|
378
407
|
local_client = get_tinybird_local_client(config, test=False, silent=True)
|
|
379
408
|
meta, _data = _analyze(fixture_path, local_client, Path(fixture_path).suffix.lstrip("."))
|
|
380
409
|
return meta
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def execute_cloud_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> str:
|
|
413
|
+
client = _get_tb_client(config["token"], config["host"])
|
|
414
|
+
return client.query(sql=query, pipeline=pipe_name)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def execute_local_query(config: dict[str, Any], query: str, pipe_name: Optional[str] = None) -> str:
|
|
418
|
+
local_client = get_tinybird_local_client(config, test=False, silent=True)
|
|
419
|
+
return local_client.query(sql=query, pipeline=pipe_name)
|
|
@@ -4,15 +4,13 @@ import threading
|
|
|
4
4
|
from time import sleep
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
|
-
import click
|
|
8
|
-
|
|
9
7
|
|
|
10
8
|
class ThinkingAnimation:
|
|
11
9
|
"""Thinking animation that shows changing sparkles as a prefix."""
|
|
12
10
|
|
|
13
11
|
def __init__(
|
|
14
12
|
self,
|
|
15
|
-
message: str = "
|
|
13
|
+
message: str = "Chirping",
|
|
16
14
|
delay: float = 0.15,
|
|
17
15
|
colors: bool = True,
|
|
18
16
|
dots: bool = True,
|
|
@@ -37,17 +35,35 @@ class ThinkingAnimation:
|
|
|
37
35
|
]
|
|
38
36
|
self.reset_color = "\033[0m"
|
|
39
37
|
|
|
40
|
-
def start(self):
|
|
38
|
+
def start(self, message: Optional[str] = None):
|
|
41
39
|
"""Start the animation in a separate thread."""
|
|
40
|
+
# Stop any existing animation first
|
|
41
|
+
if self.running:
|
|
42
|
+
self._stop_without_reset()
|
|
43
|
+
|
|
44
|
+
if message:
|
|
45
|
+
self.message = message
|
|
42
46
|
self.running = True
|
|
43
47
|
self.thread = threading.Thread(target=self._run_animation)
|
|
44
48
|
self.thread.daemon = True
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
# Clear the current line before starting new animation
|
|
51
|
+
sys.stdout.write("\r" + " " * (len(self.message) + 10) + "\r")
|
|
52
|
+
sys.stdout.flush()
|
|
47
53
|
self.thread.start()
|
|
48
54
|
|
|
49
55
|
def stop(self):
|
|
50
56
|
"""Stop the animation."""
|
|
57
|
+
self.message = "Chirping"
|
|
58
|
+
self.running = False
|
|
59
|
+
if self.thread:
|
|
60
|
+
self.thread.join()
|
|
61
|
+
# Clear the line and reset cursor position
|
|
62
|
+
sys.stdout.write("\r" + " " * (len(self.message) + 10) + "\r")
|
|
63
|
+
sys.stdout.flush()
|
|
64
|
+
|
|
65
|
+
def _stop_without_reset(self):
|
|
66
|
+
"""Stop the animation without resetting the message."""
|
|
51
67
|
self.running = False
|
|
52
68
|
if self.thread:
|
|
53
69
|
self.thread.join()
|
|
@@ -78,8 +94,11 @@ class ThinkingAnimation:
|
|
|
78
94
|
else:
|
|
79
95
|
display_message = self.message
|
|
80
96
|
|
|
81
|
-
# Print the message with the prefix sparkle
|
|
82
|
-
|
|
97
|
+
# Print the message with the prefix sparkle, padding to clear any leftover characters
|
|
98
|
+
line_content = f"{colored_sparkle} {display_message}"
|
|
99
|
+
# Pad with spaces to clear any leftover characters from longer previous messages
|
|
100
|
+
padded_line = line_content.ljust(len(self.message) + 10)
|
|
101
|
+
sys.stdout.write(f"\r{padded_line}")
|
|
83
102
|
sys.stdout.flush()
|
|
84
103
|
|
|
85
104
|
sleep(self.delay)
|
|
@@ -7,11 +7,12 @@ from pydantic_ai.providers.anthropic import AnthropicProvider
|
|
|
7
7
|
def create_model(
|
|
8
8
|
token: str,
|
|
9
9
|
base_url: str,
|
|
10
|
+
workspace_id: str,
|
|
10
11
|
model: AnthropicModelName = "claude-4-sonnet-20250514",
|
|
11
12
|
):
|
|
12
13
|
client = AsyncAnthropic(
|
|
13
14
|
base_url=base_url,
|
|
14
|
-
http_client=AsyncClient(params={"token": token}),
|
|
15
|
+
http_client=AsyncClient(params={"token": token, "workspace_id": workspace_id}),
|
|
15
16
|
auth_token=token,
|
|
16
17
|
)
|
|
17
18
|
return AnthropicModel(
|
|
@@ -22,8 +22,10 @@ Steps:
|
|
|
22
22
|
<dev_notes>
|
|
23
23
|
You can skip steps where resources will not be created or updated.
|
|
24
24
|
Always add 'Build project' step after generating resources.
|
|
25
|
-
Always add 'Generate mock data' step after building project if a landing datasource was created.
|
|
26
|
-
Always add 'Append existing fixture' step after building project if a
|
|
25
|
+
Always add 'Generate mock data' step after building project if a landing datasource was created without providing a fixture file.
|
|
26
|
+
Always add 'Append existing fixture' step after building project if a landing datasource was created after providing a fixture file.
|
|
27
|
+
Solve the specific user request, do not add extra steps that are not related to the user request.
|
|
28
|
+
Reuse the existing resources if possible.
|
|
27
29
|
</dev_notes>
|
|
28
30
|
|
|
29
31
|
Resource dependencies:
|
|
@@ -1,29 +1,10 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from pydantic_ai import RunContext
|
|
3
3
|
|
|
4
|
-
from tinybird.tb.modules.agent.utils import TinybirdAgentContext,
|
|
4
|
+
from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
|
|
5
5
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def get_append_confirmation(datasource_name: str) -> bool:
|
|
9
|
-
"""Get user confirmation for appending existing fixture"""
|
|
10
|
-
while True:
|
|
11
|
-
result = show_options(
|
|
12
|
-
options=["Yes, append existing fixture", "No, and tell Tinybird Code what to do"],
|
|
13
|
-
title=f"Do you want to append existing fixture for datasource {datasource_name}?",
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
if result is None: # Cancelled
|
|
17
|
-
return False
|
|
18
|
-
|
|
19
|
-
if result.startswith("Yes"):
|
|
20
|
-
return True
|
|
21
|
-
elif result.startswith("No"):
|
|
22
|
-
return False
|
|
23
|
-
|
|
24
|
-
return False
|
|
25
|
-
|
|
26
|
-
|
|
27
8
|
def append(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str) -> str:
|
|
28
9
|
"""Append existing fixture to a datasource
|
|
29
10
|
|
|
@@ -36,10 +17,20 @@ def append(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_
|
|
|
36
17
|
"""
|
|
37
18
|
try:
|
|
38
19
|
ctx.deps.thinking_animation.stop()
|
|
39
|
-
confirmation =
|
|
20
|
+
confirmation = show_confirmation(
|
|
21
|
+
title=f"Append existing fixture for datasource {datasource_name}?",
|
|
22
|
+
skip_confirmation=ctx.deps.dangerously_skip_permissions,
|
|
23
|
+
)
|
|
24
|
+
|
|
40
25
|
ctx.deps.thinking_animation.start()
|
|
41
26
|
|
|
42
|
-
if
|
|
27
|
+
if confirmation == "review":
|
|
28
|
+
click.echo()
|
|
29
|
+
feedback = show_input(ctx.deps.workspace_name)
|
|
30
|
+
ctx.deps.thinking_animation.start()
|
|
31
|
+
return f"User did not confirm the proposed plan and gave the following feedback: {feedback}"
|
|
32
|
+
|
|
33
|
+
if confirmation == "cancel":
|
|
43
34
|
return "User rejected appending existing fixture. Skipping..."
|
|
44
35
|
|
|
45
36
|
ctx.deps.thinking_animation.stop()
|
|
@@ -3,31 +3,17 @@ from pathlib import Path
|
|
|
3
3
|
import click
|
|
4
4
|
from pydantic_ai import RunContext
|
|
5
5
|
|
|
6
|
-
from tinybird.tb.modules.agent.utils import
|
|
6
|
+
from tinybird.tb.modules.agent.utils import (
|
|
7
|
+
Datafile,
|
|
8
|
+
TinybirdAgentContext,
|
|
9
|
+
create_terminal_box,
|
|
10
|
+
show_confirmation,
|
|
11
|
+
show_input,
|
|
12
|
+
)
|
|
7
13
|
from tinybird.tb.modules.exceptions import CLIBuildException
|
|
8
14
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
9
15
|
|
|
10
16
|
|
|
11
|
-
def get_resource_confirmation(resource: Datafile, exists: bool) -> bool:
|
|
12
|
-
"""Get user confirmation for creating a resource"""
|
|
13
|
-
while True:
|
|
14
|
-
action = "create" if not exists else "update"
|
|
15
|
-
result = show_options(
|
|
16
|
-
options=[f"Yes, {action} {resource.type} '{resource.name}'", "No, and tell Tinybird Code what to do"],
|
|
17
|
-
title=f"What would you like to do with {resource.type} '{resource.name}'?",
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
if result is None: # Cancelled
|
|
21
|
-
return False
|
|
22
|
-
|
|
23
|
-
if result.startswith("Yes"):
|
|
24
|
-
return True
|
|
25
|
-
elif result.startswith("No"):
|
|
26
|
-
return False
|
|
27
|
-
|
|
28
|
-
return False
|
|
29
|
-
|
|
30
|
-
|
|
31
17
|
def create_datafile(ctx: RunContext[TinybirdAgentContext], resource: Datafile) -> str:
|
|
32
18
|
"""Given a resource representation, create a file in the project folder
|
|
33
19
|
|
|
@@ -48,19 +34,34 @@ def create_datafile(ctx: RunContext[TinybirdAgentContext], resource: Datafile) -
|
|
|
48
34
|
else:
|
|
49
35
|
content = create_terminal_box(resource.content, title=resource.pathname)
|
|
50
36
|
click.echo(content)
|
|
51
|
-
confirmation = ctx.deps.dangerously_skip_permissions or get_resource_confirmation(resource, exists)
|
|
52
37
|
|
|
53
|
-
if not
|
|
38
|
+
action = "Create" if not exists else "Update"
|
|
39
|
+
confirmation = show_confirmation(
|
|
40
|
+
title=f"{action} '{resource.pathname}'?",
|
|
41
|
+
skip_confirmation=ctx.deps.dangerously_skip_permissions,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if confirmation == "review":
|
|
45
|
+
click.echo()
|
|
46
|
+
feedback = show_input(ctx.deps.workspace_name)
|
|
47
|
+
ctx.deps.thinking_animation.start()
|
|
48
|
+
return f"User did not confirm the proposed changes and gave the following feedback: {feedback}"
|
|
49
|
+
|
|
50
|
+
if confirmation == "cancel":
|
|
54
51
|
ctx.deps.thinking_animation.start()
|
|
55
|
-
return f"
|
|
52
|
+
return f"User cancelled {action} of {resource.pathname}. Stop resource creation."
|
|
56
53
|
|
|
54
|
+
action_text = "Creating" if not exists else "Updating"
|
|
55
|
+
click.echo(FeedbackManager.highlight(message=f"\n» {action_text} {resource.pathname}..."))
|
|
57
56
|
folder_path = path.parent
|
|
58
57
|
folder_path.mkdir(parents=True, exist_ok=True)
|
|
59
58
|
path.touch(exist_ok=True)
|
|
60
59
|
path.write_text(resource.content)
|
|
61
60
|
ctx.deps.build_project(test=True, silent=True)
|
|
61
|
+
action_text = "created" if not exists else "updated"
|
|
62
|
+
click.echo(FeedbackManager.success(message=f"✓ {resource.pathname} {action_text} successfully"))
|
|
62
63
|
ctx.deps.thinking_animation.start()
|
|
63
|
-
return f"
|
|
64
|
+
return f"{action_text} {resource.pathname}"
|
|
64
65
|
|
|
65
66
|
except CLIBuildException as e:
|
|
66
67
|
ctx.deps.thinking_animation.stop()
|
|
@@ -68,4 +69,7 @@ def create_datafile(ctx: RunContext[TinybirdAgentContext], resource: Datafile) -
|
|
|
68
69
|
ctx.deps.thinking_animation.start()
|
|
69
70
|
return f"Error building project: {e}"
|
|
70
71
|
except Exception as e:
|
|
72
|
+
ctx.deps.thinking_animation.stop()
|
|
73
|
+
click.echo(FeedbackManager.error(message=e))
|
|
74
|
+
ctx.deps.thinking_animation.start()
|
|
71
75
|
return f"Error creating {resource.pathname}: {e}"
|
|
@@ -1,41 +1,32 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from pydantic_ai import RunContext
|
|
3
3
|
|
|
4
|
-
from tinybird.tb.modules.agent.utils import TinybirdAgentContext,
|
|
4
|
+
from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
|
|
5
5
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def get_deploy_confirmation() -> bool:
|
|
9
|
-
"""Get user confirmation for deploying the project"""
|
|
10
|
-
while True:
|
|
11
|
-
result = show_options(
|
|
12
|
-
options=["Yes, deploy the project", "No, and tell Tinybird Code what to do"],
|
|
13
|
-
title="Do you want to deploy the project?",
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
if result is None: # Cancelled
|
|
17
|
-
return False
|
|
18
|
-
|
|
19
|
-
if result.startswith("Yes"):
|
|
20
|
-
return True
|
|
21
|
-
elif result.startswith("No"):
|
|
22
|
-
return False
|
|
23
|
-
|
|
24
|
-
return False
|
|
25
|
-
|
|
26
|
-
|
|
27
8
|
def deploy(ctx: RunContext[TinybirdAgentContext]) -> str:
|
|
28
9
|
"""Deploy the project"""
|
|
29
10
|
try:
|
|
30
11
|
ctx.deps.thinking_animation.stop()
|
|
31
|
-
confirmation =
|
|
32
|
-
|
|
12
|
+
confirmation = show_confirmation(
|
|
13
|
+
title="Deploy the project?",
|
|
14
|
+
skip_confirmation=ctx.deps.dangerously_skip_permissions,
|
|
15
|
+
)
|
|
33
16
|
|
|
34
|
-
if
|
|
35
|
-
|
|
17
|
+
if confirmation == "review":
|
|
18
|
+
click.echo()
|
|
19
|
+
feedback = show_input(ctx.deps.workspace_name)
|
|
20
|
+
ctx.deps.thinking_animation.start()
|
|
21
|
+
return f"User did not confirm deployment and gave the following feedback: {feedback}"
|
|
36
22
|
|
|
37
|
-
|
|
23
|
+
if confirmation == "cancel":
|
|
24
|
+
ctx.deps.thinking_animation.start()
|
|
25
|
+
return "User cancelled deployment. Stop deployment."
|
|
26
|
+
|
|
27
|
+
click.echo(FeedbackManager.highlight(message="\n» Deploying project..."))
|
|
38
28
|
ctx.deps.deploy_project()
|
|
29
|
+
click.echo(FeedbackManager.success(message="✓ Project deployed successfully"))
|
|
39
30
|
ctx.deps.thinking_animation.start()
|
|
40
31
|
return "Project deployed successfully"
|
|
41
32
|
except Exception as e:
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from pydantic_ai import RunContext
|
|
3
|
+
|
|
4
|
+
from tinybird.tb.modules.agent.utils import TinybirdAgentContext
|
|
5
|
+
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_endpoint_stats(
|
|
9
|
+
ctx: RunContext[TinybirdAgentContext], endpoint_name: str, interval_days: int = 1, cloud: bool = True
|
|
10
|
+
):
|
|
11
|
+
"""Get stats for an endpoint:
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
endpoint_name (str): The name of the endpoint to get stats for. Required.
|
|
15
|
+
interval_days (int): The number of days to get stats for. Optional.
|
|
16
|
+
cloud (bool): Whether to get stats from cloud or local. Optional.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
str: The result of the stats.
|
|
20
|
+
"""
|
|
21
|
+
if interval_days == 1:
|
|
22
|
+
pipe_stats = "tinybird.pipe_stats_rt"
|
|
23
|
+
date_column = "start_datetime"
|
|
24
|
+
else:
|
|
25
|
+
pipe_stats = "tinybird.pipe_stats"
|
|
26
|
+
date_column = "date"
|
|
27
|
+
|
|
28
|
+
days = "day" if interval_days == 1 else "days"
|
|
29
|
+
cloud_or_local = "cloud" if cloud else "local"
|
|
30
|
+
ctx.deps.thinking_animation.stop()
|
|
31
|
+
|
|
32
|
+
click.echo(
|
|
33
|
+
FeedbackManager.highlight(
|
|
34
|
+
message=f"» Analyzing {cloud_or_local} requests in the last {interval_days} {days} for '{endpoint_name}' endpoint"
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
query = f"""SELECT * FROM {pipe_stats}
|
|
39
|
+
WHERE {date_column} > NOW() - INTERVAL {interval_days} DAY
|
|
40
|
+
AND pipe_name = '{endpoint_name}'
|
|
41
|
+
LIMIT 100
|
|
42
|
+
FORMAT JSON
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
execute_query = ctx.deps.execute_cloud_query if cloud else ctx.deps.execute_local_query
|
|
46
|
+
|
|
47
|
+
result = execute_query(query=query)
|
|
48
|
+
click.echo(FeedbackManager.success(message="✓ Done!"))
|
|
49
|
+
click.echo()
|
|
50
|
+
ctx.deps.thinking_animation.start()
|
|
51
|
+
return f"Result for {endpoint_name} in the last {interval_days} {days}: {result}"
|
|
@@ -1,30 +1,11 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from pydantic_ai import RunContext
|
|
3
3
|
|
|
4
|
-
from tinybird.tb.modules.agent.utils import TinybirdAgentContext,
|
|
4
|
+
from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
|
|
5
5
|
from tinybird.tb.modules.datafile.fixture import persist_fixture
|
|
6
6
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def get_mock_confirmation(datasource_name: str) -> bool:
|
|
10
|
-
"""Get user confirmation for creating mock data"""
|
|
11
|
-
while True:
|
|
12
|
-
result = show_options(
|
|
13
|
-
options=["Yes, create mock data", "No, and tell Tinybird Code what to do"],
|
|
14
|
-
title=f"Do you want to generate mock data for datasource {datasource_name}?",
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
if result is None: # Cancelled
|
|
18
|
-
return False
|
|
19
|
-
|
|
20
|
-
if result.startswith("Yes"):
|
|
21
|
-
return True
|
|
22
|
-
elif result.startswith("No"):
|
|
23
|
-
return False
|
|
24
|
-
|
|
25
|
-
return False
|
|
26
|
-
|
|
27
|
-
|
|
28
9
|
def mock(ctx: RunContext[TinybirdAgentContext], datasource_name: str, data_format: str, rows: int) -> str:
|
|
29
10
|
"""Create mock data for a datasource
|
|
30
11
|
|
|
@@ -38,13 +19,21 @@ def mock(ctx: RunContext[TinybirdAgentContext], datasource_name: str, data_forma
|
|
|
38
19
|
"""
|
|
39
20
|
try:
|
|
40
21
|
ctx.deps.thinking_animation.stop()
|
|
41
|
-
confirmation =
|
|
42
|
-
|
|
22
|
+
confirmation = show_confirmation(
|
|
23
|
+
title=f"Generate mock data for datasource '{datasource_name}'?",
|
|
24
|
+
skip_confirmation=ctx.deps.dangerously_skip_permissions,
|
|
25
|
+
)
|
|
43
26
|
|
|
44
|
-
if
|
|
45
|
-
|
|
27
|
+
if confirmation == "review":
|
|
28
|
+
click.echo()
|
|
29
|
+
feedback = show_input(ctx.deps.workspace_name)
|
|
30
|
+
ctx.deps.thinking_animation.start()
|
|
31
|
+
return f"User did not confirm mock data generation and gave the following feedback: {feedback}"
|
|
32
|
+
|
|
33
|
+
if confirmation == "cancel":
|
|
34
|
+
ctx.deps.thinking_animation.start()
|
|
35
|
+
return "User cancelled mock data generation. Stop mock data generation."
|
|
46
36
|
|
|
47
|
-
ctx.deps.thinking_animation.stop()
|
|
48
37
|
click.echo(FeedbackManager.highlight(message=f"\n» Generating mock data for {datasource_name}..."))
|
|
49
38
|
data = ctx.deps.mock_data(datasource_name=datasource_name, data_format=data_format, rows=rows)
|
|
50
39
|
fixture_path = persist_fixture(datasource_name, data, ctx.deps.folder, format=data_format)
|
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from pydantic_ai import RunContext
|
|
3
3
|
|
|
4
|
-
from tinybird.tb.modules.agent.utils import TinybirdAgentContext,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def get_plan_confirmation() -> bool:
|
|
8
|
-
"""Get user confirmation for implementing a plan"""
|
|
9
|
-
while True:
|
|
10
|
-
result = show_options(
|
|
11
|
-
options=["Yes, implement the plan", "No, and tell Tinybird Code what to do"],
|
|
12
|
-
title="Do you want to implement the plan?",
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
if result is None: # Cancelled
|
|
16
|
-
return False
|
|
17
|
-
|
|
18
|
-
if result.startswith("Yes"):
|
|
19
|
-
return True
|
|
20
|
-
elif result.startswith("No"):
|
|
21
|
-
return False
|
|
22
|
-
|
|
23
|
-
return False
|
|
4
|
+
from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_confirmation, show_input
|
|
24
5
|
|
|
25
6
|
|
|
26
7
|
def plan(ctx: RunContext[TinybirdAgentContext], plan: str) -> str:
|
|
@@ -35,11 +16,20 @@ def plan(ctx: RunContext[TinybirdAgentContext], plan: str) -> str:
|
|
|
35
16
|
try:
|
|
36
17
|
ctx.deps.thinking_animation.stop()
|
|
37
18
|
click.echo(plan)
|
|
38
|
-
confirmation =
|
|
19
|
+
confirmation = show_confirmation(
|
|
20
|
+
title="Do you want to implement the plan?", skip_confirmation=ctx.deps.dangerously_skip_permissions
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if confirmation == "review":
|
|
24
|
+
click.echo()
|
|
25
|
+
feedback = show_input(ctx.deps.workspace_name)
|
|
26
|
+
ctx.deps.thinking_animation.start()
|
|
27
|
+
return f"User did not confirm the proposed plan and gave the following feedback: {feedback}"
|
|
28
|
+
|
|
39
29
|
ctx.deps.thinking_animation.start()
|
|
40
30
|
|
|
41
|
-
if
|
|
42
|
-
return "
|
|
31
|
+
if confirmation == "cancel":
|
|
32
|
+
return "User cancelled the plan. Stop the process."
|
|
43
33
|
|
|
44
34
|
return "User confirmed the plan. Implementing..."
|
|
45
35
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
+
import click
|
|
4
5
|
from pydantic_ai import RunContext
|
|
5
6
|
|
|
6
7
|
from tinybird.tb.modules.agent.utils import TinybirdAgentContext
|
|
8
|
+
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def read_fixture_data(ctx: RunContext[TinybirdAgentContext], fixture_pathname: str):
|
|
@@ -15,12 +17,18 @@ def read_fixture_data(ctx: RunContext[TinybirdAgentContext], fixture_pathname: s
|
|
|
15
17
|
Returns:
|
|
16
18
|
str: The content of the fixture data file.
|
|
17
19
|
"""
|
|
20
|
+
ctx.deps.thinking_animation.stop()
|
|
21
|
+
click.echo(FeedbackManager.highlight(message=f"» Analyzing {fixture_pathname}..."))
|
|
18
22
|
fixture_path = Path(ctx.deps.folder) / fixture_pathname.lstrip("/")
|
|
19
23
|
|
|
20
24
|
if not fixture_path.exists():
|
|
25
|
+
click.echo(FeedbackManager.error(message=f"No fixture data found for {fixture_pathname}."))
|
|
26
|
+
ctx.deps.thinking_animation.start()
|
|
21
27
|
return f"No fixture data found for {fixture_pathname}. Please check the name of the fixture and try again."
|
|
22
28
|
|
|
23
29
|
response = ctx.deps.analyze_fixture(fixture_path=str(fixture_path))
|
|
30
|
+
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
31
|
+
ctx.deps.thinking_animation.start()
|
|
24
32
|
# limit content to first 10 rows
|
|
25
33
|
data = response["preview"]["data"][:10]
|
|
26
34
|
columns = response["analysis"]["columns"]
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import difflib
|
|
2
2
|
import os
|
|
3
3
|
from contextlib import contextmanager
|
|
4
|
-
from typing import Any, Callable, List, Optional, Tuple
|
|
4
|
+
from typing import Any, Callable, List, Literal, Optional, Tuple
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
|
+
from prompt_toolkit import prompt as prompt_toolkit_prompt
|
|
7
8
|
from prompt_toolkit.application import Application, get_app
|
|
9
|
+
from prompt_toolkit.cursor_shapes import CursorShape
|
|
8
10
|
from prompt_toolkit.filters import IsDone
|
|
9
11
|
from prompt_toolkit.key_binding import KeyBindings
|
|
10
12
|
from prompt_toolkit.layout import Layout
|
|
@@ -17,6 +19,8 @@ from prompt_toolkit.shortcuts import PromptSession
|
|
|
17
19
|
from prompt_toolkit.styles import Style as PromptStyle
|
|
18
20
|
from pydantic import BaseModel, Field
|
|
19
21
|
|
|
22
|
+
from tinybird.tb.modules.agent.memory import load_history
|
|
23
|
+
|
|
20
24
|
try:
|
|
21
25
|
from colorama import Back, Fore, Style, init
|
|
22
26
|
|
|
@@ -38,6 +42,8 @@ class TinybirdAgentContext(BaseModel):
|
|
|
38
42
|
mock_data: Callable[..., list[dict[str, Any]]]
|
|
39
43
|
append_data: Callable[..., None]
|
|
40
44
|
analyze_fixture: Callable[..., dict[str, Any]]
|
|
45
|
+
execute_cloud_query: Callable[..., str]
|
|
46
|
+
execute_local_query: Callable[..., str]
|
|
41
47
|
dangerously_skip_permissions: bool
|
|
42
48
|
|
|
43
49
|
|
|
@@ -345,6 +351,9 @@ def show_options(options: List[str], title: str = "Select an option") -> Optiona
|
|
|
345
351
|
return result["option"]
|
|
346
352
|
|
|
347
353
|
|
|
354
|
+
ConfirmationResult = Literal["yes", "review", "cancel"]
|
|
355
|
+
|
|
356
|
+
|
|
348
357
|
def load_existing_resources(folder: str) -> str:
|
|
349
358
|
"""Load existing Tinybird resources from the workspace"""
|
|
350
359
|
existing_resources = []
|
|
@@ -677,3 +686,38 @@ def _process_line(
|
|
|
677
686
|
remaining = remaining[content_width:]
|
|
678
687
|
|
|
679
688
|
return processed
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def show_input(workspace_name: str) -> str:
|
|
692
|
+
return prompt_toolkit_prompt(
|
|
693
|
+
[("class:prompt", f"tb ({workspace_name}) » ")],
|
|
694
|
+
history=load_history(),
|
|
695
|
+
cursor=CursorShape.BLOCK,
|
|
696
|
+
style=PromptStyle.from_dict(
|
|
697
|
+
{
|
|
698
|
+
"prompt": "#40a8a8 bold",
|
|
699
|
+
"": "", # Normal color for user input
|
|
700
|
+
}
|
|
701
|
+
),
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def show_confirmation(title: str, skip_confirmation: bool = False) -> ConfirmationResult:
|
|
706
|
+
if skip_confirmation:
|
|
707
|
+
return "yes"
|
|
708
|
+
|
|
709
|
+
while True:
|
|
710
|
+
result = show_options(
|
|
711
|
+
options=["Yes, continue", "No, tell Tinybird Code what to do", "Cancel"],
|
|
712
|
+
title=title,
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
if result is None: # Cancelled
|
|
716
|
+
return "cancel"
|
|
717
|
+
|
|
718
|
+
if result.startswith("Yes"):
|
|
719
|
+
return "yes"
|
|
720
|
+
elif result.startswith("No"):
|
|
721
|
+
return "review"
|
|
722
|
+
|
|
723
|
+
return "cancel"
|
|
@@ -211,6 +211,7 @@ def deployment_ls(ctx: click.Context, include_deleted: bool) -> None:
|
|
|
211
211
|
List all the deployments you have in the project.
|
|
212
212
|
"""
|
|
213
213
|
client = ctx.ensure_object(dict)["client"]
|
|
214
|
+
output = ctx.ensure_object(dict)["output"]
|
|
214
215
|
|
|
215
216
|
TINYBIRD_API_KEY = client.token
|
|
216
217
|
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
@@ -243,7 +244,27 @@ def deployment_ls(ctx: click.Context, include_deleted: bool) -> None:
|
|
|
243
244
|
)
|
|
244
245
|
|
|
245
246
|
table.reverse()
|
|
246
|
-
|
|
247
|
+
|
|
248
|
+
# Handle different output formats
|
|
249
|
+
if output == "json":
|
|
250
|
+
# Create JSON structure
|
|
251
|
+
deployments_json = []
|
|
252
|
+
for row in table:
|
|
253
|
+
deployments_json.append({"id": row[0], "status": row[1], "created_at": row[2]})
|
|
254
|
+
from tinybird.tb.modules.common import echo_json
|
|
255
|
+
|
|
256
|
+
echo_json({"deployments": deployments_json})
|
|
257
|
+
elif output == "csv":
|
|
258
|
+
# Create CSV output
|
|
259
|
+
csv_output = f"{columns[0]},{columns[1]},{columns[2]}\n"
|
|
260
|
+
for row in table:
|
|
261
|
+
csv_output += f"{row[0]},{row[1]},{row[2]}\n"
|
|
262
|
+
from tinybird.tb.modules.common import force_echo
|
|
263
|
+
|
|
264
|
+
force_echo(csv_output)
|
|
265
|
+
else:
|
|
266
|
+
# Default human-readable output
|
|
267
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
247
268
|
|
|
248
269
|
|
|
249
270
|
@deployment_group.command(name="promote")
|
|
@@ -3,21 +3,21 @@ tinybird/context.py,sha256=FfqYfrGX_I7PKGTQo93utaKPDNVYWelg4Hsp3evX5wM,1291
|
|
|
3
3
|
tinybird/datatypes.py,sha256=r4WCvspmrXTJHiPjjyOTiZyZl31FO3Ynkwq4LQsYm6E,11059
|
|
4
4
|
tinybird/feedback_manager.py,sha256=1INQFfRfuMCb9lfB8KNf4r6qC2khW568hoHjtk-wshI,69305
|
|
5
5
|
tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
|
|
6
|
-
tinybird/prompts.py,sha256=
|
|
6
|
+
tinybird/prompts.py,sha256=4VmdaMX7oUFoqjseXe8QuF9wTtIabbDkdwVGmd34S7s,45502
|
|
7
7
|
tinybird/sql.py,sha256=BufnOgclQokDyihtuXesOwHBsebN6wRXIxO5wKRkOwE,48299
|
|
8
|
-
tinybird/sql_template.py,sha256=
|
|
8
|
+
tinybird/sql_template.py,sha256=AezE1o6_BzbHFi0J9OIqTrXQ5WvoX5eNVq4QCbFjGcs,100338
|
|
9
9
|
tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
|
|
10
10
|
tinybird/sql_toolset.py,sha256=M2rpLYkgV2W8NnYEYPC1tJdpy4uZHXVF64NBSKLQka4,19549
|
|
11
11
|
tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
|
|
12
12
|
tinybird/tornado_template.py,sha256=jjNVDMnkYFWXflmT8KU_Ssbo5vR8KQq3EJMk5vYgXRw,41959
|
|
13
13
|
tinybird/ch_utils/constants.py,sha256=fPgZtwbr1ymxaW7uqVWHKmAbt7uGj3SxCCS3xsEMJqA,4151
|
|
14
|
-
tinybird/ch_utils/engine.py,sha256=
|
|
14
|
+
tinybird/ch_utils/engine.py,sha256=4X1B-iuhdW_mxKnX_m3iCsxgP9RPVgR75g7yH1vsJ6A,40851
|
|
15
15
|
tinybird/datafile/common.py,sha256=J0Oydru3Nh3oCx66EEEHGxm2-r5_NfxvZxiFhBjgAH0,98428
|
|
16
16
|
tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
|
|
17
17
|
tinybird/datafile/parse_connection.py,sha256=tRyn2Rpr1TeWet5BXmMoQgaotbGdYep1qiTak_OqC5E,1825
|
|
18
18
|
tinybird/datafile/parse_datasource.py,sha256=ssW8QeFSgglVFi3sDZj_HgkJiTJ2069v2JgqnH3CkDE,1825
|
|
19
19
|
tinybird/datafile/parse_pipe.py,sha256=xf4m0Tw44QWJzHzAm7Z7FwUoUUtr7noMYjU1NiWnX0k,3880
|
|
20
|
-
tinybird/tb/__cli__.py,sha256=
|
|
20
|
+
tinybird/tb/__cli__.py,sha256=VsfwyuLYgBIFXG2ts697b2_s6ncyZZMxVTvvxBR-C68,247
|
|
21
21
|
tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
|
|
22
22
|
tinybird/tb/cli.py,sha256=FdDFEIayjmsZEVsVSSvRiVYn_FHOVg_zWQzchnzfWho,1008
|
|
23
23
|
tinybird/tb/client.py,sha256=pJbdkWMXGAqKseNAvdsRRnl_c7I-DCMB0dWCQnG82nU,54146
|
|
@@ -32,7 +32,7 @@ tinybird/tb/modules/connection.py,sha256=-MY56NUAai6EMC4-wpi7bT0_nz_SA8QzTmHkV7H
|
|
|
32
32
|
tinybird/tb/modules/copy.py,sha256=dPZkcIDvxjJrlQUIvToO0vsEEEs4EYumbNV77-BzNoU,4404
|
|
33
33
|
tinybird/tb/modules/create.py,sha256=pJxHXG69c9Z_21s-7VuJ3RZOF_nJU51LEwiAkvI3dZY,23251
|
|
34
34
|
tinybird/tb/modules/datasource.py,sha256=cxq0VVjjidxq-v_JSIIAH7L90XNRctgNKsHRoQ_42OI,41632
|
|
35
|
-
tinybird/tb/modules/deployment.py,sha256=
|
|
35
|
+
tinybird/tb/modules/deployment.py,sha256=c-iYgq9dgVsa3m3Oqn24CT3A1omw-p4nfVnUiUynatw,12795
|
|
36
36
|
tinybird/tb/modules/deployment_common.py,sha256=Y0r3g-3d6AcihsVVa0OHer3ow3xHSV1VPskF1eI03KI,17644
|
|
37
37
|
tinybird/tb/modules/deprecations.py,sha256=rrszC1f_JJeJ8mUxGoCxckQTJFBCR8wREf4XXXN-PRc,4507
|
|
38
38
|
tinybird/tb/modules/dev_server.py,sha256=57FCKuWpErwYUYgHspYDkLWEm9F4pbvVOtMrFXX1fVU,10129
|
|
@@ -68,24 +68,25 @@ tinybird/tb/modules/watch.py,sha256=No0bK1M1_3CYuMaIgylxf7vYFJ72lTJe3brz6xQ-mJo,
|
|
|
68
68
|
tinybird/tb/modules/workspace.py,sha256=Q_8HcxMsNg8QG9aBlwcWS2umrDP5IkTIHqqz3sfmGuc,11341
|
|
69
69
|
tinybird/tb/modules/workspace_members.py,sha256=5JdkJgfuEwbq-t6vxkBhYwgsiTDxF790wsa6Xfif9nk,8608
|
|
70
70
|
tinybird/tb/modules/agent/__init__.py,sha256=i3oe3vDIWWPaicdCM0zs7D7BJ1W0k7th93ooskHAV00,54
|
|
71
|
-
tinybird/tb/modules/agent/agent.py,sha256=
|
|
72
|
-
tinybird/tb/modules/agent/animations.py,sha256=
|
|
71
|
+
tinybird/tb/modules/agent/agent.py,sha256=OLJFtfgwWa3rNjf8Uwu2dtKVhxnTfZD__lXlAGQTUL4,20532
|
|
72
|
+
tinybird/tb/modules/agent/animations.py,sha256=4WOC5_2BracttmMCrV0H91tXfWcUzQHBUaIJc5FA7tE,3490
|
|
73
73
|
tinybird/tb/modules/agent/banner.py,sha256=KX_e467uiy1gWOZ4ofTZt0GCFGQqHQ_8Ob27XLQqda0,3053
|
|
74
74
|
tinybird/tb/modules/agent/memory.py,sha256=H6SJK--2L5C87B7AJd_jMqsq3sCvFvZwZXmajuT0GBE,1171
|
|
75
|
-
tinybird/tb/modules/agent/models.py,sha256=
|
|
76
|
-
tinybird/tb/modules/agent/prompts.py,sha256=
|
|
77
|
-
tinybird/tb/modules/agent/utils.py,sha256=
|
|
75
|
+
tinybird/tb/modules/agent/models.py,sha256=LW1D27gjcd_jwFmghEzteCgToDfodX2B6B5S8BYbysw,735
|
|
76
|
+
tinybird/tb/modules/agent/prompts.py,sha256=wbe6vUnm-fskceWgP13R5VW1v_YF7_wLDe-wBN6rlWw,6998
|
|
77
|
+
tinybird/tb/modules/agent/utils.py,sha256=bqE6Edr6giOVjuxusHsNYiMp4F0HV1aek2ZE9r1URPg,26471
|
|
78
78
|
tinybird/tb/modules/agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
-
tinybird/tb/modules/agent/tools/append.py,sha256=
|
|
79
|
+
tinybird/tb/modules/agent/tools/append.py,sha256=Db576-ziulykJyaWl03zUp-KI-NVURh0WcFIAPNbNPw,1974
|
|
80
80
|
tinybird/tb/modules/agent/tools/build.py,sha256=LhzJMx6tbxC7gogIrxhfKJc-SDgoSR-FC6IunfaCdn8,758
|
|
81
|
-
tinybird/tb/modules/agent/tools/create_datafile.py,sha256=
|
|
82
|
-
tinybird/tb/modules/agent/tools/deploy.py,sha256=
|
|
81
|
+
tinybird/tb/modules/agent/tools/create_datafile.py,sha256=wcPcChACTIFKw0lKFTlhm0sWJKhQkMLPLnGNpKyeETA,2962
|
|
82
|
+
tinybird/tb/modules/agent/tools/deploy.py,sha256=WrsSlaufKGOBx0S13uoMQQH2DnKue5LQ231Rx4RXh2I,1443
|
|
83
83
|
tinybird/tb/modules/agent/tools/deploy_check.py,sha256=VqMYC7l3_cihmmM_pi8w1t8rJ3P0xDc7pHs_st9k-9Q,684
|
|
84
84
|
tinybird/tb/modules/agent/tools/explore.py,sha256=ihALc_kBcsjrKT3hZyicqyIowB0g_K3AtNNi-5uz9-8,412
|
|
85
|
-
tinybird/tb/modules/agent/tools/
|
|
86
|
-
tinybird/tb/modules/agent/tools/
|
|
85
|
+
tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=_3wAvDykJitIOb5BRnP7wCy6y06y1qlULHLWB-MvS2M,1705
|
|
86
|
+
tinybird/tb/modules/agent/tools/mock.py,sha256=1DeHv14M1dzzKV78kKte9xWQg7RUdQIdXH8KilnQPP0,2304
|
|
87
|
+
tinybird/tb/modules/agent/tools/plan.py,sha256=pr6LnItz6vlOeCG8GE459ExsrBEG0KLx-g02SZGNjXU,1217
|
|
87
88
|
tinybird/tb/modules/agent/tools/preview_datafile.py,sha256=e9q5fR0afApcrntzFrnuHmd10ex7MG_GM6T0Pwc9bRI,850
|
|
88
|
-
tinybird/tb/modules/agent/tools/read_fixture_data.py,sha256=
|
|
89
|
+
tinybird/tb/modules/agent/tools/read_fixture_data.py,sha256=rvTdVlZsu3rQTSWqXzpFt4LEwnBcMLIT8hlI5C7MVN4,1430
|
|
89
90
|
tinybird/tb/modules/datafile/build.py,sha256=NFKBrusFLU0WJNCXePAFWiEDuTaXpwc0lHlOQWEJ43s,51117
|
|
90
91
|
tinybird/tb/modules/datafile/build_common.py,sha256=2yNdxe49IMA9wNvl25NemY2Iaz8L66snjOdT64dm1is,4511
|
|
91
92
|
tinybird/tb/modules/datafile/build_datasource.py,sha256=Ra8pVQBDafbFRUKlhpgohhTsRyp_ADKZJVG8Gd69idY,17227
|
|
@@ -106,8 +107,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
|
|
|
106
107
|
tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
|
|
107
108
|
tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
108
109
|
tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
|
|
109
|
-
tinybird-0.0.1.
|
|
110
|
-
tinybird-0.0.1.
|
|
111
|
-
tinybird-0.0.1.
|
|
112
|
-
tinybird-0.0.1.
|
|
113
|
-
tinybird-0.0.1.
|
|
110
|
+
tinybird-0.0.1.dev250.dist-info/METADATA,sha256=5urJcqt4JiSsBmETS17ZeDZ20R3QVzATLCXLNuuIiSQ,1733
|
|
111
|
+
tinybird-0.0.1.dev250.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
112
|
+
tinybird-0.0.1.dev250.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
113
|
+
tinybird-0.0.1.dev250.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
|
|
114
|
+
tinybird-0.0.1.dev250.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|