tinybird 0.0.1.dev34__py3-none-any.whl → 0.0.1.dev36__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/context.py +1 -1
- tinybird/feedback_manager.py +6 -0
- tinybird/prompts.py +6 -0
- tinybird/sql_toolset.py +9 -2
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/cli.py +2 -2
- tinybird/tb/modules/build.py +53 -12
- tinybird/tb/modules/cli.py +7 -94
- tinybird/tb/modules/create.py +4 -4
- tinybird/tb/modules/datafile/build.py +2 -2
- tinybird/tb/modules/datafile/common.py +17 -1
- tinybird/tb/modules/datasource.py +3 -471
- tinybird/tb/modules/{deploy.py → deployment.py} +22 -12
- tinybird/tb/modules/endpoint.py +187 -0
- tinybird/tb/modules/llm.py +10 -16
- tinybird/tb/modules/llm_utils.py +87 -0
- tinybird/tb/modules/local.py +4 -1
- tinybird/tb/modules/local_common.py +2 -2
- tinybird/tb/modules/mock.py +3 -4
- tinybird/tb/modules/pipe.py +1 -254
- tinybird/tb/modules/shell.py +8 -1
- tinybird/tb/modules/test.py +2 -2
- tinybird/tb/modules/update.py +4 -4
- tinybird/tb/modules/watch.py +4 -4
- tinybird/tb/modules/workspace.py +0 -96
- tinybird/tb_cli_modules/common.py +19 -17
- {tinybird-0.0.1.dev34.dist-info → tinybird-0.0.1.dev36.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev34.dist-info → tinybird-0.0.1.dev36.dist-info}/RECORD +31 -31
- tinybird/tb/modules/connection.py +0 -803
- {tinybird-0.0.1.dev34.dist-info → tinybird-0.0.1.dev36.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev34.dist-info → tinybird-0.0.1.dev36.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev34.dist-info → tinybird-0.0.1.dev36.dist-info}/top_level.txt +0 -0
tinybird/context.py
CHANGED
|
@@ -7,7 +7,7 @@ if TYPE_CHECKING:
|
|
|
7
7
|
|
|
8
8
|
workspace_id: ContextVar[str] = ContextVar("workspace_id")
|
|
9
9
|
workspace: ContextVar["User"] = ContextVar("workspace")
|
|
10
|
-
|
|
10
|
+
datasource_id: ContextVar[str] = ContextVar("datasource_id")
|
|
11
11
|
hfi_frequency: ContextVar[float] = ContextVar("hfi_frequency")
|
|
12
12
|
hfi_frequency_gatherer: ContextVar[float] = ContextVar("hfi_frequency_gatherer")
|
|
13
13
|
use_gatherer: ContextVar[bool] = ContextVar("use_gatherer")
|
tinybird/feedback_manager.py
CHANGED
|
@@ -130,6 +130,12 @@ class FeedbackManager:
|
|
|
130
130
|
error_remove_no_endpoint = error_message("Pipe does not have any endpoint")
|
|
131
131
|
error_updating_pipe = error_message("Failed updating pipe {error}")
|
|
132
132
|
error_updating_connector_not_supported = error_message("Changing {param} is not currently supported")
|
|
133
|
+
error_updating_connector_missing_at_least_one_param = error_message(
|
|
134
|
+
"Connection settings not updated. Connection info should have at least one of {param} settings"
|
|
135
|
+
)
|
|
136
|
+
error_updating_connector_missing_params = error_message(
|
|
137
|
+
"Connection settings not updated. Connection info should have {param} settings"
|
|
138
|
+
)
|
|
133
139
|
error_removing_node = error_message("Failed removing node from pipe {pipe}: {error}")
|
|
134
140
|
error_pushing_pipe = error_message("Failed pushing pipe {pipe}: {error}")
|
|
135
141
|
error_creating_endpoint = error_message("Failed creating endpoint in node {node} on pipe {pipe}: {error}")
|
tinybird/prompts.py
CHANGED
|
@@ -641,8 +641,11 @@ The previous instructions are explanations of how things work in Tinybird. Answe
|
|
|
641
641
|
|
|
642
642
|
datasource_instructions = """
|
|
643
643
|
<datasource_file_instructions>
|
|
644
|
+
- Content cannot be empty.
|
|
644
645
|
- The datasource names must be unique.
|
|
645
646
|
- No indentation is allowed for property names: DESCRIPTION, SCHEMA, ENGINE, ENGINE_PARTITION_KEY, ENGINE_SORTING_KEY, etc.
|
|
647
|
+
- Use MergeTree engine by default.
|
|
648
|
+
- Use AggregatingMergeTree engine when the datasource is the target of a materialized pipe.
|
|
646
649
|
</datasource_file_instructions>
|
|
647
650
|
"""
|
|
648
651
|
|
|
@@ -699,6 +702,7 @@ sql_instructions = """
|
|
|
699
702
|
</valid_query_with_parameters_with_%_on_top>
|
|
700
703
|
- The Parameter functions like this one {{{{String(my_param_name,default_value)}}}} can be one of the following: String, DateTime, Date, Float32, Float64, Int, Integer, UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, Int8, Int16, Int32, Int64, Int128, Int256
|
|
701
704
|
- Parameter names must be different from column names. Pass always the param name and a default value to the function.
|
|
705
|
+
- Use ALWAYS hardcoded values for default values for parameters.
|
|
702
706
|
- Code inside the template {{{{template_expression}}}} follows the rules of Tornado templating language so no module is allowed to be imported. So for example you can't use now() as default value for a DateTime parameter. You need an if else block like this:
|
|
703
707
|
<invalid_condition_with_now>
|
|
704
708
|
AND timestamp BETWEEN {{DateTime(start_date, now() - interval 30 day)}} AND {{DateTime(end_date, now())}}
|
|
@@ -732,6 +736,8 @@ sql_instructions = """
|
|
|
732
736
|
- When aliasing a column, use first the column name and then the alias.
|
|
733
737
|
- General functions and aggregate functions are case sensitive.
|
|
734
738
|
- Character insensitive functions are case insensitive.
|
|
739
|
+
- When you use defined function with a paremeter inside, do NOT add quotes around the parameter.
|
|
740
|
+
- Parameters are never quoted in any case.
|
|
735
741
|
</sql_instructions>
|
|
736
742
|
""".format(
|
|
737
743
|
general_functions=general_functions,
|
tinybird/sql_toolset.py
CHANGED
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from functools import lru_cache
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import FrozenSet, List, Optional, Set, Tuple
|
|
7
7
|
|
|
8
8
|
from chtoolset import query as chquery
|
|
9
9
|
from toposort import toposort
|
|
@@ -172,7 +172,7 @@ def tables_or_sql(replacement: dict, table_functions=False) -> set:
|
|
|
172
172
|
return {replacement}
|
|
173
173
|
|
|
174
174
|
|
|
175
|
-
def _separate_as_tuple_if_contains_database_and_table(definition: str) ->
|
|
175
|
+
def _separate_as_tuple_if_contains_database_and_table(definition: str) -> str | Tuple[str, str]:
|
|
176
176
|
if "." in definition:
|
|
177
177
|
database_and_table_separated = definition.split(".")
|
|
178
178
|
return database_and_table_separated[0], database_and_table_separated[1]
|
|
@@ -219,6 +219,7 @@ def replace_tables(
|
|
|
219
219
|
output_one_line: bool = False,
|
|
220
220
|
timestamp: Optional[datetime] = None,
|
|
221
221
|
function_allow_list: Optional[FrozenSet[str]] = None,
|
|
222
|
+
original_replacements: Optional[dict] = None,
|
|
222
223
|
) -> str:
|
|
223
224
|
"""
|
|
224
225
|
Given a query and a list of table replacements, returns the query after applying the table replacements.
|
|
@@ -239,6 +240,12 @@ def replace_tables(
|
|
|
239
240
|
_replacements[rk] = r if isinstance(r, tuple) else (default_database, r)
|
|
240
241
|
_replaced_with.add(r)
|
|
241
242
|
|
|
243
|
+
if original_replacements:
|
|
244
|
+
# Some replacements have been expanded by filters and turned to a query str, but we need to send the original
|
|
245
|
+
# ones to is_invalid_resource()
|
|
246
|
+
for r in original_replacements.values():
|
|
247
|
+
_replaced_with.add(r)
|
|
248
|
+
|
|
242
249
|
deps: defaultdict = defaultdict(set)
|
|
243
250
|
_tables = sql_get_used_tables(
|
|
244
251
|
sql,
|
tinybird/tb/__cli__.py
CHANGED
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '0.0.1.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev36'
|
|
8
|
+
__revision__ = '5ad3ab4'
|
tinybird/tb/cli.py
CHANGED
|
@@ -9,10 +9,10 @@ import tinybird.tb.modules.build
|
|
|
9
9
|
import tinybird.tb.modules.build_client
|
|
10
10
|
import tinybird.tb.modules.cli
|
|
11
11
|
import tinybird.tb.modules.common
|
|
12
|
-
import tinybird.tb.modules.connection
|
|
13
12
|
import tinybird.tb.modules.create
|
|
14
13
|
import tinybird.tb.modules.datasource
|
|
15
|
-
import tinybird.tb.modules.
|
|
14
|
+
import tinybird.tb.modules.deployment
|
|
15
|
+
import tinybird.tb.modules.endpoint
|
|
16
16
|
import tinybird.tb.modules.fmt
|
|
17
17
|
import tinybird.tb.modules.job
|
|
18
18
|
import tinybird.tb.modules.local
|
tinybird/tb/modules/build.py
CHANGED
|
@@ -2,7 +2,9 @@ import asyncio
|
|
|
2
2
|
import glob
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
import os
|
|
5
6
|
import threading
|
|
7
|
+
import time
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import List
|
|
8
10
|
|
|
@@ -12,6 +14,7 @@ import requests
|
|
|
12
14
|
from tinybird.client import TinyB
|
|
13
15
|
from tinybird.tb.modules.cli import cli
|
|
14
16
|
from tinybird.tb.modules.common import push_data
|
|
17
|
+
from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
|
|
15
18
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
16
19
|
from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
17
20
|
from tinybird.tb.modules.shell import Shell
|
|
@@ -19,7 +22,7 @@ from tinybird.tb.modules.watch import watch_project
|
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
@cli.command()
|
|
22
|
-
@click.option("--folder", type=str, default=
|
|
25
|
+
@click.option("--folder", type=str, default=os.getcwd())
|
|
23
26
|
@click.option("--watch", is_flag=True, default=False, help="Watch for changes and rebuild automatically")
|
|
24
27
|
def build(folder: str, watch: bool) -> None:
|
|
25
28
|
"""
|
|
@@ -27,12 +30,19 @@ def build(folder: str, watch: bool) -> None:
|
|
|
27
30
|
"""
|
|
28
31
|
|
|
29
32
|
tb_client = asyncio.run(get_tinybird_local_client(folder))
|
|
33
|
+
click.echo(FeedbackManager.highlight(message="\n» Building project..."))
|
|
34
|
+
|
|
35
|
+
time_start = time.time()
|
|
30
36
|
|
|
31
37
|
def process() -> None:
|
|
32
38
|
build_project(folder, tb_client)
|
|
33
39
|
|
|
34
40
|
process()
|
|
35
41
|
|
|
42
|
+
time_end = time.time()
|
|
43
|
+
elapsed_time = time_end - time_start
|
|
44
|
+
click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
|
|
45
|
+
|
|
36
46
|
if watch:
|
|
37
47
|
shell = Shell(folder=folder, client=tb_client)
|
|
38
48
|
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
@@ -71,6 +81,7 @@ def build_project(folder: str, tb_client: TinyB) -> None:
|
|
|
71
81
|
fds = []
|
|
72
82
|
project_path = Path(folder)
|
|
73
83
|
project_files = get_project_files(project_path)
|
|
84
|
+
|
|
74
85
|
for file_path in project_files:
|
|
75
86
|
relative_path = str(Path(file_path).relative_to(project_path))
|
|
76
87
|
fd = open(file_path, "rb")
|
|
@@ -91,7 +102,34 @@ def build_project(folder: str, tb_client: TinyB) -> None:
|
|
|
91
102
|
|
|
92
103
|
build_result = result.get("result")
|
|
93
104
|
if build_result == "success":
|
|
94
|
-
|
|
105
|
+
datasources = result.get("datasources", [])
|
|
106
|
+
pipes = result.get("pipes", [])
|
|
107
|
+
for ds in datasources:
|
|
108
|
+
ds_path = next((p for p in project_files if p.endswith(ds.get("name") + ".datasource")), None)
|
|
109
|
+
if ds_path:
|
|
110
|
+
ds_path = ds_path.replace(f"{folder}/", "")
|
|
111
|
+
click.echo(FeedbackManager.info(message=f"✓ {ds_path} created"))
|
|
112
|
+
for pipe in pipes:
|
|
113
|
+
pipe_name = pipe.get("name")
|
|
114
|
+
pipe_path = next((p for p in project_files if p.endswith(pipe_name + ".pipe")), None)
|
|
115
|
+
if pipe_path:
|
|
116
|
+
pipe_path = pipe_path.replace(f"{folder}/", "")
|
|
117
|
+
click.echo(FeedbackManager.info(message=f"✓ {pipe_path} created"))
|
|
118
|
+
|
|
119
|
+
for filename in project_files:
|
|
120
|
+
if filename.endswith(".datasource"):
|
|
121
|
+
ds_path = Path(filename)
|
|
122
|
+
ds_name = ds_path.stem
|
|
123
|
+
name = build_fixture_name(filename, ds_name, ds_path.read_text())
|
|
124
|
+
fixture_folder = get_fixture_dir(folder)
|
|
125
|
+
fixture_path = fixture_folder / f"{name}.ndjson"
|
|
126
|
+
|
|
127
|
+
if not fixture_path.exists():
|
|
128
|
+
fixture_path = fixture_folder / f"{ds_name}.ndjson"
|
|
129
|
+
|
|
130
|
+
if fixture_path.exists():
|
|
131
|
+
append_fixture(tb_client, ds_name, str(fixture_path))
|
|
132
|
+
|
|
95
133
|
elif build_result == "failed":
|
|
96
134
|
click.echo(FeedbackManager.error(message="Build failed"))
|
|
97
135
|
build_errors = result.get("errors")
|
|
@@ -101,24 +139,27 @@ def build_project(folder: str, tb_client: TinyB) -> None:
|
|
|
101
139
|
click.echo(FeedbackManager.error(message=error_msg))
|
|
102
140
|
else:
|
|
103
141
|
click.echo(FeedbackManager.error(message=f"Unknown build result. Error: {result.get('error')}"))
|
|
142
|
+
|
|
104
143
|
except Exception as e:
|
|
105
|
-
click.echo(FeedbackManager.error_exception(error="Error
|
|
144
|
+
click.echo(FeedbackManager.error_exception(error="Error: " + str(e)))
|
|
106
145
|
finally:
|
|
107
146
|
for fd in fds:
|
|
108
147
|
fd.close()
|
|
109
148
|
|
|
110
149
|
|
|
111
|
-
|
|
150
|
+
def append_fixture(
|
|
112
151
|
tb_client: TinyB,
|
|
113
152
|
datasource_name: str,
|
|
114
153
|
url: str,
|
|
115
154
|
):
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
asyncio.run(tb_client.datasource_truncate(datasource_name))
|
|
156
|
+
asyncio.run(
|
|
157
|
+
push_data(
|
|
158
|
+
tb_client,
|
|
159
|
+
datasource_name,
|
|
160
|
+
url,
|
|
161
|
+
mode="append",
|
|
162
|
+
concurrency=1,
|
|
163
|
+
silent=True,
|
|
164
|
+
)
|
|
124
165
|
)
|
tinybird/tb/modules/cli.py
CHANGED
|
@@ -67,58 +67,13 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
|
|
|
67
67
|
)
|
|
68
68
|
@click.option("--token", help="Use auth token, defaults to TB_TOKEN envvar, then to the .tinyb file")
|
|
69
69
|
@click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
|
|
70
|
-
@click.option("--gcp-project-id", help="The Google Cloud project ID", hidden=True)
|
|
71
|
-
@click.option(
|
|
72
|
-
"--gcs-bucket", help="The Google Cloud Storage bucket to write temp files when using the connectors", hidden=True
|
|
73
|
-
)
|
|
74
|
-
@click.option(
|
|
75
|
-
"--google-application-credentials",
|
|
76
|
-
envvar="GOOGLE_APPLICATION_CREDENTIALS",
|
|
77
|
-
help="Set GOOGLE_APPLICATION_CREDENTIALS",
|
|
78
|
-
hidden=True,
|
|
79
|
-
)
|
|
80
|
-
@click.option("--sf-account", help="The Snowflake Account (e.g. your-domain.west-europe.azure)", hidden=True)
|
|
81
|
-
@click.option("--sf-warehouse", help="The Snowflake warehouse name", hidden=True)
|
|
82
|
-
@click.option("--sf-database", help="The Snowflake database name", hidden=True)
|
|
83
|
-
@click.option("--sf-schema", help="The Snowflake schema name", hidden=True)
|
|
84
|
-
@click.option("--sf-role", help="The Snowflake role name", hidden=True)
|
|
85
|
-
@click.option("--sf-user", help="The Snowflake user name", hidden=True)
|
|
86
|
-
@click.option("--sf-password", help="The Snowflake password", hidden=True)
|
|
87
|
-
@click.option(
|
|
88
|
-
"--sf-storage-integration",
|
|
89
|
-
help="The Snowflake GCS storage integration name (leave empty to auto-generate one)",
|
|
90
|
-
hidden=True,
|
|
91
|
-
)
|
|
92
|
-
@click.option("--sf-stage", help="The Snowflake GCS stage name (leave empty to auto-generate one)", hidden=True)
|
|
93
|
-
@click.option(
|
|
94
|
-
"--with-headers", help="Flag to enable connector to export with headers", is_flag=True, default=False, hidden=True
|
|
95
|
-
)
|
|
96
70
|
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
|
|
97
|
-
@click.option("--
|
|
71
|
+
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
|
|
72
|
+
@click.option("--prod", is_flag=True, default=False, help="Run against production")
|
|
98
73
|
@click.version_option(version=VERSION)
|
|
99
74
|
@click.pass_context
|
|
100
75
|
@coro
|
|
101
|
-
async def cli(
|
|
102
|
-
ctx: Context,
|
|
103
|
-
debug: bool,
|
|
104
|
-
token: str,
|
|
105
|
-
host: str,
|
|
106
|
-
gcp_project_id: str,
|
|
107
|
-
gcs_bucket: str,
|
|
108
|
-
google_application_credentials: str,
|
|
109
|
-
sf_account: str,
|
|
110
|
-
sf_warehouse: str,
|
|
111
|
-
sf_database: str,
|
|
112
|
-
sf_schema: str,
|
|
113
|
-
sf_role: str,
|
|
114
|
-
sf_user: str,
|
|
115
|
-
sf_password: str,
|
|
116
|
-
sf_storage_integration: str,
|
|
117
|
-
sf_stage,
|
|
118
|
-
with_headers: bool,
|
|
119
|
-
show_tokens: bool,
|
|
120
|
-
prod: bool,
|
|
121
|
-
) -> None:
|
|
76
|
+
async def cli(ctx: Context, debug: bool, token: str, host: str, show_tokens: bool, prod: bool) -> None:
|
|
122
77
|
"""
|
|
123
78
|
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.
|
|
124
79
|
"""
|
|
@@ -166,51 +121,9 @@ async def cli(
|
|
|
166
121
|
|
|
167
122
|
ctx.ensure_object(dict)["config"] = config
|
|
168
123
|
|
|
169
|
-
if ctx.invoked_subcommand == "auth":
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
from tinybird.connectors import create_connector
|
|
173
|
-
|
|
174
|
-
if gcp_project_id and gcs_bucket and google_application_credentials and not sf_account:
|
|
175
|
-
bq_config = {
|
|
176
|
-
"project_id": gcp_project_id,
|
|
177
|
-
"bucket_name": gcs_bucket,
|
|
178
|
-
"service_account": google_application_credentials,
|
|
179
|
-
"with_headers": with_headers,
|
|
180
|
-
}
|
|
181
|
-
ctx.ensure_object(dict)["bigquery"] = create_connector("bigquery", bq_config)
|
|
182
|
-
if (
|
|
183
|
-
sf_account
|
|
184
|
-
and sf_warehouse
|
|
185
|
-
and sf_database
|
|
186
|
-
and sf_schema
|
|
187
|
-
and sf_role
|
|
188
|
-
and sf_user
|
|
189
|
-
and sf_password
|
|
190
|
-
and gcs_bucket
|
|
191
|
-
and google_application_credentials
|
|
192
|
-
and gcp_project_id
|
|
193
|
-
):
|
|
194
|
-
sf_config = {
|
|
195
|
-
"account": sf_account,
|
|
196
|
-
"warehouse": sf_warehouse,
|
|
197
|
-
"database": sf_database,
|
|
198
|
-
"schema": sf_schema,
|
|
199
|
-
"role": sf_role,
|
|
200
|
-
"user": sf_user,
|
|
201
|
-
"password": sf_password,
|
|
202
|
-
"storage_integration": sf_storage_integration,
|
|
203
|
-
"stage": sf_stage,
|
|
204
|
-
"bucket_name": gcs_bucket,
|
|
205
|
-
"service_account": google_application_credentials,
|
|
206
|
-
"project_id": gcp_project_id,
|
|
207
|
-
"with_headers": with_headers,
|
|
208
|
-
}
|
|
209
|
-
ctx.ensure_object(dict)["snowflake"] = create_connector("snowflake", sf_config)
|
|
210
|
-
|
|
211
124
|
logging.debug("debug enabled")
|
|
212
125
|
|
|
213
|
-
skip_client = ctx.invoked_subcommand in ["login", "workspace", "local"]
|
|
126
|
+
skip_client = ctx.invoked_subcommand in ["auth", "login", "workspace", "local", "build"]
|
|
214
127
|
client = await create_ctx_client(config, prod, skip_client)
|
|
215
128
|
|
|
216
129
|
if client:
|
|
@@ -487,7 +400,7 @@ async def sql(
|
|
|
487
400
|
|
|
488
401
|
@cli.command(hidden=True)
|
|
489
402
|
@click.argument("prompt")
|
|
490
|
-
@click.option("--folder", default=
|
|
403
|
+
@click.option("--folder", default=os.getcwd(), help="The folder to use for the project")
|
|
491
404
|
@coro
|
|
492
405
|
async def ask(prompt: str, folder: str) -> None:
|
|
493
406
|
"""Ask things about your data project."""
|
|
@@ -529,8 +442,8 @@ async def ask(prompt: str, folder: str) -> None:
|
|
|
529
442
|
)
|
|
530
443
|
|
|
531
444
|
client = config.get_client()
|
|
532
|
-
llm = LLM(user_token=user_token,
|
|
533
|
-
click.echo(
|
|
445
|
+
llm = LLM(user_token=user_token, host=client.host)
|
|
446
|
+
click.echo(llm.ask(system_prompt=ask_prompt(resources_xml), prompt=prompt))
|
|
534
447
|
except Exception as e:
|
|
535
448
|
raise CLIException(FeedbackManager.error_exception(error=e))
|
|
536
449
|
|
tinybird/tb/modules/create.py
CHANGED
|
@@ -103,13 +103,13 @@ async def create(
|
|
|
103
103
|
datasource_files = [f for f in os.listdir(Path(folder) / "datasources") if f.endswith(".datasource")]
|
|
104
104
|
for datasource_file in datasource_files:
|
|
105
105
|
datasource_path = Path(folder) / "datasources" / datasource_file
|
|
106
|
-
llm = LLM(user_token=user_token,
|
|
106
|
+
llm = LLM(user_token=user_token, host=tb_client.host)
|
|
107
107
|
datasource_name = datasource_path.stem
|
|
108
108
|
datasource_content = datasource_path.read_text()
|
|
109
109
|
has_json_path = "`json:" in datasource_content
|
|
110
110
|
if has_json_path:
|
|
111
111
|
prompt = f"<datasource_schema>{datasource_content}</datasource_schema>\n<user_input>{prompt}</user_input>"
|
|
112
|
-
response =
|
|
112
|
+
response = llm.ask(system_prompt=mock_prompt(rows), prompt=prompt)
|
|
113
113
|
sql = extract_xml(response, "sql")
|
|
114
114
|
sql = sql.split("FORMAT")[0]
|
|
115
115
|
result = await local_client.query(f"{sql} FORMAT JSON")
|
|
@@ -205,8 +205,8 @@ TYPE ENDPOINT
|
|
|
205
205
|
]
|
|
206
206
|
]
|
|
207
207
|
)
|
|
208
|
-
llm = LLM(user_token=user_token,
|
|
209
|
-
result =
|
|
208
|
+
llm = LLM(user_token=user_token, host=tb_client.host)
|
|
209
|
+
result = llm.ask(system_prompt=create_prompt(resources_xml), prompt=prompt)
|
|
210
210
|
result = extract_xml(result, "response")
|
|
211
211
|
resources = parse_xml(result, "resource")
|
|
212
212
|
datasources = []
|
|
@@ -44,7 +44,7 @@ from tinybird.tb.modules.datafile.exceptions import AlreadyExistsException, Incl
|
|
|
44
44
|
from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
|
|
45
45
|
from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
|
|
46
46
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
47
|
-
from tinybird.tb.modules.local_common import
|
|
47
|
+
from tinybird.tb.modules.local_common import get_tinybird_local_config
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
async def folder_build(
|
|
@@ -58,7 +58,7 @@ async def folder_build(
|
|
|
58
58
|
local_ws: Optional[Dict[str, Any]] = None,
|
|
59
59
|
watch: bool = False,
|
|
60
60
|
):
|
|
61
|
-
config = await
|
|
61
|
+
config = await get_tinybird_local_config(folder)
|
|
62
62
|
build = True
|
|
63
63
|
dry_run = False
|
|
64
64
|
force = True
|
|
@@ -204,6 +204,10 @@ class Datafile:
|
|
|
204
204
|
if self.kind == DatafileKind.pipe:
|
|
205
205
|
# TODO(eclbg):
|
|
206
206
|
# [x] node names are unique
|
|
207
|
+
# [x] SQL in all nodes
|
|
208
|
+
# [x] Materialized nodes have target datasource
|
|
209
|
+
# [x] Only one materialized node
|
|
210
|
+
# [ ] Only one node of any specific type
|
|
207
211
|
# [ ] ...
|
|
208
212
|
repeated_node_names = [
|
|
209
213
|
name for name, count in filter(lambda x: x[1] > 1, Counter(n["name"] for n in self.nodes).items())
|
|
@@ -212,7 +216,19 @@ class Datafile:
|
|
|
212
216
|
raise DatafileValidationError(
|
|
213
217
|
f"Pipe node names must be unique. These names are repeated: {repeated_node_names}"
|
|
214
218
|
)
|
|
215
|
-
|
|
219
|
+
for node in self.nodes:
|
|
220
|
+
if "sql" not in node:
|
|
221
|
+
raise DatafileValidationError(f"SQL missing for node {repr(node['name'])}")
|
|
222
|
+
materialized_nodes_count = 0
|
|
223
|
+
for node in self.nodes:
|
|
224
|
+
if node.get("type", "").lower() == "materialized":
|
|
225
|
+
materialized_nodes_count += 1
|
|
226
|
+
if materialized_nodes_count > 1:
|
|
227
|
+
raise DatafileValidationError("Multiple materialized nodes in pipe. There can only be one")
|
|
228
|
+
if "datasource" not in node:
|
|
229
|
+
raise DatafileValidationError(
|
|
230
|
+
f"Materialized node {repr(node['name'])} missing target datasource"
|
|
231
|
+
)
|
|
216
232
|
elif self.kind == DatafileKind.datasource:
|
|
217
233
|
# TODO(eclbg):
|
|
218
234
|
# [x] Just one node
|