tinybird 0.0.1.dev291__py3-none-any.whl → 1.0.5__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.
- tinybird/ch_utils/constants.py +5 -0
- tinybird/connectors.py +1 -7
- tinybird/context.py +3 -3
- tinybird/datafile/common.py +10 -8
- tinybird/datafile/parse_pipe.py +2 -2
- tinybird/feedback_manager.py +3 -0
- tinybird/prompts.py +1 -0
- tinybird/service_datasources.py +223 -0
- tinybird/sql_template.py +26 -11
- tinybird/sql_template_fmt.py +14 -4
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/cli.py +1 -0
- tinybird/tb/client.py +104 -26
- tinybird/tb/config.py +24 -0
- tinybird/tb/modules/agent/agent.py +103 -67
- tinybird/tb/modules/agent/banner.py +15 -15
- tinybird/tb/modules/agent/explore_agent.py +5 -0
- tinybird/tb/modules/agent/mock_agent.py +5 -1
- tinybird/tb/modules/agent/models.py +6 -2
- tinybird/tb/modules/agent/prompts.py +49 -2
- tinybird/tb/modules/agent/tools/deploy.py +1 -1
- tinybird/tb/modules/agent/tools/execute_query.py +15 -18
- tinybird/tb/modules/agent/tools/request_endpoint.py +1 -1
- tinybird/tb/modules/agent/tools/run_command.py +9 -0
- tinybird/tb/modules/agent/utils.py +38 -48
- tinybird/tb/modules/branch.py +150 -0
- tinybird/tb/modules/build.py +58 -13
- tinybird/tb/modules/build_common.py +209 -25
- tinybird/tb/modules/cli.py +129 -16
- tinybird/tb/modules/common.py +172 -146
- tinybird/tb/modules/connection.py +125 -194
- tinybird/tb/modules/connection_kafka.py +382 -0
- tinybird/tb/modules/copy.py +3 -1
- tinybird/tb/modules/create.py +83 -150
- tinybird/tb/modules/datafile/build.py +27 -38
- tinybird/tb/modules/datafile/build_datasource.py +21 -25
- tinybird/tb/modules/datafile/diff.py +1 -1
- tinybird/tb/modules/datafile/format_pipe.py +46 -7
- tinybird/tb/modules/datafile/playground.py +59 -68
- tinybird/tb/modules/datafile/pull.py +2 -3
- tinybird/tb/modules/datasource.py +477 -308
- tinybird/tb/modules/deployment.py +2 -0
- tinybird/tb/modules/deployment_common.py +84 -44
- tinybird/tb/modules/deprecations.py +4 -4
- tinybird/tb/modules/dev_server.py +33 -12
- tinybird/tb/modules/exceptions.py +14 -0
- tinybird/tb/modules/feedback_manager.py +1 -1
- tinybird/tb/modules/info.py +69 -12
- tinybird/tb/modules/infra.py +4 -5
- tinybird/tb/modules/job_common.py +15 -0
- tinybird/tb/modules/local.py +143 -23
- tinybird/tb/modules/local_common.py +347 -19
- tinybird/tb/modules/local_logs.py +209 -0
- tinybird/tb/modules/login.py +21 -2
- tinybird/tb/modules/login_common.py +254 -12
- tinybird/tb/modules/mock.py +5 -54
- tinybird/tb/modules/mock_common.py +0 -54
- tinybird/tb/modules/open.py +10 -5
- tinybird/tb/modules/project.py +14 -5
- tinybird/tb/modules/shell.py +15 -7
- tinybird/tb/modules/sink.py +3 -1
- tinybird/tb/modules/telemetry.py +11 -3
- tinybird/tb/modules/test.py +13 -9
- tinybird/tb/modules/test_common.py +13 -87
- tinybird/tb/modules/tinyunit/tinyunit.py +0 -14
- tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -6
- tinybird/tb/modules/watch.py +5 -3
- tinybird/tb_cli_modules/common.py +2 -2
- tinybird/tb_cli_modules/telemetry.py +1 -1
- tinybird/tornado_template.py +6 -7
- {tinybird-0.0.1.dev291.dist-info → tinybird-1.0.5.dist-info}/METADATA +32 -6
- tinybird-1.0.5.dist-info/RECORD +132 -0
- {tinybird-0.0.1.dev291.dist-info → tinybird-1.0.5.dist-info}/WHEEL +1 -1
- tinybird-0.0.1.dev291.dist-info/RECORD +0 -128
- {tinybird-0.0.1.dev291.dist-info → tinybird-1.0.5.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev291.dist-info → tinybird-1.0.5.dist-info}/top_level.txt +0 -0
tinybird/ch_utils/constants.py
CHANGED
|
@@ -258,4 +258,9 @@ VALID_QUERY_FORMATS = (
|
|
|
258
258
|
"RowBinaryWithNamesAndTypes",
|
|
259
259
|
"TabSeparated",
|
|
260
260
|
"JSONCompactEachRowWithNamesAndTypes",
|
|
261
|
+
"TabSeparatedWithNamesAndTypes",
|
|
262
|
+
"JSONCompactEachRow",
|
|
263
|
+
"JSONCompact",
|
|
264
|
+
"JSONStringsEachRowWithProgress",
|
|
265
|
+
"ODBCDriver2",
|
|
261
266
|
)
|
tinybird/connectors.py
CHANGED
|
@@ -369,13 +369,7 @@ class Snowflake(Connector):
|
|
|
369
369
|
the_type = "String"
|
|
370
370
|
if t.startswith("NUMBER"):
|
|
371
371
|
the_type = "Int32"
|
|
372
|
-
if (
|
|
373
|
-
t.startswith("FLOAT")
|
|
374
|
-
or t.startswith("DOUBLE")
|
|
375
|
-
or t.startswith("REAL")
|
|
376
|
-
or t.startswith("NUMERIC")
|
|
377
|
-
or t.startswith("DECIMAL")
|
|
378
|
-
):
|
|
372
|
+
if t.startswith(("FLOAT", "DOUBLE", "REAL", "NUMERIC", "DECIMAL")):
|
|
379
373
|
the_type = "Float32"
|
|
380
374
|
if t == "DATE":
|
|
381
375
|
the_type = "Date"
|
tinybird/context.py
CHANGED
|
@@ -3,15 +3,15 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
|
|
4
4
|
# Avoid circular import error
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
|
-
from
|
|
6
|
+
from hfi.hfi_workspace_data import HfiWorkspaceData
|
|
7
|
+
|
|
7
8
|
|
|
8
9
|
workspace_id: ContextVar[str] = ContextVar("workspace_id")
|
|
9
|
-
|
|
10
|
+
hfi_workspace_data: ContextVar["HfiWorkspaceData"] = ContextVar("hfi_workspace_data")
|
|
10
11
|
table_id: ContextVar[str] = ContextVar("table_id")
|
|
11
12
|
hfi_frequency: ContextVar[float] = ContextVar("hfi_frequency")
|
|
12
13
|
hfi_frequency_gatherer: ContextVar[float] = ContextVar("hfi_frequency_gatherer")
|
|
13
14
|
use_gatherer: ContextVar[bool] = ContextVar("use_gatherer")
|
|
14
|
-
allow_gatherer_fallback: ContextVar[bool] = ContextVar("allow_gatherer_fallback")
|
|
15
15
|
gatherer_allow_s3_backup_on_user_errors: ContextVar[bool] = ContextVar("gatherer_allow_s3_backup_on_user_errors")
|
|
16
16
|
disable_template_security_validation: ContextVar[bool] = ContextVar("disable_template_security_validation")
|
|
17
17
|
origin: ContextVar[str] = ContextVar("origin")
|
tinybird/datafile/common.py
CHANGED
|
@@ -630,7 +630,10 @@ class Datafile:
|
|
|
630
630
|
def _validate_single_column(self, col_name: str, column_info: Dict[str, Any]) -> None:
|
|
631
631
|
"""Validate a single column for use in sorting keys."""
|
|
632
632
|
col_type = column_info.get("type", "").lower()
|
|
633
|
-
|
|
633
|
+
|
|
634
|
+
# we need to check any presence of Nullable in the column type
|
|
635
|
+
is_nullable = column_info.get("nullable", False) or "Nullable(" in column_info.get("type", "")
|
|
636
|
+
|
|
634
637
|
if is_nullable:
|
|
635
638
|
raise DatafileValidationError(
|
|
636
639
|
f"Sorting key contains nullable column '{col_name}'. Nullable columns cannot be used in sorting keys."
|
|
@@ -2048,11 +2051,7 @@ def parse(
|
|
|
2048
2051
|
lexer = list(sa)
|
|
2049
2052
|
if lexer:
|
|
2050
2053
|
cmd, args = lexer[0], lexer[1:]
|
|
2051
|
-
if (
|
|
2052
|
-
parser_state.multiline
|
|
2053
|
-
and cmd.lower() in cmds
|
|
2054
|
-
and not (line.startswith(" ") or line.startswith("\t"))
|
|
2055
|
-
):
|
|
2054
|
+
if parser_state.multiline and cmd.lower() in cmds and not line.startswith((" ", "\t")):
|
|
2056
2055
|
cmds[parser_state.command](
|
|
2057
2056
|
parser_state.multiline_string,
|
|
2058
2057
|
lineno=lineno,
|
|
@@ -2128,6 +2127,9 @@ def parse(
|
|
|
2128
2127
|
return ParseResult(datafile=doc, warnings=warnings)
|
|
2129
2128
|
|
|
2130
2129
|
|
|
2130
|
+
# TODO: This class is duplicated in tinybird/datafile_common.py with a slightly different
|
|
2131
|
+
# _REPLACEMENTS tuple. The duplication happened during the CLI/server code split (commit
|
|
2132
|
+
# f86d02cdd7). Consider extracting shared code into a common module that both files can import.
|
|
2131
2133
|
class ImportReplacements:
|
|
2132
2134
|
_REPLACEMENTS: Tuple[Tuple[str, str, Optional[str]], ...] = (
|
|
2133
2135
|
("import_strategy", "mode", "replace"),
|
|
@@ -2147,7 +2149,7 @@ class ImportReplacements:
|
|
|
2147
2149
|
return [x[0] for x in ImportReplacements._REPLACEMENTS]
|
|
2148
2150
|
|
|
2149
2151
|
@staticmethod
|
|
2150
|
-
def get_api_param_for_datafile_param(
|
|
2152
|
+
def get_api_param_for_datafile_param(key: str) -> Tuple[Optional[str], Optional[str]]:
|
|
2151
2153
|
"""Returns the API parameter name and default value for a given
|
|
2152
2154
|
datafile parameter.
|
|
2153
2155
|
"""
|
|
@@ -2505,7 +2507,7 @@ def is_file_a_datasource(filename: str) -> bool:
|
|
|
2505
2507
|
|
|
2506
2508
|
for line in lines:
|
|
2507
2509
|
trimmed_line = line.strip().lower()
|
|
2508
|
-
if trimmed_line.startswith("schema"
|
|
2510
|
+
if trimmed_line.startswith(("schema", "engine")):
|
|
2509
2511
|
return True
|
|
2510
2512
|
|
|
2511
2513
|
return False
|
tinybird/datafile/parse_pipe.py
CHANGED
|
@@ -11,7 +11,7 @@ from tinybird.datafile.common import (
|
|
|
11
11
|
parse,
|
|
12
12
|
)
|
|
13
13
|
from tinybird.datafile.exceptions import IncludeFileNotFoundException, ParseException
|
|
14
|
-
from tinybird.sql_template import get_template_and_variables, render_sql_template
|
|
14
|
+
from tinybird.sql_template import get_template_and_variables, render_sql_template
|
|
15
15
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
16
16
|
from tinybird.tornado_template import UnClosedIfError
|
|
17
17
|
|
|
@@ -54,7 +54,7 @@ def parse_pipe(
|
|
|
54
54
|
if sql.strip()[0] == "%":
|
|
55
55
|
secrets_list: Optional[List[str]] = None
|
|
56
56
|
if secrets:
|
|
57
|
-
secrets_list =
|
|
57
|
+
secrets_list = list(secrets.keys())
|
|
58
58
|
# Setting test_mode=True to ignore errors on required parameters and
|
|
59
59
|
# secrets_in_test_mode=False to raise errors on missing secrets
|
|
60
60
|
sql, _, variable_warnings = render_sql_template(
|
tinybird/feedback_manager.py
CHANGED
|
@@ -506,6 +506,9 @@ Ready? """
|
|
|
506
506
|
prompt_init_git_release_force = prompt_message(
|
|
507
507
|
"You are going to manually update workspace commit reference manually, this is just for special occasions. Do you want to update current commit reference '{current_commit}' to '{new_commit}'?"
|
|
508
508
|
)
|
|
509
|
+
prompt_init_git_release_new = prompt_message(
|
|
510
|
+
"This workspace does not have any release yet. Do you want to create one with commit '{commit}'? This will enable 'tb deploy' to work."
|
|
511
|
+
)
|
|
509
512
|
|
|
510
513
|
warning_exchange = warning_message(
|
|
511
514
|
"Warning: Do you want to exchange Data Source {datasource_a} by Data Source {datasource_b}?"
|
tinybird/prompts.py
CHANGED
|
@@ -776,6 +776,7 @@ datasource_instructions = """
|
|
|
776
776
|
- Use always json paths to define the schema. Example: `user_id` String `json:$.user_id`,
|
|
777
777
|
- Array columns are supported with a special syntax. Example: `items` Array(String) `json:$.items[:]`
|
|
778
778
|
- If the datasource is using an S3 or GCS connection, they need to set IMPORT_CONNECTION_NAME, IMPORT_BUCKET_URI and IMPORT_SCHEDULE (GCS @on-demand only, S3 supports @auto too)
|
|
779
|
+
- If the datasource is using a Kafka connection, they need to set KAFKA_CONNECTION_NAME as the name of the .connection file, KAFKA_TOPIC topic_name and KAFKA_GROUP_ID as the group id for the datasource
|
|
779
780
|
- Unless the user asks for them, do not include ENGINE_PARTITION_KEY and ENGINE_PRIMARY_KEY.
|
|
780
781
|
- DateTime64 type without precision is not supported. Use DateTime64(3) instead.
|
|
781
782
|
</datasource_file_instructions>
|
tinybird/service_datasources.py
CHANGED
|
@@ -5,6 +5,7 @@ This module provides access to predefined service datasources and their schemas
|
|
|
5
5
|
for both Tinybird and Organization scopes.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from functools import lru_cache
|
|
8
9
|
from typing import Any, Dict, List, Optional
|
|
9
10
|
|
|
10
11
|
|
|
@@ -428,6 +429,135 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
|
|
|
428
429
|
{"name": "total_cpu_time_seconds", "type": "Float64"},
|
|
429
430
|
],
|
|
430
431
|
},
|
|
432
|
+
{
|
|
433
|
+
"name": "tinybird.llm_usage",
|
|
434
|
+
"description": "LLM usage metrics from Tinybird AI features including token consumption, costs, and model usage for each request in the workspace.",
|
|
435
|
+
"dateColumn": "start_time",
|
|
436
|
+
"engine": {
|
|
437
|
+
"engine": "MergeTree",
|
|
438
|
+
"sorting_key": "workspace_id, start_time, user_email, request_id",
|
|
439
|
+
"partition_key": "toYYYYMM(start_time)",
|
|
440
|
+
},
|
|
441
|
+
"columns": [
|
|
442
|
+
{"name": "start_time", "type": "DateTime"},
|
|
443
|
+
{"name": "end_time", "type": "DateTime"},
|
|
444
|
+
{"name": "organization_id", "type": "String"},
|
|
445
|
+
{"name": "organization_name", "type": "String"},
|
|
446
|
+
{"name": "workspace_id", "type": "String"},
|
|
447
|
+
{"name": "workspace_name", "type": "String"},
|
|
448
|
+
{"name": "user_email", "type": "String"},
|
|
449
|
+
{"name": "request_id", "type": "String"},
|
|
450
|
+
{"name": "prompt_tokens", "type": "UInt32"},
|
|
451
|
+
{"name": "completion_tokens", "type": "UInt32"},
|
|
452
|
+
{"name": "total_tokens", "type": "UInt32"},
|
|
453
|
+
{"name": "duration", "type": "Float32"},
|
|
454
|
+
{"name": "cost", "type": "Float32"},
|
|
455
|
+
{"name": "origin", "type": "String"},
|
|
456
|
+
{"name": "feature", "type": "String"},
|
|
457
|
+
],
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"name": "tinybird.llm_usage",
|
|
461
|
+
"description": "LLM usage metrics from Tinybird AI features including token consumption, costs, and model usage for each request in the workspace.",
|
|
462
|
+
"dateColumn": "start_time",
|
|
463
|
+
"engine": {
|
|
464
|
+
"engine": "MergeTree",
|
|
465
|
+
"sorting_key": "workspace_id, start_time, user_email, request_id",
|
|
466
|
+
"partition_key": "toYYYYMM(start_time)",
|
|
467
|
+
},
|
|
468
|
+
"columns": [
|
|
469
|
+
{"name": "start_time", "type": "DateTime"},
|
|
470
|
+
{"name": "end_time", "type": "DateTime"},
|
|
471
|
+
{"name": "organization_id", "type": "String"},
|
|
472
|
+
{"name": "organization_name", "type": "String"},
|
|
473
|
+
{"name": "workspace_id", "type": "String"},
|
|
474
|
+
{"name": "workspace_name", "type": "String"},
|
|
475
|
+
{"name": "user_email", "type": "String"},
|
|
476
|
+
{"name": "request_id", "type": "String"},
|
|
477
|
+
{"name": "prompt_tokens", "type": "UInt32"},
|
|
478
|
+
{"name": "completion_tokens", "type": "UInt32"},
|
|
479
|
+
{"name": "total_tokens", "type": "UInt32"},
|
|
480
|
+
{"name": "duration", "type": "Float32"},
|
|
481
|
+
{"name": "cost", "type": "Float32"},
|
|
482
|
+
{"name": "origin", "type": "String"},
|
|
483
|
+
{"name": "feature", "type": "String"},
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
"name": "tinybird.query_metrics",
|
|
488
|
+
"description": "Query stats metrics from your workspace.",
|
|
489
|
+
"dateColumn": "event_time",
|
|
490
|
+
"engine": {
|
|
491
|
+
"engine": "ReplacingMergeTree",
|
|
492
|
+
"sorting_key": "event_time, organization_id, query_id",
|
|
493
|
+
"partition_key": "toStartOfDay(event_time)",
|
|
494
|
+
"ttl": "toDate(event_time) + INTERVAL 30 DAY",
|
|
495
|
+
},
|
|
496
|
+
"columns": [
|
|
497
|
+
{"name": "event_time", "type": "DateTime"},
|
|
498
|
+
{"name": "organization_id", "type": "String"},
|
|
499
|
+
{"name": "workspace_id", "type": "String"},
|
|
500
|
+
{"name": "query", "type": "String"},
|
|
501
|
+
{"name": "query_id", "type": "String"},
|
|
502
|
+
{"name": "query_type", "type": "String"},
|
|
503
|
+
{"name": "query_start_time", "type": "DateTime"},
|
|
504
|
+
{"name": "query_duration_ms", "type": "Int32"},
|
|
505
|
+
{"name": "pipe_id", "type": "String"},
|
|
506
|
+
{"name": "job_id", "type": "String"},
|
|
507
|
+
{"name": "job_kind", "type": "String"},
|
|
508
|
+
{"name": "read_rows", "type": "Int32"},
|
|
509
|
+
{"name": "read_bytes", "type": "Int32"},
|
|
510
|
+
{"name": "written_rows", "type": "Int32"},
|
|
511
|
+
{"name": "written_bytes", "type": "Int32"},
|
|
512
|
+
{"name": "memory_usage", "type": "Int32"},
|
|
513
|
+
{"name": "vcpu_time", "type": "Float32"},
|
|
514
|
+
{"name": "exception_code", "type": "Int32"},
|
|
515
|
+
{"name": "exception", "type": "String"},
|
|
516
|
+
],
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"name": "tinybird.vcpu_time",
|
|
520
|
+
"description": "vCPU time metrics from your workspace.",
|
|
521
|
+
"dateColumn": "second_slot",
|
|
522
|
+
"engine": {
|
|
523
|
+
"engine": "AggregatingMergeTree",
|
|
524
|
+
"sorting_key": "organization_id, second_slot",
|
|
525
|
+
"partition_key": "toStartOfDay(second_slot)",
|
|
526
|
+
"ttl": "toDate(second_slot) + INTERVAL 30 DAY",
|
|
527
|
+
},
|
|
528
|
+
"columns": [
|
|
529
|
+
{"name": "second_slot", "type": "DateTime"},
|
|
530
|
+
{"name": "organization_id", "type": "String"},
|
|
531
|
+
{"name": "vcpu_time", "type": "Float64"},
|
|
532
|
+
],
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
"name": "tinybird.query_validator_log",
|
|
536
|
+
"description": "Log of failed queries executions in the next available ClickHouse version and their results.",
|
|
537
|
+
"dateColumn": "run_validation",
|
|
538
|
+
"engine": {
|
|
539
|
+
"engine": "MergeTree",
|
|
540
|
+
"sorting_key": "database, host, run_validation, query_hash",
|
|
541
|
+
"partition_key": "toYYYYMM(run_validation)",
|
|
542
|
+
},
|
|
543
|
+
"columns": [
|
|
544
|
+
{"name": "host", "type": "LowCardinality(String)"},
|
|
545
|
+
{"name": "version", "type": "LowCardinality(String)"},
|
|
546
|
+
{"name": "stable_version", "type": "LowCardinality(String)"},
|
|
547
|
+
{"name": "query_hash", "type": "UInt64"},
|
|
548
|
+
{"name": "query_last_execution", "type": "DateTime"},
|
|
549
|
+
{"name": "region", "type": "String"},
|
|
550
|
+
{"name": "workspace", "type": "String"},
|
|
551
|
+
{"name": "database", "type": "String"},
|
|
552
|
+
{"name": "pipe_name", "type": "String"},
|
|
553
|
+
{"name": "query_id", "type": "String"},
|
|
554
|
+
{"name": "query", "type": "String"},
|
|
555
|
+
{"name": "error_code", "type": "Int16"},
|
|
556
|
+
{"name": "error", "type": "String"},
|
|
557
|
+
{"name": "fix_suggestion", "type": "String"},
|
|
558
|
+
{"name": "run_validation", "type": "DateTime"},
|
|
559
|
+
],
|
|
560
|
+
},
|
|
431
561
|
]
|
|
432
562
|
|
|
433
563
|
|
|
@@ -806,6 +936,22 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
806
936
|
{"name": "active_minutes", "type": "Float64"},
|
|
807
937
|
],
|
|
808
938
|
},
|
|
939
|
+
{
|
|
940
|
+
"name": "organization.shared_infra_active_seconds",
|
|
941
|
+
"description": "Contains information about vCPU active seconds consumption aggregated by second for all Organization workspaces. Only available for Developer and Enterprise plans in shared infrastructure.",
|
|
942
|
+
"dateColumn": "second",
|
|
943
|
+
"engine": {
|
|
944
|
+
"engine": "AggregatingMergeTree",
|
|
945
|
+
"sorting_key": "second_slot, organization_id",
|
|
946
|
+
"partition_key": "toYYYYMM(second_slot)",
|
|
947
|
+
},
|
|
948
|
+
"columns": [
|
|
949
|
+
{"name": "second_slot", "type": "DateTime"},
|
|
950
|
+
{"name": "organization_id", "type": "String"},
|
|
951
|
+
{"name": "organization_name", "type": "String"},
|
|
952
|
+
{"name": "cpu_time", "type": "Float64"},
|
|
953
|
+
],
|
|
954
|
+
},
|
|
809
955
|
{
|
|
810
956
|
"name": "organization.shared_infra_qps_overages",
|
|
811
957
|
"description": "Contains information about QPS consumption and overages aggregated by second for all Organization workspaces. Only available for Developer and Enterprise plans in shared infrastructure.",
|
|
@@ -881,9 +1027,86 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
881
1027
|
{"name": "total_written_bytes", "type": "UInt64"},
|
|
882
1028
|
],
|
|
883
1029
|
},
|
|
1030
|
+
{
|
|
1031
|
+
"name": "organization.llm_usage",
|
|
1032
|
+
"description": "LLM usage metrics from Tinybird AI features including token consumption, costs, and model usage for each request in the organization.",
|
|
1033
|
+
"dateColumn": "start_time",
|
|
1034
|
+
"engine": {
|
|
1035
|
+
"engine": "MergeTree",
|
|
1036
|
+
"sorting_key": "workspace_id, start_time, user_email, request_id",
|
|
1037
|
+
"partition_key": "toYYYYMM(start_time)",
|
|
1038
|
+
},
|
|
1039
|
+
"columns": [
|
|
1040
|
+
{"name": "start_time", "type": "DateTime"},
|
|
1041
|
+
{"name": "end_time", "type": "DateTime"},
|
|
1042
|
+
{"name": "organization_id", "type": "String"},
|
|
1043
|
+
{"name": "organization_name", "type": "String"},
|
|
1044
|
+
{"name": "workspace_id", "type": "String"},
|
|
1045
|
+
{"name": "workspace_name", "type": "String"},
|
|
1046
|
+
{"name": "user_email", "type": "String"},
|
|
1047
|
+
{"name": "request_id", "type": "String"},
|
|
1048
|
+
{"name": "prompt_tokens", "type": "UInt32"},
|
|
1049
|
+
{"name": "completion_tokens", "type": "UInt32"},
|
|
1050
|
+
{"name": "total_tokens", "type": "UInt32"},
|
|
1051
|
+
{"name": "duration", "type": "Float32"},
|
|
1052
|
+
{"name": "cost", "type": "Float32"},
|
|
1053
|
+
{"name": "origin", "type": "String"},
|
|
1054
|
+
{"name": "feature", "type": "String"},
|
|
1055
|
+
],
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
"name": "organization.query_metrics",
|
|
1059
|
+
"description": "Query stats metrics from your workspace.",
|
|
1060
|
+
"dateColumn": "event_time",
|
|
1061
|
+
"engine": {
|
|
1062
|
+
"engine": "ReplacingMergeTree",
|
|
1063
|
+
"sorting_key": "event_time, organization_id, query_id",
|
|
1064
|
+
"partition_key": "toStartOfDay(event_time)",
|
|
1065
|
+
"ttl": "toDate(event_time) + INTERVAL 30 DAY",
|
|
1066
|
+
},
|
|
1067
|
+
"columns": [
|
|
1068
|
+
{"name": "event_time", "type": "DateTime"},
|
|
1069
|
+
{"name": "organization_id", "type": "String"},
|
|
1070
|
+
{"name": "workspace_id", "type": "String"},
|
|
1071
|
+
{"name": "query", "type": "String"},
|
|
1072
|
+
{"name": "query_id", "type": "String"},
|
|
1073
|
+
{"name": "query_type", "type": "String"},
|
|
1074
|
+
{"name": "query_start_time", "type": "DateTime"},
|
|
1075
|
+
{"name": "query_duration_ms", "type": "Int32"},
|
|
1076
|
+
{"name": "pipe_id", "type": "String"},
|
|
1077
|
+
{"name": "job_id", "type": "String"},
|
|
1078
|
+
{"name": "job_kind", "type": "String"},
|
|
1079
|
+
{"name": "read_rows", "type": "Int32"},
|
|
1080
|
+
{"name": "read_bytes", "type": "Int32"},
|
|
1081
|
+
{"name": "written_rows", "type": "Int32"},
|
|
1082
|
+
{"name": "written_bytes", "type": "Int32"},
|
|
1083
|
+
{"name": "memory_usage", "type": "Int32"},
|
|
1084
|
+
{"name": "vcpu_time", "type": "Float32"},
|
|
1085
|
+
{"name": "exception_code", "type": "Int32"},
|
|
1086
|
+
{"name": "exception", "type": "String"},
|
|
1087
|
+
],
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
"name": "organization.vcpu_time",
|
|
1091
|
+
"description": "vCPU time metrics from your workspace.",
|
|
1092
|
+
"dateColumn": "second_slot",
|
|
1093
|
+
"engine": {
|
|
1094
|
+
"engine": "AggregatingMergeTree",
|
|
1095
|
+
"sorting_key": "organization_id, second_slot",
|
|
1096
|
+
"partition_key": "toStartOfDay(second_slot)",
|
|
1097
|
+
"ttl": "toDate(second_slot) + INTERVAL 30 DAY",
|
|
1098
|
+
},
|
|
1099
|
+
"columns": [
|
|
1100
|
+
{"name": "second_slot", "type": "DateTime"},
|
|
1101
|
+
{"name": "organization_id", "type": "String"},
|
|
1102
|
+
{"name": "workspace_id", "type": "String"},
|
|
1103
|
+
{"name": "vcpu_time", "type": "Float64"},
|
|
1104
|
+
],
|
|
1105
|
+
},
|
|
884
1106
|
]
|
|
885
1107
|
|
|
886
1108
|
|
|
1109
|
+
@lru_cache(maxsize=1)
|
|
887
1110
|
def get_service_datasources() -> List[Dict[str, Any]]:
|
|
888
1111
|
"""
|
|
889
1112
|
Get the list of all Tinybird and Organization service datasources.
|
tinybird/sql_template.py
CHANGED
|
@@ -384,14 +384,13 @@ def array_type(types):
|
|
|
384
384
|
if isinstance(x, Placeholder):
|
|
385
385
|
if default:
|
|
386
386
|
x = default
|
|
387
|
-
|
|
388
|
-
if _type
|
|
389
|
-
if _type == "String":
|
|
390
|
-
x = ""
|
|
391
|
-
else:
|
|
392
|
-
x = ",".join(map(str, [types[_type](x) for _ in range(2)]))
|
|
393
|
-
else:
|
|
387
|
+
elif _type and _type in types:
|
|
388
|
+
if _type == "String":
|
|
394
389
|
x = ""
|
|
390
|
+
else:
|
|
391
|
+
x = ",".join(map(str, [types[_type](x) for _ in range(2)]))
|
|
392
|
+
else:
|
|
393
|
+
x = ""
|
|
395
394
|
elif x is None:
|
|
396
395
|
x = default
|
|
397
396
|
if x is None:
|
|
@@ -1405,8 +1404,11 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
|
|
|
1405
1404
|
namespace = {}
|
|
1406
1405
|
template_execution_results = TemplateExecutionResults()
|
|
1407
1406
|
for key in kwargs.get("tb_secrets", []):
|
|
1407
|
+
# Avoid double-prefixing if the key already has the tb_secret_ prefix
|
|
1408
1408
|
if is_secret_template_key(key):
|
|
1409
1409
|
template_execution_results.add_template_param(key)
|
|
1410
|
+
else:
|
|
1411
|
+
template_execution_results.add_template_param(secret_template_key(key))
|
|
1410
1412
|
|
|
1411
1413
|
if TB_SECRET_IN_TEST_MODE in kwargs:
|
|
1412
1414
|
template_execution_results[TB_SECRET_IN_TEST_MODE] = None
|
|
@@ -1415,15 +1417,20 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
|
|
|
1415
1417
|
try:
|
|
1416
1418
|
key = secret_template_key(x)
|
|
1417
1419
|
if key in template_execution_results.template_params:
|
|
1420
|
+
# secret available: Always use workspace secret regardless of test mode
|
|
1418
1421
|
template_execution_results.add_ch_param(x)
|
|
1419
1422
|
return Symbol("{" + sqlescape(x) + ": String}")
|
|
1420
1423
|
else:
|
|
1424
|
+
# secret not available: Check test mode and defaults
|
|
1421
1425
|
is_test_mode = TB_SECRET_IN_TEST_MODE in template_execution_results
|
|
1422
|
-
if
|
|
1423
|
-
|
|
1424
|
-
elif default is not None:
|
|
1426
|
+
if default is not None:
|
|
1427
|
+
# Use provided default value
|
|
1425
1428
|
return default
|
|
1429
|
+
elif is_test_mode:
|
|
1430
|
+
# In test mode without default - return placeholder
|
|
1431
|
+
return Symbol("{" + sqlescape(x) + ": String}")
|
|
1426
1432
|
else:
|
|
1433
|
+
# Not in test mode, no secret, no default - raise error
|
|
1427
1434
|
raise SQLTemplateException(
|
|
1428
1435
|
f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
|
|
1429
1436
|
)
|
|
@@ -1834,7 +1841,7 @@ def get_var_names_and_types(t, node_id=None):
|
|
|
1834
1841
|
raise SQLTemplateException(e)
|
|
1835
1842
|
|
|
1836
1843
|
|
|
1837
|
-
@lru_cache(maxsize=
|
|
1844
|
+
@lru_cache(maxsize=512)
|
|
1838
1845
|
def get_var_names_and_types_cached(t: Template):
|
|
1839
1846
|
return get_var_names_and_types(t)
|
|
1840
1847
|
|
|
@@ -2378,6 +2385,14 @@ def render_sql_template(
|
|
|
2378
2385
|
documentation="/cli/advanced-templates.html",
|
|
2379
2386
|
)
|
|
2380
2387
|
raise SQLTemplateException(str(e), documentation="/cli/advanced-templates.html")
|
|
2388
|
+
except IndexError as e:
|
|
2389
|
+
# This happens when trying to access string indices on empty strings
|
|
2390
|
+
if "string index out of range" in str(e):
|
|
2391
|
+
raise SQLTemplateException(
|
|
2392
|
+
"String index out of range. Check that string parameters have values before accessing specific characters (e.g., param[0]). Provide default values or add length checks in your template.",
|
|
2393
|
+
documentation="/cli/advanced-templates.html",
|
|
2394
|
+
)
|
|
2395
|
+
raise SQLTemplateException(str(e), documentation="/cli/advanced-templates.html")
|
|
2381
2396
|
except Exception as e:
|
|
2382
2397
|
# errors might vary here, we need to support as much as possible
|
|
2383
2398
|
# https://gitlab.com/tinybird/analytics/-/issues/943
|
tinybird/sql_template_fmt.py
CHANGED
|
@@ -203,13 +203,15 @@ class TinybirdDialect(ClickHouse):
|
|
|
203
203
|
Rule(
|
|
204
204
|
name="jinja_if_block_end",
|
|
205
205
|
priority=203,
|
|
206
|
-
|
|
206
|
+
# Accept both Tornado-style {% end %} and {% end if %}
|
|
207
|
+
pattern=group(r"\{%-?\s*end(\s+if)?\s*-?%\}"),
|
|
207
208
|
action=actions.raise_sqlfmt_bracket_error,
|
|
208
209
|
),
|
|
209
210
|
Rule(
|
|
210
211
|
name="jinja_for_block_end",
|
|
211
212
|
priority=211,
|
|
212
|
-
|
|
213
|
+
# Accept both Tornado-style {% end %} and {% end for %}
|
|
214
|
+
pattern=group(r"\{%-?\s*end(\s+for)?\s*-?%\}"),
|
|
213
215
|
action=actions.raise_sqlfmt_bracket_error,
|
|
214
216
|
),
|
|
215
217
|
],
|
|
@@ -261,7 +263,13 @@ def _calc_str(self) -> str:
|
|
|
261
263
|
Comment._calc_str = property(_calc_str)
|
|
262
264
|
|
|
263
265
|
|
|
264
|
-
def format_sql_template(
|
|
266
|
+
def format_sql_template(
|
|
267
|
+
sql: str,
|
|
268
|
+
line_length: Optional[int] = None,
|
|
269
|
+
lower_keywords: bool = False,
|
|
270
|
+
resource_name: Optional[str] = None,
|
|
271
|
+
resource_source: Optional[str] = None,
|
|
272
|
+
) -> str:
|
|
265
273
|
try:
|
|
266
274
|
# https://github.com/tconbeer/sqlfmt/blob/c11775b92d8a45f0e91d871b81a88a894d620bec/src/sqlfmt/mode.py#L16-L29
|
|
267
275
|
config: Dict[str, Any] = {
|
|
@@ -277,5 +285,7 @@ def format_sql_template(sql: str, line_length: Optional[int] = None, lower_keywo
|
|
|
277
285
|
else api.format_string(sql, mode=mode).strip()
|
|
278
286
|
)
|
|
279
287
|
except Exception as e:
|
|
280
|
-
|
|
288
|
+
resource_info = f" in '{resource_name}'" if resource_name else ""
|
|
289
|
+
source_info = f" ({resource_source})" if resource_source else ""
|
|
290
|
+
logging.warning(f"sqlfmt error{resource_info}{source_info}: {str(e)}")
|
|
281
291
|
return sql
|
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__ = '
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '1.0.5'
|
|
8
|
+
__revision__ = '60ae688'
|