pytrilogy 0.3.142__cp312-cp312-win_amd64.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.
Files changed (200) hide show
  1. LICENSE.md +19 -0
  2. _preql_import_resolver/__init__.py +5 -0
  3. _preql_import_resolver/_preql_import_resolver.cp312-win_amd64.pyd +0 -0
  4. pytrilogy-0.3.142.dist-info/METADATA +555 -0
  5. pytrilogy-0.3.142.dist-info/RECORD +200 -0
  6. pytrilogy-0.3.142.dist-info/WHEEL +4 -0
  7. pytrilogy-0.3.142.dist-info/entry_points.txt +2 -0
  8. pytrilogy-0.3.142.dist-info/licenses/LICENSE.md +19 -0
  9. trilogy/__init__.py +16 -0
  10. trilogy/ai/README.md +10 -0
  11. trilogy/ai/__init__.py +19 -0
  12. trilogy/ai/constants.py +92 -0
  13. trilogy/ai/conversation.py +107 -0
  14. trilogy/ai/enums.py +7 -0
  15. trilogy/ai/execute.py +50 -0
  16. trilogy/ai/models.py +34 -0
  17. trilogy/ai/prompts.py +100 -0
  18. trilogy/ai/providers/__init__.py +0 -0
  19. trilogy/ai/providers/anthropic.py +106 -0
  20. trilogy/ai/providers/base.py +24 -0
  21. trilogy/ai/providers/google.py +146 -0
  22. trilogy/ai/providers/openai.py +89 -0
  23. trilogy/ai/providers/utils.py +68 -0
  24. trilogy/authoring/README.md +3 -0
  25. trilogy/authoring/__init__.py +148 -0
  26. trilogy/constants.py +113 -0
  27. trilogy/core/README.md +52 -0
  28. trilogy/core/__init__.py +0 -0
  29. trilogy/core/constants.py +6 -0
  30. trilogy/core/enums.py +443 -0
  31. trilogy/core/env_processor.py +120 -0
  32. trilogy/core/environment_helpers.py +320 -0
  33. trilogy/core/ergonomics.py +193 -0
  34. trilogy/core/exceptions.py +123 -0
  35. trilogy/core/functions.py +1227 -0
  36. trilogy/core/graph_models.py +139 -0
  37. trilogy/core/internal.py +85 -0
  38. trilogy/core/models/__init__.py +0 -0
  39. trilogy/core/models/author.py +2669 -0
  40. trilogy/core/models/build.py +2521 -0
  41. trilogy/core/models/build_environment.py +180 -0
  42. trilogy/core/models/core.py +501 -0
  43. trilogy/core/models/datasource.py +322 -0
  44. trilogy/core/models/environment.py +751 -0
  45. trilogy/core/models/execute.py +1177 -0
  46. trilogy/core/optimization.py +251 -0
  47. trilogy/core/optimizations/__init__.py +12 -0
  48. trilogy/core/optimizations/base_optimization.py +17 -0
  49. trilogy/core/optimizations/hide_unused_concept.py +47 -0
  50. trilogy/core/optimizations/inline_datasource.py +102 -0
  51. trilogy/core/optimizations/predicate_pushdown.py +245 -0
  52. trilogy/core/processing/README.md +94 -0
  53. trilogy/core/processing/READMEv2.md +121 -0
  54. trilogy/core/processing/VIRTUAL_UNNEST.md +30 -0
  55. trilogy/core/processing/__init__.py +0 -0
  56. trilogy/core/processing/concept_strategies_v3.py +508 -0
  57. trilogy/core/processing/constants.py +15 -0
  58. trilogy/core/processing/discovery_node_factory.py +451 -0
  59. trilogy/core/processing/discovery_utility.py +548 -0
  60. trilogy/core/processing/discovery_validation.py +167 -0
  61. trilogy/core/processing/graph_utils.py +43 -0
  62. trilogy/core/processing/node_generators/README.md +9 -0
  63. trilogy/core/processing/node_generators/__init__.py +31 -0
  64. trilogy/core/processing/node_generators/basic_node.py +160 -0
  65. trilogy/core/processing/node_generators/common.py +268 -0
  66. trilogy/core/processing/node_generators/constant_node.py +38 -0
  67. trilogy/core/processing/node_generators/filter_node.py +315 -0
  68. trilogy/core/processing/node_generators/group_node.py +213 -0
  69. trilogy/core/processing/node_generators/group_to_node.py +117 -0
  70. trilogy/core/processing/node_generators/multiselect_node.py +205 -0
  71. trilogy/core/processing/node_generators/node_merge_node.py +653 -0
  72. trilogy/core/processing/node_generators/recursive_node.py +88 -0
  73. trilogy/core/processing/node_generators/rowset_node.py +165 -0
  74. trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  75. trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +261 -0
  76. trilogy/core/processing/node_generators/select_merge_node.py +748 -0
  77. trilogy/core/processing/node_generators/select_node.py +95 -0
  78. trilogy/core/processing/node_generators/synonym_node.py +98 -0
  79. trilogy/core/processing/node_generators/union_node.py +91 -0
  80. trilogy/core/processing/node_generators/unnest_node.py +182 -0
  81. trilogy/core/processing/node_generators/window_node.py +201 -0
  82. trilogy/core/processing/nodes/README.md +28 -0
  83. trilogy/core/processing/nodes/__init__.py +179 -0
  84. trilogy/core/processing/nodes/base_node.py +519 -0
  85. trilogy/core/processing/nodes/filter_node.py +75 -0
  86. trilogy/core/processing/nodes/group_node.py +194 -0
  87. trilogy/core/processing/nodes/merge_node.py +420 -0
  88. trilogy/core/processing/nodes/recursive_node.py +46 -0
  89. trilogy/core/processing/nodes/select_node_v2.py +242 -0
  90. trilogy/core/processing/nodes/union_node.py +53 -0
  91. trilogy/core/processing/nodes/unnest_node.py +62 -0
  92. trilogy/core/processing/nodes/window_node.py +56 -0
  93. trilogy/core/processing/utility.py +823 -0
  94. trilogy/core/query_processor.py +596 -0
  95. trilogy/core/statements/README.md +35 -0
  96. trilogy/core/statements/__init__.py +0 -0
  97. trilogy/core/statements/author.py +536 -0
  98. trilogy/core/statements/build.py +0 -0
  99. trilogy/core/statements/common.py +20 -0
  100. trilogy/core/statements/execute.py +155 -0
  101. trilogy/core/table_processor.py +66 -0
  102. trilogy/core/utility.py +8 -0
  103. trilogy/core/validation/README.md +46 -0
  104. trilogy/core/validation/__init__.py +0 -0
  105. trilogy/core/validation/common.py +161 -0
  106. trilogy/core/validation/concept.py +146 -0
  107. trilogy/core/validation/datasource.py +227 -0
  108. trilogy/core/validation/environment.py +73 -0
  109. trilogy/core/validation/fix.py +256 -0
  110. trilogy/dialect/__init__.py +32 -0
  111. trilogy/dialect/base.py +1392 -0
  112. trilogy/dialect/bigquery.py +308 -0
  113. trilogy/dialect/common.py +147 -0
  114. trilogy/dialect/config.py +144 -0
  115. trilogy/dialect/dataframe.py +50 -0
  116. trilogy/dialect/duckdb.py +231 -0
  117. trilogy/dialect/enums.py +147 -0
  118. trilogy/dialect/metadata.py +173 -0
  119. trilogy/dialect/mock.py +190 -0
  120. trilogy/dialect/postgres.py +117 -0
  121. trilogy/dialect/presto.py +110 -0
  122. trilogy/dialect/results.py +89 -0
  123. trilogy/dialect/snowflake.py +129 -0
  124. trilogy/dialect/sql_server.py +137 -0
  125. trilogy/engine.py +48 -0
  126. trilogy/execution/config.py +75 -0
  127. trilogy/executor.py +568 -0
  128. trilogy/hooks/__init__.py +4 -0
  129. trilogy/hooks/base_hook.py +40 -0
  130. trilogy/hooks/graph_hook.py +139 -0
  131. trilogy/hooks/query_debugger.py +166 -0
  132. trilogy/metadata/__init__.py +0 -0
  133. trilogy/parser.py +10 -0
  134. trilogy/parsing/README.md +21 -0
  135. trilogy/parsing/__init__.py +0 -0
  136. trilogy/parsing/common.py +1069 -0
  137. trilogy/parsing/config.py +5 -0
  138. trilogy/parsing/exceptions.py +8 -0
  139. trilogy/parsing/helpers.py +1 -0
  140. trilogy/parsing/parse_engine.py +2813 -0
  141. trilogy/parsing/render.py +769 -0
  142. trilogy/parsing/trilogy.lark +540 -0
  143. trilogy/py.typed +0 -0
  144. trilogy/render.py +42 -0
  145. trilogy/scripts/README.md +9 -0
  146. trilogy/scripts/__init__.py +0 -0
  147. trilogy/scripts/agent.py +41 -0
  148. trilogy/scripts/agent_info.py +303 -0
  149. trilogy/scripts/common.py +355 -0
  150. trilogy/scripts/dependency/Cargo.lock +617 -0
  151. trilogy/scripts/dependency/Cargo.toml +39 -0
  152. trilogy/scripts/dependency/README.md +131 -0
  153. trilogy/scripts/dependency/build.sh +25 -0
  154. trilogy/scripts/dependency/src/directory_resolver.rs +177 -0
  155. trilogy/scripts/dependency/src/lib.rs +16 -0
  156. trilogy/scripts/dependency/src/main.rs +770 -0
  157. trilogy/scripts/dependency/src/parser.rs +435 -0
  158. trilogy/scripts/dependency/src/preql.pest +208 -0
  159. trilogy/scripts/dependency/src/python_bindings.rs +303 -0
  160. trilogy/scripts/dependency/src/resolver.rs +716 -0
  161. trilogy/scripts/dependency/tests/base.preql +3 -0
  162. trilogy/scripts/dependency/tests/cli_integration.rs +377 -0
  163. trilogy/scripts/dependency/tests/customer.preql +6 -0
  164. trilogy/scripts/dependency/tests/main.preql +9 -0
  165. trilogy/scripts/dependency/tests/orders.preql +7 -0
  166. trilogy/scripts/dependency/tests/test_data/base.preql +9 -0
  167. trilogy/scripts/dependency/tests/test_data/consumer.preql +1 -0
  168. trilogy/scripts/dependency.py +323 -0
  169. trilogy/scripts/display.py +512 -0
  170. trilogy/scripts/environment.py +46 -0
  171. trilogy/scripts/fmt.py +32 -0
  172. trilogy/scripts/ingest.py +471 -0
  173. trilogy/scripts/ingest_helpers/__init__.py +1 -0
  174. trilogy/scripts/ingest_helpers/foreign_keys.py +123 -0
  175. trilogy/scripts/ingest_helpers/formatting.py +93 -0
  176. trilogy/scripts/ingest_helpers/typing.py +161 -0
  177. trilogy/scripts/init.py +105 -0
  178. trilogy/scripts/parallel_execution.py +713 -0
  179. trilogy/scripts/plan.py +189 -0
  180. trilogy/scripts/run.py +63 -0
  181. trilogy/scripts/serve.py +140 -0
  182. trilogy/scripts/serve_helpers/__init__.py +41 -0
  183. trilogy/scripts/serve_helpers/file_discovery.py +142 -0
  184. trilogy/scripts/serve_helpers/index_generation.py +206 -0
  185. trilogy/scripts/serve_helpers/models.py +38 -0
  186. trilogy/scripts/single_execution.py +131 -0
  187. trilogy/scripts/testing.py +119 -0
  188. trilogy/scripts/trilogy.py +68 -0
  189. trilogy/std/__init__.py +0 -0
  190. trilogy/std/color.preql +3 -0
  191. trilogy/std/date.preql +13 -0
  192. trilogy/std/display.preql +18 -0
  193. trilogy/std/geography.preql +22 -0
  194. trilogy/std/metric.preql +15 -0
  195. trilogy/std/money.preql +67 -0
  196. trilogy/std/net.preql +14 -0
  197. trilogy/std/ranking.preql +7 -0
  198. trilogy/std/report.preql +5 -0
  199. trilogy/std/semantic.preql +6 -0
  200. trilogy/utility.py +34 -0
