tinybird 0.0.1.dev42__py3-none-any.whl → 0.0.1.dev44__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/client.py +1 -1
- tinybird/connectors.py +3 -3
- tinybird/feedback_manager.py +1 -1
- tinybird/prompts.py +31 -3
- tinybird/sql.py +1 -1
- tinybird/sql_template_fmt.py +1 -1
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/cli.py +1 -1
- tinybird/tb/modules/build.py +39 -21
- tinybird/tb/modules/cicd.py +2 -2
- tinybird/tb/modules/cli.py +8 -61
- tinybird/tb/modules/common.py +2 -1
- tinybird/tb/modules/copy.py +96 -5
- tinybird/tb/modules/create.py +105 -46
- tinybird/tb/modules/datafile/build.py +64 -247
- tinybird/tb/modules/datasource.py +1 -1
- tinybird/tb/modules/deployment.py +86 -61
- tinybird/tb/modules/endpoint.py +90 -3
- tinybird/tb/modules/llm_utils.py +2 -2
- tinybird/tb/modules/materialization.py +146 -0
- tinybird/tb/modules/mock.py +56 -16
- tinybird/tb/modules/pipe.py +2 -411
- tinybird/tb/modules/project.py +31 -1
- tinybird/tb/modules/test.py +72 -37
- tinybird/tb/modules/update.py +1 -1
- tinybird/tb/modules/watch.py +54 -5
- tinybird/tb_cli_modules/common.py +1 -1
- tinybird/tornado_template.py +2 -2
- {tinybird-0.0.1.dev42.dist-info → tinybird-0.0.1.dev44.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev42.dist-info → tinybird-0.0.1.dev44.dist-info}/RECORD +33 -33
- tinybird/tb/modules/build_client.py +0 -199
- {tinybird-0.0.1.dev42.dist-info → tinybird-0.0.1.dev44.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev42.dist-info → tinybird-0.0.1.dev44.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev42.dist-info → tinybird-0.0.1.dev44.dist-info}/top_level.txt +0 -0
tinybird/tb/modules/create.py
CHANGED
|
@@ -18,6 +18,7 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
|
18
18
|
from tinybird.tb.modules.llm import LLM
|
|
19
19
|
from tinybird.tb.modules.llm_utils import extract_xml, parse_xml
|
|
20
20
|
from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
21
|
+
from tinybird.tb.modules.project import Project
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@cli.command()
|
|
@@ -41,8 +42,10 @@ from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
|
41
42
|
)
|
|
42
43
|
@click.option("--rows", type=int, default=10, help="Number of events to send")
|
|
43
44
|
@click.option("--cursor", default=False, is_flag=True, help="Create .cursorrules file with Tinybird rules")
|
|
45
|
+
@click.pass_context
|
|
44
46
|
@coro
|
|
45
47
|
async def create(
|
|
48
|
+
ctx: click.Context,
|
|
46
49
|
data: Optional[str],
|
|
47
50
|
prompt: Optional[str],
|
|
48
51
|
folder: Optional[str],
|
|
@@ -50,13 +53,14 @@ async def create(
|
|
|
50
53
|
cursor: bool,
|
|
51
54
|
) -> None:
|
|
52
55
|
"""Initialize a new project."""
|
|
56
|
+
project: Project = ctx.ensure_object(dict)["project"]
|
|
53
57
|
folder = folder or getcwd()
|
|
54
58
|
folder_path = Path(folder)
|
|
55
59
|
if not folder_path.exists():
|
|
56
60
|
folder_path.mkdir()
|
|
57
61
|
|
|
58
62
|
try:
|
|
59
|
-
config = CLIConfig.get_project_config(
|
|
63
|
+
config = CLIConfig.get_project_config(str(project.path))
|
|
60
64
|
tb_client = config.get_client()
|
|
61
65
|
user_token: Optional[str] = None
|
|
62
66
|
if prompt:
|
|
@@ -80,7 +84,7 @@ async def create(
|
|
|
80
84
|
click.echo(FeedbackManager.success(message="✓ Scaffolding completed!\n"))
|
|
81
85
|
|
|
82
86
|
click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
|
|
83
|
-
|
|
87
|
+
result = await create_resources(local_client, tb_client, user_token, data, prompt, folder)
|
|
84
88
|
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
85
89
|
|
|
86
90
|
if not already_has_cicd(folder):
|
|
@@ -89,7 +93,7 @@ async def create(
|
|
|
89
93
|
await init_cicd(data_project_dir=os.path.relpath(folder))
|
|
90
94
|
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
91
95
|
|
|
92
|
-
if
|
|
96
|
+
if should_generate_fixtures(result):
|
|
93
97
|
click.echo(FeedbackManager.highlight(message="\n» Generating fixtures..."))
|
|
94
98
|
|
|
95
99
|
if data:
|
|
@@ -110,7 +114,7 @@ async def create(
|
|
|
110
114
|
datasource_content = datasource_path.read_text()
|
|
111
115
|
has_json_path = "`json:" in datasource_content
|
|
112
116
|
if has_json_path:
|
|
113
|
-
prompt = f"<datasource_schema>{datasource_content}</datasource_schema
|
|
117
|
+
prompt = f"<datasource_schema>{datasource_content}</datasource_schema>"
|
|
114
118
|
response = llm.ask(system_prompt=mock_prompt(rows), prompt=prompt)
|
|
115
119
|
sql = extract_xml(response, "sql")
|
|
116
120
|
sql = sql.split("FORMAT")[0]
|
|
@@ -139,9 +143,10 @@ def validate_project_structure(folder: str) -> bool:
|
|
|
139
143
|
return all((Path(folder) / path).exists() for path in PROJECT_PATHS)
|
|
140
144
|
|
|
141
145
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
def should_generate_fixtures(result: str) -> bool:
|
|
147
|
+
if not result:
|
|
148
|
+
return False
|
|
149
|
+
return "<type>datasource</type>" in result
|
|
145
150
|
|
|
146
151
|
|
|
147
152
|
def already_has_cicd(folder: str) -> bool:
|
|
@@ -168,12 +173,12 @@ async def create_resources(
|
|
|
168
173
|
prompt: Optional[str],
|
|
169
174
|
folder: str,
|
|
170
175
|
):
|
|
171
|
-
|
|
176
|
+
result = ""
|
|
172
177
|
folder_path = Path(folder)
|
|
173
178
|
if data:
|
|
174
179
|
path = folder_path / data
|
|
175
180
|
format = path.suffix.lstrip(".")
|
|
176
|
-
await _generate_datafile(str(path), local_client, format=format, force=
|
|
181
|
+
await _generate_datafile(str(path), local_client, format=format, force=True)
|
|
177
182
|
name = data.split(".")[0]
|
|
178
183
|
generate_pipe_file(
|
|
179
184
|
f"{name}_endpoint",
|
|
@@ -185,7 +190,10 @@ TYPE ENDPOINT
|
|
|
185
190
|
""",
|
|
186
191
|
folder,
|
|
187
192
|
)
|
|
188
|
-
|
|
193
|
+
result = (
|
|
194
|
+
f"<response><resource><type>datasource</type><name>{name}</name><content></content></resource></response>"
|
|
195
|
+
)
|
|
196
|
+
|
|
189
197
|
elif prompt and user_token:
|
|
190
198
|
datasource_paths = [
|
|
191
199
|
Path(folder) / "datasources" / f
|
|
@@ -212,41 +220,74 @@ TYPE ENDPOINT
|
|
|
212
220
|
]
|
|
213
221
|
)
|
|
214
222
|
llm = LLM(user_token=user_token, host=tb_client.host)
|
|
215
|
-
result =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
223
|
+
result = ""
|
|
224
|
+
iterations = 0
|
|
225
|
+
history = ""
|
|
226
|
+
generated_paths: list[Path] = []
|
|
227
|
+
|
|
228
|
+
while iterations < 10:
|
|
229
|
+
feedback = ""
|
|
230
|
+
if iterations > 0:
|
|
231
|
+
feedback = click.prompt("\nFollow-up instructions or continue", default="continue")
|
|
232
|
+
if iterations > 0 and (not feedback or feedback in ("continue", "ok", "exit", "quit", "q")):
|
|
233
|
+
break
|
|
234
|
+
else:
|
|
235
|
+
if iterations > 0:
|
|
236
|
+
click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
|
|
237
|
+
for path in generated_paths:
|
|
238
|
+
path.unlink()
|
|
239
|
+
generated_paths = []
|
|
240
|
+
|
|
241
|
+
save_context(prompt, feedback)
|
|
242
|
+
result = llm.ask(system_prompt=create_prompt(resources_xml, feedback, history), prompt=prompt)
|
|
243
|
+
result = extract_xml(result, "response")
|
|
244
|
+
history = (
|
|
245
|
+
history
|
|
246
|
+
+ f"""
|
|
247
|
+
<result_iteration_{iterations}>
|
|
248
|
+
{result}
|
|
249
|
+
</result_iteration_{iterations}>
|
|
250
|
+
"""
|
|
251
|
+
)
|
|
252
|
+
resources = parse_xml(result, "resource")
|
|
253
|
+
datasources = []
|
|
254
|
+
pipes = []
|
|
255
|
+
for resource_xml in resources:
|
|
256
|
+
resource_type = extract_xml(resource_xml, "type")
|
|
257
|
+
name = extract_xml(resource_xml, "name")
|
|
258
|
+
content = extract_xml(resource_xml, "content")
|
|
259
|
+
resource = {
|
|
260
|
+
"name": name,
|
|
261
|
+
"content": content,
|
|
262
|
+
}
|
|
263
|
+
if resource_type.lower() == "datasource":
|
|
264
|
+
datasources.append(resource)
|
|
265
|
+
elif resource_type.lower() == "pipe":
|
|
266
|
+
pipes.append(resource)
|
|
267
|
+
|
|
268
|
+
for ds in datasources:
|
|
269
|
+
content = ds["content"].replace("```", "")
|
|
270
|
+
filename = f"{ds['name']}.datasource"
|
|
271
|
+
datasource_path = generate_datafile(
|
|
272
|
+
content,
|
|
273
|
+
filename=filename,
|
|
274
|
+
data=None,
|
|
275
|
+
_format="ndjson",
|
|
276
|
+
force=True,
|
|
277
|
+
folder=folder,
|
|
278
|
+
)
|
|
279
|
+
generated_paths.append(datasource_path)
|
|
280
|
+
for pipe in pipes:
|
|
281
|
+
content = pipe["content"].replace("```", "")
|
|
282
|
+
pipe_path = generate_pipe_file(pipe["name"], content, folder)
|
|
283
|
+
generated_paths.append(pipe_path)
|
|
284
|
+
|
|
285
|
+
iterations += 1
|
|
286
|
+
|
|
287
|
+
if iterations == 10:
|
|
288
|
+
click.echo(FeedbackManager.info(message="Too many iterations. Change the prompt and try again."))
|
|
289
|
+
|
|
290
|
+
return result
|
|
250
291
|
|
|
251
292
|
|
|
252
293
|
def init_git(folder: str):
|
|
@@ -266,7 +307,7 @@ def init_git(folder: str):
|
|
|
266
307
|
raise CLIException(f"Error initializing Git: {e}")
|
|
267
308
|
|
|
268
309
|
|
|
269
|
-
def generate_pipe_file(name: str, content: str, folder: str):
|
|
310
|
+
def generate_pipe_file(name: str, content: str, folder: str) -> Path:
|
|
270
311
|
def is_copy(content: str) -> bool:
|
|
271
312
|
return re.search(r"TYPE copy", content, re.IGNORECASE) is not None
|
|
272
313
|
|
|
@@ -292,6 +333,7 @@ def generate_pipe_file(name: str, content: str, folder: str):
|
|
|
292
333
|
with open(f"{f}", "w") as file:
|
|
293
334
|
file.write(content)
|
|
294
335
|
click.echo(FeedbackManager.info_file_created(file=f.relative_to(folder)))
|
|
336
|
+
return f.relative_to(folder)
|
|
295
337
|
|
|
296
338
|
|
|
297
339
|
def already_has_cursorrules(folder: str) -> bool:
|
|
@@ -301,3 +343,20 @@ def already_has_cursorrules(folder: str) -> bool:
|
|
|
301
343
|
def create_cursorrules(folder: str):
|
|
302
344
|
cursorrules_file = Path(folder) / ".cursorrules"
|
|
303
345
|
cursorrules_file.write_text(cursorrules_prompt)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def get_context_file() -> Path:
|
|
349
|
+
context_file = Path(os.path.expanduser("~/.tb_create_context"))
|
|
350
|
+
if not context_file.exists():
|
|
351
|
+
context_file.touch()
|
|
352
|
+
return context_file
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def get_context() -> str:
|
|
356
|
+
context_file = get_context_file()
|
|
357
|
+
return context_file.read_text()
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def save_context(prompt: str, feedback: str):
|
|
361
|
+
context_file = get_context_file()
|
|
362
|
+
context_file.write_text(f"- {prompt}\n{feedback}")
|