tinybird 0.0.1.dev245__tar.gz → 0.0.1.dev247__tar.gz

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

Potentially problematic release.


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

Files changed (135) hide show
  1. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/ch_utils/constants.py +2 -0
  3. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/__cli__.py +2 -2
  4. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/agent.py +136 -27
  5. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/models.py +6 -0
  6. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/prompts.py +71 -43
  7. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/append.py +55 -0
  8. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/build.py +20 -0
  9. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/create_datafile.py +145 -0
  10. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/deploy.py +45 -0
  11. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/deploy_check.py +19 -0
  12. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/mock.py +59 -0
  13. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/tools/plan.py +1 -1
  14. tinybird-0.0.1.dev247/tinybird/tb/modules/agent/tools/read_fixture_data.py +28 -0
  15. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/utils.py +9 -2
  16. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/build.py +4 -1
  17. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/build_common.py +2 -3
  18. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/cli.py +9 -1
  19. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/create.py +1 -1
  20. tinybird-0.0.1.dev247/tinybird/tb/modules/deployment.py +360 -0
  21. tinybird-0.0.1.dev245/tinybird/tb/modules/deployment.py → tinybird-0.0.1.dev247/tinybird/tb/modules/deployment_common.py +5 -324
  22. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/feedback_manager.py +8 -6
  23. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/llm.py +1 -1
  24. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/mock.py +3 -69
  25. tinybird-0.0.1.dev247/tinybird/tb/modules/mock_common.py +71 -0
  26. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/project.py +9 -0
  27. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird.egg-info/PKG-INFO +1 -1
  28. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird.egg-info/SOURCES.txt +8 -0
  29. tinybird-0.0.1.dev245/tinybird/tb/modules/agent/tools/create_datafile.py +0 -66
  30. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/setup.cfg +0 -0
  31. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/__cli__.py +0 -0
  32. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/ch_utils/engine.py +0 -0
  33. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/check_pypi.py +0 -0
  34. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/client.py +0 -0
  35. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/config.py +0 -0
  36. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/connectors.py +0 -0
  37. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/context.py +0 -0
  38. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/datafile/common.py +0 -0
  39. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/datafile/exceptions.py +0 -0
  40. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/datafile/parse_connection.py +0 -0
  41. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/datafile/parse_datasource.py +0 -0
  42. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/datafile/parse_pipe.py +0 -0
  43. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/datatypes.py +0 -0
  44. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/feedback_manager.py +0 -0
  45. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/git_settings.py +0 -0
  46. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/prompts.py +0 -0
  47. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/sql.py +0 -0
  48. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/sql_template.py +0 -0
  49. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/sql_template_fmt.py +0 -0
  50. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/sql_toolset.py +0 -0
  51. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/syncasync.py +0 -0
  52. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/check_pypi.py +0 -0
  53. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/cli.py +0 -0
  54. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/client.py +0 -0
  55. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/config.py +0 -0
  56. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/__init__.py +0 -0
  57. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/animations.py +0 -0
  58. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/banner.py +0 -0
  59. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/memory.py +0 -0
  60. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/tools/__init__.py +0 -0
  61. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/tools/explore.py +0 -0
  62. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/agent/tools/preview_datafile.py +0 -0
  63. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/cicd.py +0 -0
  64. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/common.py +0 -0
  65. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/config.py +0 -0
  66. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/connection.py +0 -0
  67. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/copy.py +0 -0
  68. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/build.py +0 -0
  69. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/build_common.py +0 -0
  70. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  71. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  72. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/diff.py +0 -0
  73. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/fixture.py +0 -0
  74. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/format_common.py +0 -0
  75. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  76. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  77. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  78. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/playground.py +0 -0
  79. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datafile/pull.py +0 -0
  80. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/datasource.py +0 -0
  81. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/deprecations.py +0 -0
  82. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/dev_server.py +0 -0
  83. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/endpoint.py +0 -0
  84. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/exceptions.py +0 -0
  85. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/info.py +0 -0
  86. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/infra.py +0 -0
  87. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/job.py +0 -0
  88. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/llm_utils.py +0 -0
  89. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/local.py +0 -0
  90. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/local_common.py +0 -0
  91. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/login.py +0 -0
  92. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/logout.py +0 -0
  93. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/materialization.py +0 -0
  94. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/open.py +0 -0
  95. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/pipe.py +0 -0
  96. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/regions.py +0 -0
  97. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/secret.py +0 -0
  98. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/secret_common.py +0 -0
  99. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/shell.py +0 -0
  100. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/sink.py +0 -0
  101. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/table.py +0 -0
  102. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/telemetry.py +0 -0
  103. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/test.py +0 -0
  104. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  105. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  106. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/token.py +0 -0
  107. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/watch.py +0 -0
  108. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/workspace.py +0 -0
  109. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb/modules/workspace_members.py +0 -0
  110. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli.py +0 -0
  111. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/auth.py +0 -0
  112. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/branch.py +0 -0
  113. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/cicd.py +0 -0
  114. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/cli.py +0 -0
  115. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/common.py +0 -0
  116. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/config.py +0 -0
  117. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/connection.py +0 -0
  118. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/datasource.py +0 -0
  119. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/exceptions.py +0 -0
  120. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/fmt.py +0 -0
  121. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/job.py +0 -0
  122. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/pipe.py +0 -0
  123. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/regions.py +0 -0
  124. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/tag.py +0 -0
  125. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/telemetry.py +0 -0
  126. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/test.py +0 -0
  127. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  128. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  129. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/workspace.py +0 -0
  130. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  131. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird/tornado_template.py +0 -0
  132. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird.egg-info/dependency_links.txt +0 -0
  133. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird.egg-info/entry_points.txt +0 -0
  134. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird.egg-info/requires.txt +0 -0
  135. {tinybird-0.0.1.dev245 → tinybird-0.0.1.dev247}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev245