LICENSE.md ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2023
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ from ._preql_import_resolver import *
2
+
3
+ __doc__ = _preql_import_resolver.__doc__
4
+ if hasattr(_preql_import_resolver, "__all__"):
5
+ __all__ = _preql_import_resolver.__all__
@@ -0,0 +1,555 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytrilogy
3
+ Version: 0.3.142
4
+ Classifier: Programming Language :: Python
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: Python :: 3.9
7
+ Classifier: Programming Language :: Python :: 3.10
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Dist: pyodbc ; extra == 'odbc'
12
+ Requires-Dist: psycopg2-binary ; extra == 'postgres'
13
+ Requires-Dist: sqlalchemy-bigquery ; extra == 'bigquery'
14
+ Requires-Dist: snowflake-sqlalchemy ; extra == 'snowflake'
15
+ Requires-Dist: httpx ; extra == 'ai'
16
+ Requires-Dist: rich ; extra == 'cli'
17
+ Requires-Dist: fastapi ; extra == 'serve'
18
+ Requires-Dist: uvicorn ; extra == 'serve'
19
+ Provides-Extra: odbc
20
+ Provides-Extra: postgres
21
+ Provides-Extra: bigquery
22
+ Provides-Extra: snowflake
23
+ Provides-Extra: ai
24
+ Provides-Extra: cli
25
+ Provides-Extra: serve
26
+ License-File: LICENSE.md
27
+ Summary: Declarative, typed query language that compiles to SQL.
28
+ Description-Content-Type: text/markdown
29
+ Requires-Dist: lark
30
+ Requires-Dist: jinja2
31
+ Requires-Dist: sqlalchemy<2.0.0
32
+ Requires-Dist: networkx
33
+ Requires-Dist: pydantic
34
+ Requires-Dist: duckdb<1.4.0
35
+ Requires-Dist: pyarrow
36
+ Requires-Dist: duckdb-engine
37
+ Requires-Dist: click
38
+
39
+ # Trilogy
40
+ **SQL with superpowers for analytics**
41
+
42
+ [![Website](https://img.shields.io/badge/INTRO-WEB-orange?)](https://trilogydata.dev/)
43
+ [![Discord](https://img.shields.io/badge/DISCORD-CHAT-red?logo=discord)](https://discord.gg/Z4QSSuqGEd)
44
+ [![PyPI version](https://badge.fury.io/py/pytrilogy.svg)](https://badge.fury.io/py/pytrilogy)
45
+
46
+ The Trilogy language is an experiment in better SQL for analytics - a streamlined SQL that replaces tables/joins with a lightweight semantic binding layer and provides easy reuse and composability. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
47
+
48
+ [pytrilogy](https://github.com/trilogy-data/pytrilogy) is the reference implementation, written in Python.
49
+
50
+ ## What Trilogy Gives You
51
+
52
+ - **Speed** - write faster, with concise, powerful syntax
53
+ - **Efficiency** - write less SQL, and reuse what you do
54
+ - **Fearless refactoring** - change models without breaking queries
55
+ - **Testability** - built-in testing patterns with query fixtures
56
+ - **Easy to use** - for humans and LLMs alike
57
+
58
+ Trilogy is especially powerful for data consumption, providing a rich metadata layer that makes creating, interpreting, and visualizing queries easy and expressive.
59
+
60
+
61
+ We recommend starting with the studio to explore Trilogy. For integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
62
+
63
+
64
+ ## Quick Start
65
+
66
+ > [!TIP]
67
+ > **Try it now:** [Open-source studio](https://trilogydata.dev/trilogy-studio-core/) | [Interactive demo](https://trilogydata.dev/demo/) | [Documentation](https://trilogydata.dev/)
68
+
69
+ **Install**
70
+ ```bash
71
+ pip install pytrilogy
72
+ ```
73
+
74
+ **Save in hello.preql**
75
+ ```sql
76
+ const prime <- unnest([2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
77
+
78
+ def cube_plus_one(x) -> (x * x * x + 1);
79
+
80
+ WHERE
81
+ prime_cubed_plus_one % 7 = 0
82
+ SELECT
83
+ prime,
84
+ @cube_plus_one(prime) as prime_cubed_plus_one
85
+ ORDER BY
86
+ prime asc
87
+ LIMIT 10;
88
+ ```
89
+
90
+ **Run it in DuckDB**
91
+ ```bash
92
+ trilogy run hello.preql duckdb
93
+ ```
94
+
95
+ ## Trilogy is Easy to Write
96
+ For humans *and* AI. Enjoy flexible, one-shot query generation without any DB access or security risks.
97
+
98
+ (full code in the python API section.)
99
+
100
+ ```python
101
+ query = text_to_query(
102
+ executor.environment,
103
+ "number of flights by month in 2005",
104
+ Provider.OPENAI,
105
+ "gpt-5-chat-latest",
106
+ api_key,
107
+ )
108
+
109
+ # get a ready to run query
110
+ print(query)
111
+ # typical output
112
+ '''where local.dep_time.year = 2020
113
+ select
114
+ local.dep_time.month,
115
+ count(local.id2) as number_of_flights
116
+ order by
117
+ local.dep_time.month asc;'''
118
+ ```
119
+
120
+ ## Goals
121
+
122
+ Versus SQL, Trilogy aims to:
123
+
124
+ **Keep:**
125
+ - Correctness
126
+ - Accessibility
127
+
128
+ **Improve:**
129
+ - Simplicity
130
+ - Refactoring/maintainability
131
+ - Reusability/composability
132
+ - Expressivness
133
+
134
+ **Maintain:**
135
+ - Acceptable performance
136
+
137
+ ## Backend Support
138
+
139
+ | Backend | Status | Notes |
140
+ |---------|--------|-------|
141
+ | **BigQuery** | Core | Full support |
142
+ | **DuckDB** | Core | Full support |
143
+ | **Snowflake** | Core | Full support |
144
+ | **SQL Server** | Experimental | Limited testing |
145
+ | **Presto** | Experimental | Limited testing |
146
+
147
+ ## Examples
148
+
149
+ ### Hello World
150
+
151
+ Save the following code in a file named `hello.preql`
152
+
153
+ ```python
154
+ # semantic model is abstract from data
155
+
156
+ type word string; # types can be used to provide expressive metadata tags that propagate through dataflow
157
+
158
+ key sentence_id int;
159
+ property sentence_id.word_one string::word; # comments after a definition
160
+ property sentence_id.word_two string::word; # are syntactic sugar for adding
161
+ property sentence_id.word_three string::word; # a description to it
162
+
163
+ # comments in other places are just comments
164
+
165
+ # define our datasource to bind the model to data
166
+ # for most work, you can import something already defined
167
+ # testing using query fixtures is a common pattern
168
+ datasource word_one(
169
+ sentence: sentence_id,
170
+ word:word_one
171
+ )
172
+ grain(sentence_id)
173
+ query '''
174
+ select 1 as sentence, 'Hello' as word
175
+ union all
176
+ select 2, 'Bonjour'
177
+ ''';
178
+
179
+ datasource word_two(
180
+ sentence: sentence_id,
181
+ word:word_two
182
+ )
183
+ grain(sentence_id)
184
+ query '''
185
+ select 1 as sentence, 'World' as word
186
+ union all
187
+ select 2 as sentence, 'World'
188
+ ''';
189
+
190
+ datasource word_three(
191
+ sentence: sentence_id,
192
+ word:word_three
193
+ )
194
+ grain(sentence_id)
195
+ query '''
196
+ select 1 as sentence, '!' as word
197
+ union all
198
+ select 2 as sentence, '!'
199
+ ''';
200
+
201
+ def concat_with_space(x,y) -> x || ' ' || y;
202
+
203
+ # an actual select statement
204
+ # joins are automatically resolved between the 3 sources
205
+ with sentences as
206
+ select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
207
+
208
+ WHERE
209
+ sentences.sentence_id in (1,2)
210
+ SELECT
211
+ sentences.text
212
+ ;
213
+ ```
214
+
215
+ **Run it:**
216
+ ```bash
217
+ trilogy run hello.preql duckdb
218
+ ```
219
+
220
+ ![UI Preview](hello-world.png)
221
+
222
+ ### Python SDK Usage
223
+
224
+ Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
225
+
226
+ A BigQuery example, similar to the [BigQuery quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console):
227
+
228
+ ```python
229
+ from trilogy import Dialects, Environment
230
+
231
+ environment = Environment()
232
+
233
+ environment.parse('''
234
+ key name string;
235
+ key gender string;
236
+ key state string;
237
+ key year int;
238
+ key yearly_name_count int; int;
239
+
240
+ datasource usa_names(
241
+ name:name,
242
+ number:yearly_name_count,
243
+ year:year,
244
+ gender:gender,
245
+ state:state
246
+ )
247
+ address `bigquery-public-data.usa_names.usa_1910_2013`;
248
+ ''')
249
+
250
+ executor = Dialects.BIGQUERY.default_executor(environment=environment)
251
+
252
+ results = executor.execute_text('''
253
+ WHERE
254
+ name = 'Elvis'
255
+ SELECT
256
+ name,
257
+ sum(yearly_name_count) -> name_count
258
+ ORDER BY
259
+ name_count desc
260
+ LIMIT 10;
261
+ ''')
262
+
263
+ # multiple queries can result from one text batch
264
+ for row in results:
265
+ # get results for first query
266
+ answers = row.fetchall()
267
+ for x in answers:
268
+ print(x)
269
+ ```
270
+
271
+ ### LLM Usage
272
+
273
+ Connect to your favorite provider and generate queries with confidence and high accuracy.
274
+
275
+ ```python
276
+ from trilogy import Environment, Dialects
277
+ from trilogy.ai import Provider, text_to_query
278
+ import os
279
+
280
+ executor = Dialects.DUCK_DB.default_executor(
281
+ environment=Environment(working_path=Path(__file__).parent)
282
+ )
283
+
284
+ api_key = os.environ.get(OPENAI_API_KEY)
285
+ if not api_key:
286
+ raise ValueError("OPENAI_API_KEY required for gpt generation")
287
+ # load a model
288
+ executor.parse_file("flight.preql")
289
+ # create tables in the DB if needed
290
+ executor.execute_file("setup.sql")
291
+ # generate a query
292
+ query = text_to_query(
293
+ executor.environment,
294
+ "number of flights by month in 2005",
295
+ Provider.OPENAI,
296
+ "gpt-5-chat-latest",
297
+ api_key,
298
+ )
299
+
300
+ # print the generated trilogy query
301
+ print(query)
302
+ # run it
303
+ results = executor.execute_text(query)[-1].fetchall()
304
+ assert len(results) == 12
305
+
306
+ for row in results:
307
+ # all monthly flights are between 5000 and 7000
308
+ assert row[1] > 5000 and row[1] < 7000, row
309
+
310
+ ```
311
+
312
+ ### CLI Usage
313
+
314
+ Trilogy can be run through a CLI tool, also named 'trilogy'.
315
+
316
+ **Basic syntax:**
317
+ ```bash
318
+ trilogy run <cmd or path to trilogy file> <dialect>
319
+ ```
320
+
321
+ **With backend options:**
322
+ ```bash
323
+ trilogy run "key x int; datasource test_source(i:x) grain(x) address test; select x;" duckdb --path <path/to/database>
324
+ ```
325
+
326
+ **Format code:**
327
+ ```bash
328
+ trilogy fmt <path to trilogy file>
329
+ ```
330
+
331
+ #### Backend Configuration
332
+
333
+ **BigQuery:**
334
+ - Uses applicationdefault authentication (TODO: support arbitrary credential paths)
335
+ - In Python, you can pass a custom client
336
+
337
+ **DuckDB:**
338
+ - `--path` - Optional database file path
339
+
340
+ **Postgres:**
341
+ - `--host` - Database host
342
+ - `--port` - Database port
343
+ - `--username` - Username
344
+ - `--password` - Password
345
+ - `--database` - Database name
346
+
347
+ **Snowflake:**
348
+ - `--account` - Snowflake account
349
+ - `--username` - Username
350
+ - `--password` - Password
351
+
352
+ ## Config Files
353
+ The CLI can pick up default configuration from a config file in the toml format.
354
+ Detection will be recursive form parent directories of the current working directory,
355
+ including the current working directory.
356
+
357
+ This can be used to set
358
+ - default engine and arguments
359
+ - parallelism for execute for the CLI
360
+ - any startup commands to run whenever creating an executor.
361
+
362
+ ```toml
363
+ # Trilogy Configuration File
364
+ # Learn more at: https://github.com/trilogy-data/pytrilogy
365
+
366
+ [engine]
367
+ # Default dialect for execution
368
+ dialect = "duck_db"
369
+
370
+ # Parallelism level for directory execution
371
+ # parallelism = 2
372
+
373
+ # Startup scripts to run before execution
374
+ [setup]
375
+ # startup_trilogy = []
376
+ sql = ['setup/setup_dev.sql']
377
+ ```
378
+
379
+ ## More Resources
380
+
381
+ - [Interactive demo](https://trilogydata.dev/demo/)
382
+ - [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
383
+ - [Full documentation](https://trilogydata.dev/)
384
+
385
+ ## Python API Integration
386
+
387
+ ### Root Imports
388
+
389
+ Are stable and should be sufficient for executing code from Trilogy as text.
390
+
391
+ ```python
392
+ from pytrilogy import Executor, Dialect
393
+ ```
394
+
395
+ ### Authoring Imports
396
+
397
+ Are also stable, and should be used for cases which programatically generate Trilogy statements without text inputs
398
+ or need to process/transform parsed code in more complicated ways.
399
+
400
+ ```python
401
+ from pytrilogy.authoring import Concept, Function, ...
402
+ ```
403
+
404
+ ### Other Imports
405
+
406
+ Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
407
+
408
+ ## MCP/Server
409
+
410
+ Trilogy is straightforward to run as a server/MCP server; the former to generate SQL on demand and integrate into other tools, and MCP
411
+ for full interactive query loops.
412
+
413
+ This makes it easy to integrate Trilogy into existing tools or workflows.
414
+
415
+ You can see examples of both use cases in the trilogy-studio codebase [here](https://github.com/trilogy-data/trilogy-studio-core)
416
+ and install and run an MCP server directly with that codebase.
417
+
418
+ If you're interested in a more fleshed out standalone server or MCP server, please open an issue and we'll prioritize it!
419
+
420
+ ## Trilogy Syntax Reference
421
+
422
+ Not exhaustive - see [documentation](https://trilogydata.dev/) for more details.
423
+
424
+ ### Import
425
+ ```sql
426
+ import [path] as [alias];
427
+ ```
428
+
429
+ ### Concepts
430
+
431
+ **Types:**
432
+ `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval | array<[type]> | map<[type], [type]> | struct<name:[type], name:[type]>`
433
+
434
+ **Key:**
435
+ ```sql
436
+ key [name] [type];
437
+ ```
438
+
439
+ **Property:**
440
+ ```sql
441
+ property [key].[name] [type];
442
+ property x.y int;
443
+
444
+ # or multi-key
445
+ property <[key],[key]>.[name] [type];
446
+ property <x,y>.z int;
447
+ ```
448
+
449
+ **Transformation:**
450
+ ```sql
451
+ auto [name] <- [expression];
452
+ auto x <- y + 1;
453
+ ```
454
+
455
+ ### Datasource
456
+ ```sql
457
+ datasource <name>(
458
+ <column_and_concept_with_same_name>,
459
+ # or a mapping from column to concept
460
+ <column>:<concept>,
461
+ <column>:<concept>,
462
+ )
463
+ grain(<concept>, <concept>)
464
+ address <table>;
465
+
466
+ datasource orders(
467
+ order_id,
468
+ order_date,
469
+ total_rev: point_of_sale_rev,
470
+ customomer_id: customer.id
471
+ )
472
+ grain orders
473
+ address orders;
474
+
475
+ ```
476
+
477
+ ### Queries
478
+
479
+ **Basic SELECT:**
480
+ ```sql
481
+ WHERE
482
+ <concept> = <value>
483
+ SELECT
484
+ <concept>,
485
+ <concept>+1 -> <alias>,
486
+ ...
487
+ HAVING
488
+ <alias> = <value2>
489
+ ORDER BY
490
+ <concept> asc|desc
491
+ ;
492
+ ```
493
+
494
+ **CTEs/Rowsets:**
495
+ ```sql
496
+ with <alias> as
497
+ WHERE
498
+ <concept> = <value>
499
+ select
500
+ <concept>,
501
+ <concept>+1 -> <alias>,
502
+ ...
503
+
504
+ select <alias>.<concept>;
505
+ ```
506
+
507
+ ### Data Operations
508
+
509
+ **Persist to table:**
510
+ ```sql
511
+ persist <alias> as <table_name> from
512
+ <select>;
513
+ ```
514
+
515
+ **Export to file:**
516
+ ```sql
517
+ COPY INTO <TARGET_TYPE> '<target_path>' FROM SELECT
518
+ <concept>, ...
519
+ ORDER BY
520
+ <concept>, ...
521
+ ;
522
+ ```
523
+
524
+ **Show generated SQL:**
525
+ ```sql
526
+ show <select>;
527
+ ```
528
+
529
+ **Validate Model**
530
+ ```sql
531
+ validate all
532
+ validate concepts abc,def...
533
+ validate datasources abc,def...
534
+ ```
535
+
536
+
537
+ ## Contributing
538
+
539
+ Clone repository and install requirements.txt and requirements-test.txt.
540
+
541
+ Please open an issue first to discuss what you would like to change, and then create a PR against that issue.
542
+
543
+ ## Similar Projects
544
+
545
+ Trilogy combines two aspects: a semantic layer and a query language. Examples of both are linked below:
546
+
547
+ **Semantic layers** - tools for defining a metadata layer above SQL/warehouse to enable higher level abstractions:
548
+ - [MetricFlow](https://github.com/dbt-labs/metricflow)
549
+ - [Cube](https://github.com/cube-js/cube)
550
+ - [Zillion](https://github.com/totalhack/zillion)
551
+
552
+ **Better SQL** has been a popular space. We believe Trilogy takes a different approach than the following, but all are worth checking out. Please open PRs/comment for anything missed!
553
+ - [Malloy](https://github.com/malloydata/malloy)
554
+ - [Preql](https://github.com/erezsh/Preql)
555
+ - [PRQL](https://github.com/PRQL/prql)