3
+ Version: 0.0.1.dev247
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -255,4 +255,6 @@ VALID_QUERY_FORMATS = (
255
255
  "JSONStrings",
256
256
  "Prometheus",
257
257
  "Native",
258
+ "RowBinaryWithNamesAndTypes",
259
+ "TabSeparated",
258
260
  )
@@ -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.dev245'
8
- __revision__ = 'f0c439a'
7
+ __version__ = '0.0.1.dev247'
8
+ __revision__ = '379a827'
@@ -1,7 +1,8 @@
1
+ import subprocess
1
2
  import sys
2
- import uuid
3
3
  from datetime import datetime
4
4
  from functools import partial
5
+ from pathlib import Path
5
6
  from typing import Any
6
7
 
7
8
  import click
@@ -28,29 +29,40 @@ from tinybird.tb.client import TinyB
28
29
  from tinybird.tb.modules.agent.animations import ThinkingAnimation
29
30
  from tinybird.tb.modules.agent.banner import display_banner
30
31
  from tinybird.tb.modules.agent.memory import clear_history, load_history
31
- from tinybird.tb.modules.agent.models import create_model
32
+ from tinybird.tb.modules.agent.models import create_model, model_costs
32
33
  from tinybird.tb.modules.agent.prompts import (
33
34
  datafile_instructions,
34
35
  plan_instructions,
35
36
  resources_prompt,
36
37
  sql_instructions,
37
38
  )
39
+ from tinybird.tb.modules.agent.tools.append import append
40
+ from tinybird.tb.modules.agent.tools.build import build
38
41
  from tinybird.tb.modules.agent.tools.create_datafile import create_datafile
42
+ from tinybird.tb.modules.agent.tools.deploy import deploy
43
+ from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
39
44
  from tinybird.tb.modules.agent.tools.explore import explore_data
45
+ from tinybird.tb.modules.agent.tools.mock import mock
40
46
  from tinybird.tb.modules.agent.tools.plan import plan
41
47
  from tinybird.tb.modules.agent.tools.preview_datafile import preview_datafile
48
+ from tinybird.tb.modules.agent.tools.read_fixture_data import read_fixture_data
42
49
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext
43
50
  from tinybird.tb.modules.build_common import process as build_process
44
- from tinybird.tb.modules.exceptions import CLIBuildException
51
+ from tinybird.tb.modules.common import _analyze, _get_tb_client
52
+ from tinybird.tb.modules.config import CLIConfig
53
+ from tinybird.tb.modules.deployment_common import create_deployment
54
+ from tinybird.tb.modules.exceptions import CLIBuildException, CLIMockException
45
55
  from tinybird.tb.modules.feedback_manager import FeedbackManager
46
56
  from tinybird.tb.modules.local_common import get_tinybird_local_client
57
+ from tinybird.tb.modules.mock_common import append_mock_data, create_mock_data
47
58
  from tinybird.tb.modules.project import Project
48
59
 
49
60
 
50
61
  class TinybirdAgent:
51
- def __init__(self, token: str, host: str, project: Project):
62
+ def __init__(self, token: str, host: str, project: Project, dangerously_skip_permissions: bool):
52
63
  self.token = token
53
64
  self.host = host
65
+ self.dangerously_skip_permissions = dangerously_skip_permissions
54
66
  self.project = project
55
67
  self.messages: list[ModelMessage] = []
56
68
  self.agent = Agent(
@@ -86,7 +98,12 @@ You have access to the following tools:
86
98
  2. `preview_datafile` - Preview the content of a datafile (datasource, endpoint, materialized, sink, copy, connection).
87
99
  3. `create_datafile` - Create a file in the project folder. Confirmation will be asked by the tool before creating the file.
88
100
  4. `plan` - Plan the creation or update of resources.
89
-
101
+ 5. `build` - Build the project.
102
+ 6. `deploy` - Deploy the project to Tinybird Cloud.
103
+ 7. `deploy_check` - Check if the project can be deployed to Tinybird Cloud before deploying it.
104
+ 8. `mock` - Create mock data for a landing datasource.
105
+ 9. `read_fixture_data` - Read a fixture data file present in the project folder.
106
+ 10. `append` - Append existing fixture to a datasource.
90
107
 
91
108
  # When creating or updating datafiles:
92
109
  1. Use `plan` tool to plan the creation or update of resources.
@@ -140,6 +157,12 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
140
157
  Tool(preview_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=False),
141
158
  Tool(create_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
142
159
  Tool(plan, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
160
+ Tool(build, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
161
+ Tool(deploy, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
162
+ Tool(deploy_check, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
163
+ Tool(mock, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
164
+ Tool(read_fixture_data, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
165
+ Tool(append, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
143
166
  ],
144
167
  )
145
168
 
@@ -147,44 +170,65 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
147
170
  """Keep only the last 5 messages to manage token usage."""
148
171
  return self.messages[-5:] if len(self.messages) > 5 else self.messages
149
172
 
150
- def run(self, user_prompt: str, project: Project) -> None:
151
- user_prompt = f"{user_prompt}\n\n# Existing resources in the project:\n{resources_prompt(project)}"
173
+ def run(self, user_prompt: str, config: dict[str, Any], project: Project) -> None:
174
+ user_prompt = f"{user_prompt}\n\n{resources_prompt(project)}"
152
175
  client = TinyB(token=self.token, host=self.host)
153
176
  folder = self.project.folder
177
+
154
178
  thinking_animation = ThinkingAnimation(message="Chirping", delay=0.15)
155
179
  thinking_animation.start()
156
-
157
180
  result = self.agent.run_sync(
158
181
  user_prompt,
159
182
  deps=TinybirdAgentContext(
160
183
  # context does not support the whole client, so we need to pass only the functions we need
161
184
  explore_data=client.explore_data,
162
- build_project=partial(build_project, folder=folder),
185
+ build_project=partial(build_project, project=project, config=config),
186
+ deploy_project=partial(deploy_project, project=project, config=config),
187
+ deploy_check_project=partial(deploy_check_project, project=project, config=config),
188
+ mock_data=partial(mock_data, project=project, config=config),
189
+ append_data=partial(append_data, config=config),
190
+ analyze_fixture=partial(analyze_fixture, config=config),
163
191
  get_project_files=project.get_project_files,
164
192
  folder=folder,
165
193
  thinking_animation=thinking_animation,
194
+ workspace_name=self.project.workspace_name,
195
+ dangerously_skip_permissions=self.dangerously_skip_permissions,
166
196
  ),
167
197
  message_history=self.messages,
168
198
  )
169
199
  new_messages = result.new_messages()
170
200
  self.messages.extend(new_messages)
171
201
  thinking_animation.stop()
172
-
202
+ usage = result.usage()
203
+ request_tokens = usage.request_tokens or 0
204
+ response_tokens = usage.response_tokens or 0
205
+ total_tokens = usage.total_tokens or 0
206
+ cost = (
207
+ request_tokens * model_costs["input_cost_per_token"]
208
+ + response_tokens * model_costs["output_cost_per_token"]
209
+ )
210
+ click.echo()
173
211
  click.echo(result.output)
174
212
  click.echo("\n")
213
+ click.echo(f"Input tokens: {request_tokens}")
214
+ click.echo(f"Output tokens: {response_tokens}")
215
+ click.echo(f"Total tokens: {total_tokens}")
216
+ click.echo(f"Cost: ${cost:.6f}")
175
217
 
176
218
 
177
- def run_agent(config: dict[str, Any], project: Project):
219
+ def run_agent(config: dict[str, Any], project: Project, dangerously_skip_permissions: bool):
178
220
  display_banner()
179
221
 
180
222
  try:
181
223
  token = config["token"]
182
224
  host = config["host"]
183
- agent = TinybirdAgent(token, host, project)
225
+ agent = TinybirdAgent(token, host, project, dangerously_skip_permissions)
184
226
  click.echo()
185
- click.echo(FeedbackManager.success(message="Welcome to Tinybird Code"))
186
- click.echo(FeedbackManager.info(message="Describe what you want to create and I'll help you build it"))
187
- click.echo(FeedbackManager.info(message="Commands: 'exit', 'quit', 'help', or Ctrl+C to exit"))
227
+ if config.get("token"):
228
+ click.echo(FeedbackManager.info(message="Describe what you want to create and I'll help you build it"))
229
+ click.echo(FeedbackManager.info(message="Run /help for more commands"))
230
+ else:
231
+ click.echo(FeedbackManager.info(message="Run /login to authenticate"))
188
232
  click.echo()
189
233
 
190
234
  except Exception as e:
@@ -207,25 +251,29 @@ def run_agent(config: dict[str, Any], project: Project):
207
251
  ),
208
252
  )
209
253
 
210
- if user_input.lower() in ["exit", "quit"]:
254
+ if user_input.lower() in ["/exit", "/quit"]:
211
255
  click.echo(FeedbackManager.info(message="Goodbye!"))
212
256
  break
213
- elif user_input.lower() == "clear":
257
+ elif user_input.lower() == "/clear":
214
258
  clear_history()
215
259
  continue
216
- elif user_input.lower() == "help":
260
+ elif user_input.lower() == "/login":
261
+ click.echo()
262
+ subprocess.run(["tb", "login"], check=True)
263
+ click.echo()
264
+ continue
265
+ elif user_input.lower() == "/help":
217
266
  click.echo()
218
- click.echo(FeedbackManager.info(message="Tinybird Code Help:"))
219
267
  click.echo("• Describe what you want to create: 'Create a user analytics system'")
220
268
  click.echo("• Ask for specific resources: 'Create a pipe to aggregate daily clicks'")
221
- click.echo("• Request data sources: 'Set up a Kafka connection for events'")
222
- click.echo("• Type 'exit' or 'quit' to leave")
269
+ click.echo("• Connect to external services: 'Set up a Kafka connection for events'")
270
+ click.echo("• Type '/exit' or '/quit' to leave")
223
271
  click.echo()
224
272
  continue
225
273
  elif user_input.strip() == "":
226
274
  continue
227
275
  else:
228
- agent.run(user_input, project)
276
+ agent.run(user_input, config, project)
229
277
 
230
278
  except KeyboardInterrupt:
231
279
  click.echo(FeedbackManager.info(message="Goodbye!"))
@@ -239,10 +287,71 @@ def run_agent(config: dict[str, Any], project: Project):
239
287
  sys.exit(1)
240
288
 
241
289
 
242
- def build_project(folder: str) -> None:
243
- workspace_name = f"tmp_workspace_{uuid.uuid4()}"
244
- project = Project(folder, workspace_name=workspace_name)
245
- local_client = get_tinybird_local_client({"path": folder, "name": workspace_name}, test=True, silent=True)
246
- build_error = build_process(project=project, tb_client=local_client, watch=False, silent=True, exit_on_error=False)
290
+ def build_project(config: dict[str, Any], project: Project, silent: bool = True, test: bool = True) -> None:
291
+ local_client = get_tinybird_local_client(config, test=test, silent=silent)
292
+ build_error = build_process(
293
+ project=project, tb_client=local_client, watch=False, silent=silent, exit_on_error=False
294
+ )
247
295
  if build_error:
248
296
  raise CLIBuildException(build_error)
297
+
298
+
299
+ def deploy_project(config: dict[str, Any], project: Project) -> None:
300
+ client = _get_tb_client(config["token"], config["host"])
301
+ create_deployment(
302
+ project=project,
303
+ client=client,
304
+ config=config,
305
+ wait=True,
306
+ auto=True,
307
+ allow_destructive_operations=False,
308
+ )
309
+
310
+
311
+ def deploy_check_project(config: dict[str, Any], project: Project) -> None:
312
+ client = _get_tb_client(config["token"], config["host"])
313
+ create_deployment(
314
+ project=project,
315
+ client=client,
316
+ config=config,
317
+ check=True,
318
+ wait=True,
319
+ auto=True,
320
+ )
321
+
322
+
323
+ def append_data(config: dict[str, Any], datasource_name: str, path: str) -> None:
324
+ client = get_tinybird_local_client(config, test=False, silent=False)
325
+ append_mock_data(client, datasource_name, path)
326
+
327
+
328
+ def mock_data(
329
+ config: dict[str, Any], project: Project, datasource_name: str, data_format: str, rows: int
330
+ ) -> list[dict[str, Any]]:
331
+ client = get_tinybird_local_client(config, test=False, silent=False)
332
+ cli_config = CLIConfig.get_project_config()
333
+ datasource_path = project.get_resource_path(datasource_name, "datasource")
334
+
335
+ if not datasource_path:
336
+ raise CLIMockException(f"Datasource {datasource_name} not found")
337
+
338
+ datasource_content = Path(datasource_path).read_text()
339
+ prompt = ""
340
+ return create_mock_data(
341
+ datasource_name,
342
+ datasource_content,
343
+ rows,
344
+ prompt,
345
+ cli_config,
346
+ config,
347
+ cli_config.get_user_token() or "",
348
+ client,
349
+ data_format,
350
+ project.folder,
351
+ )
352
+
353
+
354
+ def analyze_fixture(config: dict[str, Any], fixture_path: str) -> dict[str, Any]:
355
+ local_client = get_tinybird_local_client(config, test=False, silent=True)
356
+ meta, _data = _analyze(fixture_path, local_client, Path(fixture_path).suffix.lstrip("."))
357
+ return meta
@@ -18,3 +18,9 @@ def create_model(
18
18
  model_name=model,
19
19
  provider=AnthropicProvider(anthropic_client=client),
20
20
  )
21
+
22
+
23
+ model_costs = {
24
+ "input_cost_per_token": 3e-06,
25
+ "output_cost_per_token": 1.5e-05,
26
+ }
@@ -5,23 +5,29 @@ from tinybird.tb.modules.project import Project
5
5
  plan_instructions = """
6
6
  When asked to create a plan, you MUST respond with this EXACT format and NOTHING ELSE:
7
7
 
8
- PLAN_DESCRIPTION: [One sentence describing what will be built]
9
-
10
- STEPS:
11
- 1. CREATE_DATASOURCE: [name] - [description] - DEPENDS_ON: none
12
- 2. CREATE_ENDPOINT: [name] - [description] - DEPENDS_ON: [resource_name]
13
- 3. CREATE_MATERIALIZED_PIPE: [name] - [description] - DEPENDS_ON: [resource_name]
14
- 4. CREATE_MATERIALIZED_DATASOURCE: [name] - [description] - DEPENDS_ON: [resource_name]
15
- 5. CREATE_SINK: [name] - [description] - DEPENDS_ON: [resource_name]
16
- 6. CREATE_COPY: [name] - [description] - DEPENDS_ON: [resource_name]
17
- 7. CREATE_CONNECTION: [name] - [description] - DEPENDS_ON: none
18
-
8
+ Plan description: [One sentence describing what will be built]
9
+
10
+ Steps:
11
+ 1. Connection: [name] - [description]
12
+ 2. Datasource: [name] - [description] - Depends on: [connection_name (optional)]
13
+ 3. Endpoint: [name] - [description] - Depends on: [resources]
14
+ 4. Materialized pipe: [name] - [description] - Depends on: [resources]
15
+ 5. Materialized datasource: [name] - [description] - Depends on: [resources]
16
+ 6. Sink: [name] - [description] - Depends on: [resources]
17
+ 7. Copy: [name] - [description] - Depends on: [resources]
18
+ 8. Build project
19
+ 9. Generate mock data: [datasource_name]
20
+ 10. Append existing fixture: [fixture_pathname] - Target: [datasource_name]
21
+
22
+ <dev_notes>
19
23
  You can skip steps where resources will not be created or updated.
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 fixture file was provided at the beginning of the plan.
27
+ </dev_notes>
20
28
 
21
- ESTIMATED_RESOURCES: [total number of steps]
22
-
23
- RESOURCE_DEPENDENCIES:
24
- [resource_name]: [resource_name]
29
+ Resource dependencies:
30
+ [resource_name]: [resources]
25
31
  """
26
32
 
27
33
 
@@ -89,31 +95,53 @@ datafile_instructions = """
89
95
 
90
96
  def resources_prompt(project: Project) -> str:
91
97
  files = project.get_project_files()
92
-
93
- if not files:
94
- return "No resources found"
95
-
96
- paths = [Path(file_path) for file_path in files]
97
-
98
- def get_resource_type(path: Path) -> str:
99
- if path.suffix.lower() == ".pipe":
100
- return Project.get_pipe_type(str(path))
101
- elif path.suffix.lower() == ".datasource":
102
- return "datasource"
103
- elif path.suffix.lower() == ".connection":
104
- return "connection"
105
- return "unknown"
106
-
107
- return "\n".join(
108
- [
109
- f"""
110
- <resource>
111
- <path>{file_path.relative_to(project.folder)}</path>
112
- <type>{get_resource_type(file_path)}</type>
113
- <name>{file_path.stem}</name>
114
- <content>{file_path.read_text()}</content>
115
- </resource>
116
- """
117
- for file_path in paths
118
- ]
119
- )
98
+ fixture_files = project.get_fixture_files()
99
+
100
+ resources_content = "# Existing resources in the project:\n"
101
+ if files:
102
+ paths = [Path(file_path) for file_path in files]
103
+
104
+ resources_content += "\n".join(
105
+ [
106
+ f"""
107
+ <resource>
108
+ <path>{file_path.relative_to(project.folder)}</path>
109
+ <type>{get_resource_type(file_path)}</type>
110
+ <name>{file_path.stem}</name>
111
+ <content>{file_path.read_text()}</content>
112
+ </resource>
113
+ """
114
+ for file_path in paths
115
+ ]
116
+ )
117
+ else:
118
+ resources_content += "No resources found"
119
+
120
+ fixture_content = "# Fixture files in the project:\n"
121
+ if fixture_files:
122
+ paths = [Path(file_path) for file_path in fixture_files]
123
+ fixture_content += "\n".join(
124
+ [
125
+ f"""
126
+ <fixture>
127
+ <path>{file_path.relative_to(project.folder)}</path>
128
+ <name>{file_path.stem}</name>
129
+ </fixture>
130
+ """
131
+ for file_path in paths
132
+ ]
133
+ )
134
+ else:
135
+ fixture_content += "No fixture files found"
136
+
137
+ return resources_content + "\n" + fixture_content
138
+
139
+
140
+ def get_resource_type(path: Path) -> str:
141
+ if path.suffix.lower() == ".pipe":
142
+ return Project.get_pipe_type(str(path))
143
+ elif path.suffix.lower() == ".datasource":
144
+ return "datasource"
145
+ elif path.suffix.lower() == ".connection":
146
+ return "connection"
147
+ return "unknown"
@@ -0,0 +1,55 @@
1
+ import click
2
+ from pydantic_ai import RunContext
3
+
4
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_options
5
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
6
+
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
+ def append(ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str) -> str:
28
+ """Append existing fixture to a datasource
29
+
30
+ Args:
31
+ datasource_name: Name of the datasource to append fixture to
32
+ fixture_pathname: Path to the fixture file to append
33
+
34
+ Returns:
35
+ str: Message indicating the success or failure of the appending
36
+ """
37
+ try:
38
+ ctx.deps.thinking_animation.stop()
39
+ confirmation = ctx.deps.dangerously_skip_permissions or get_append_confirmation(datasource_name)
40
+ ctx.deps.thinking_animation.start()
41
+
42
+ if not confirmation:
43
+ return "User rejected appending existing fixture. Skipping..."
44
+
45
+ ctx.deps.thinking_animation.stop()
46
+ click.echo(FeedbackManager.highlight(message=f"\n» Appending {fixture_pathname} to {datasource_name}..."))
47
+ ctx.deps.append_data(datasource_name=datasource_name, path=fixture_pathname)
48
+ click.echo(FeedbackManager.success(message=f"✓ Data appended to {datasource_name}"))
49
+ ctx.deps.thinking_animation.start()
50
+ return f"Data appended to {datasource_name}"
51
+ except Exception as e:
52
+ ctx.deps.thinking_animation.stop()
53
+ click.echo(FeedbackManager.error(message=e))
54
+ ctx.deps.thinking_animation.start()
55
+ return f"Error appending fixture {fixture_pathname} to {datasource_name}: {e}"
@@ -0,0 +1,20 @@
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 build(ctx: RunContext[TinybirdAgentContext]) -> str:
9
+ """Build the project"""
10
+ try:
11
+ ctx.deps.thinking_animation.stop()
12
+ click.echo(FeedbackManager.highlight(message="\n» Building project..."))
13
+ ctx.deps.build_project(test=False, silent=False)
14
+ ctx.deps.thinking_animation.start()
15
+ return "Project built successfully"
16
+ except Exception as e:
17
+ ctx.deps.thinking_animation.stop()
18
+ click.echo(FeedbackManager.error(message=e))
19
+ ctx.deps.thinking_animation.start()
20
+ return f"Error building project: {e}"
@@ -0,0 +1,145 @@
1
+ import difflib
2
+ from pathlib import Path
3
+
4
+ try:
5
+ from colorama import Back, Fore, Style, init
6
+
7
+ init()
8
+ except ImportError: # fallback so that the imported classes always exist
9
+
10
+ class ColorFallback:
11
+ def __getattr__(self, name):
12
+ return ""
13
+
14
+ Fore = Back = Style = ColorFallback()
15
+
16
+ import click
17
+ from pydantic_ai import RunContext
18
+
19
+ from tinybird.tb.modules.agent.utils import Datafile, TinybirdAgentContext, show_options
20
+ from tinybird.tb.modules.exceptions import CLIBuildException
21
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
22
+
23
+
24
+ def create_line_numbered_diff(original_content: str, new_content: str, filename: str) -> str:
25
+ """Create a diff with line numbers similar to the example format"""
26
+ original_lines = original_content.splitlines()
27
+ new_lines = new_content.splitlines()
28
+
29
+ # Create a SequenceMatcher to find the differences
30
+ matcher = difflib.SequenceMatcher(None, original_lines, new_lines)
31
+
32
+ result = []
33
+ result.append(f"╭{'─' * 88}╮")
34
+ result.append(f"│ {filename:<86} │")
35
+ result.append(f"│{' ' * 88}│")
36
+
37
+ # Process the opcodes to build the diff
38
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
39
+ if tag == "equal":
40
+ # Show context lines
41
+ for i, line in enumerate(original_lines[i1:i2]):
42
+ line_num = i1 + i + 1
43
+ result.append(f"│ {line_num:4} {line:<74} │")
44
+ elif tag == "replace":
45
+ # Show removed lines
46
+ for i, line in enumerate(original_lines[i1:i2]):
47
+ line_num = i1 + i + 1
48
+ result.append(f"│ {Back.RED}{line_num:4} - {line:<74}{Back.RESET} │")
49
+ # Show added lines
50
+ for i, line in enumerate(new_lines[j1:j2]):
51
+ line_num = i1 + i + 1
52
+ result.append(f"│ {Back.GREEN}{line_num:4} + {line:<74}{Back.RESET} │")
53
+ elif tag == "delete":
54
+ # Show removed lines
55
+ for i, line in enumerate(original_lines[i1:i2]):
56
+ line_num = i1 + i + 1
57
+ result.append(f"│ {Back.RED}{line_num:4} - {line:<74}{Back.RESET} │")
58
+ elif tag == "insert":
59
+ # Show added lines
60
+ for i, line in enumerate(new_lines[j1:j2]):
61
+ # Use the line number from the original position
62
+ line_num = i1 + i + 1
63
+ result.append(f"│ {Back.GREEN}{line_num:4} + {line:<74}{Back.RESET} │")
64
+
65
+ result.append(f"╰{'─' * 88}╯")
66
+ return "\n".join(result)
67
+
68
+
69
+ def create_line_numbered_content(content: str, filename: str) -> str:
70
+ """Create a formatted display of file content with line numbers"""
71
+ lines = content.splitlines()
72
+
73
+ result = []
74
+ result.append(f"╭{'─' * 88}╮")
75
+ result.append(f"│ {filename:<86} │")
76
+ result.append(f"│{' ' * 88}│")
77
+
78
+ for i, line in enumerate(lines, 1):
79
+ result.append(f"│ {i:4} {line:<74} │")
80
+
81
+ result.append(f"╰{'─' * 88}╯")
82
+ return "\n".join(result)
83
+
84
+
85
+ def get_resource_confirmation(resource: Datafile, exists: bool) -> bool:
86
+ """Get user confirmation for creating a resource"""
87
+ while True:
88
+ action = "create" if not exists else "update"
89
+ result = show_options(
90
+ options=[f"Yes, {action} {resource.type} '{resource.name}'", "No, and tell Tinybird Code what to do"],
91
+ title=f"What would you like to do with {resource.type} '{resource.name}'?",
92
+ )
93
+
94
+ if result is None: # Cancelled
95
+ return False
96
+
97
+ if result.startswith("Yes"):
98
+ return True
99
+ elif result.startswith("No"):
100
+ return False
101
+
102
+ return False
103
+
104
+
105
+ def create_datafile(ctx: RunContext[TinybirdAgentContext], resource: Datafile) -> str:
106
+ """Given a resource representation, create a file in the project folder
107
+
108
+ Args:
109
+ resource (Datafile): The resource to create. Required.
110
+
111
+ Returns:
112
+ str: If the resource was created or not.
113
+ """
114
+ try:
115
+ ctx.deps.thinking_animation.stop()
116
+ resource.pathname = resource.pathname.removeprefix("/")
117
+ path = Path(ctx.deps.folder) / resource.pathname
118
+ content = resource.content
119
+ exists = str(path) in ctx.deps.get_project_files()
120
+ if exists:
121
+ content = create_line_numbered_diff(path.read_text(), resource.content, resource.pathname)
122
+ else:
123
+ content = create_line_numbered_content(resource.content, resource.pathname)
124
+ click.echo(content)
125
+ confirmation = ctx.deps.dangerously_skip_permissions or get_resource_confirmation(resource, exists)
126
+
127
+ if not confirmation:
128
+ ctx.deps.thinking_animation.start()
129
+ return f"Resource {resource.pathname} was not created. User cancelled creation."
130
+
131
+ folder_path = path.parent
132
+ folder_path.mkdir(parents=True, exist_ok=True)
133
+ path.touch(exist_ok=True)
134
+ path.write_text(resource.content)
135
+ ctx.deps.build_project(test=True, silent=True)
136
+ ctx.deps.thinking_animation.start()
137
+ return f"Created {resource.pathname}"
138
+
139
+ except CLIBuildException as e:
140
+ ctx.deps.thinking_animation.stop()
141
+ click.echo(FeedbackManager.error(message=e))
142
+ ctx.deps.thinking_animation.start()
143
+ return f"Error building project: {e}"
144
+ except Exception as e:
145
+ return f"Error creating {resource.pathname}: {e}"