relationalai 1.0.0a3__py3-none-any.whl → 1.0.0a4__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.
Files changed (29) hide show
  1. relationalai/config/shims.py +1 -0
  2. relationalai/semantics/__init__.py +7 -1
  3. relationalai/semantics/frontend/base.py +19 -13
  4. relationalai/semantics/frontend/core.py +30 -2
  5. relationalai/semantics/frontend/front_compiler.py +38 -11
  6. relationalai/semantics/frontend/pprint.py +1 -1
  7. relationalai/semantics/metamodel/rewriter.py +6 -2
  8. relationalai/semantics/metamodel/typer.py +70 -26
  9. relationalai/semantics/reasoners/__init__.py +11 -0
  10. relationalai/semantics/reasoners/graph/__init__.py +38 -0
  11. relationalai/semantics/reasoners/graph/core.py +9015 -0
  12. relationalai/shims/hoister.py +9 -0
  13. relationalai/shims/mm2v0.py +32 -24
  14. relationalai/tools/cli/cli.py +138 -0
  15. relationalai/tools/cli/docs.py +394 -0
  16. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/METADATA +5 -3
  17. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/RECORD +29 -24
  18. v0/relationalai/clients/exec_txn_poller.py +91 -0
  19. v0/relationalai/clients/resources/snowflake/__init__.py +2 -2
  20. v0/relationalai/clients/resources/snowflake/direct_access_resources.py +16 -10
  21. v0/relationalai/clients/resources/snowflake/snowflake.py +43 -14
  22. v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
  23. v0/relationalai/errors.py +18 -0
  24. v0/relationalai/semantics/lqp/executor.py +3 -1
  25. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
  26. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +335 -84
  27. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/WHEEL +0 -0
  28. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/entry_points.txt +0 -0
  29. {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,394 @@
1
+ """
2
+ Documentation generation and serving utilities.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import shutil
8
+ import subprocess
9
+ import webbrowser
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ import click
14
+ from rich.console import Console
15
+
16
+ console = Console()
17
+
18
+
19
+ def find_docs_dir() -> Path:
20
+ """Find the documentation source directory."""
21
+ # Try multiple possible locations
22
+ possible_paths = [
23
+ Path("docs"), # Root level
24
+ Path(__file__).parent.parent.parent.parent / "docs", # From this file
25
+ Path.cwd() / "docs", # Current working directory
26
+ ]
27
+
28
+ for path in possible_paths:
29
+ if path.exists() and (path / "package.json").exists():
30
+ return path.resolve()
31
+
32
+ raise click.ClickException(
33
+ "Documentation source directory not found. "
34
+ "Expected 'docs/' directory with package.json"
35
+ )
36
+
37
+
38
+ def has_nodejs() -> bool:
39
+ """Check if Node.js is installed."""
40
+ try:
41
+ subprocess.run(
42
+ ["node", "--version"],
43
+ capture_output=True,
44
+ text=True,
45
+ check=True,
46
+ )
47
+ return True
48
+ except (subprocess.CalledProcessError, FileNotFoundError):
49
+ return False
50
+
51
+
52
+ def has_npm() -> bool:
53
+ """Check if npm is installed."""
54
+ try:
55
+ subprocess.run(
56
+ ["npm", "--version"],
57
+ capture_output=True,
58
+ text=True,
59
+ check=True,
60
+ )
61
+ return True
62
+ except (subprocess.CalledProcessError, FileNotFoundError):
63
+ return False
64
+
65
+
66
+ def print_generation_success(output_path: Path) -> None:
67
+ """
68
+ Print success message after documentation generation.
69
+
70
+ Args:
71
+ output_path: Path to the generated documentation output directory
72
+ """
73
+ console.print("[green]✓ Documentation generated")
74
+ console.print()
75
+ console.print("[cyan]The documentation site is ready to host![/cyan]")
76
+ console.print(f"[dim]Output location: {output_path}[/dim]")
77
+ console.print()
78
+ console.print("[yellow]You can serve it locally with:[/yellow]")
79
+ console.print(" [bold]rai docs serve[/bold]")
80
+ console.print()
81
+ console.print("[yellow]Or host the dist folder directly:[/yellow]")
82
+ console.print(
83
+ "[dim]The dist folder contains a complete static website. You can copy it to any "
84
+ "web server (Nginx, Apache, Flask, etc.), upload it to a CDN, or deploy it to "
85
+ "static hosting services like GitHub Pages, Netlify, or Vercel. All files use "
86
+ "relative paths, so it works regardless of where it's hosted.[/dim]"
87
+ )
88
+
89
+
90
+ def generate_docs(
91
+ output_dir: Optional[str] = None,
92
+ docs_dir: Optional[Path] = None,
93
+ ) -> Path:
94
+ """
95
+ Generate static documentation site.
96
+
97
+ Args:
98
+ output_dir: Optional output directory (defaults to docs/dist)
99
+ docs_dir: Optional path to docs directory
100
+
101
+ Returns:
102
+ Path to the generated output directory
103
+ """
104
+ if docs_dir is None:
105
+ docs_dir = find_docs_dir()
106
+
107
+ # Check prerequisites
108
+ if not has_nodejs():
109
+ raise click.ClickException(
110
+ "Node.js is not installed. "
111
+ "Please install Node.js from https://nodejs.org/"
112
+ )
113
+
114
+ if not has_npm():
115
+ raise click.ClickException(
116
+ "npm is not installed. "
117
+ "npm should come with Node.js installation."
118
+ )
119
+
120
+ # Check if package.json exists
121
+ package_json = docs_dir / "package.json"
122
+ if not package_json.exists():
123
+ raise click.ClickException(
124
+ f"package.json not found in {docs_dir}. "
125
+ "This doesn't appear to be a valid SolidJS project."
126
+ )
127
+
128
+ # Install dependencies if node_modules doesn't exist
129
+ node_modules = docs_dir / "node_modules"
130
+ if not node_modules.exists():
131
+ console.print("[blue]Installing dependencies...[/blue]")
132
+ try:
133
+ subprocess.run(
134
+ ["npm", "install"],
135
+ cwd=docs_dir,
136
+ check=True,
137
+ stdout=subprocess.PIPE,
138
+ stderr=subprocess.PIPE,
139
+ )
140
+ console.print("[green]✓ Dependencies installed[/green]")
141
+ except subprocess.CalledProcessError as e:
142
+ error_msg = e.stderr.decode() if e.stderr else "Unknown error"
143
+ raise click.ClickException(
144
+ f"Failed to install dependencies: {error_msg}"
145
+ )
146
+
147
+ # Build the documentation
148
+ console.print("[blue]Building documentation site...[/blue]")
149
+ try:
150
+ subprocess.run(
151
+ ["npm", "run", "build"],
152
+ cwd=docs_dir,
153
+ check=True,
154
+ capture_output=True,
155
+ text=True,
156
+ )
157
+ console.print("[green]✓ Build completed successfully[/green]")
158
+ except subprocess.CalledProcessError as e:
159
+ error_msg = e.stderr if e.stderr else e.stdout if e.stdout else "Unknown error"
160
+ console.print(f"[red]Build error output:[/red]\n{error_msg}")
161
+ raise click.ClickException(f"Build failed: {error_msg}")
162
+
163
+ # Determine output directory
164
+ build_output = docs_dir / "dist"
165
+ if not build_output.exists():
166
+ raise click.ClickException(
167
+ "Build output directory not found. "
168
+ "Build may have failed."
169
+ )
170
+
171
+ # Copy to custom output directory if specified
172
+ if output_dir:
173
+ output_path = Path(output_dir).resolve()
174
+ if output_path.exists():
175
+ console.print(f"[yellow]Output directory '{output_path}' exists. Cleaning...[/yellow]")
176
+ shutil.rmtree(output_path)
177
+ shutil.copytree(build_output, output_path)
178
+ print_generation_success(output_path)
179
+ return output_path
180
+
181
+ print_generation_success(build_output)
182
+ return build_output
183
+
184
+
185
+ def serve_docs(
186
+ build_dir: str,
187
+ host: str = "127.0.0.1",
188
+ port: int = 8000,
189
+ open_browser: bool = True,
190
+ ) -> None:
191
+ """
192
+ Serve generated documentation site.
193
+
194
+ Args:
195
+ build_dir: Directory containing built documentation
196
+ host: Host to bind to
197
+ port: Port to serve on
198
+ open_browser: Whether to open browser automatically
199
+ """
200
+ build_path = Path(build_dir).resolve()
201
+
202
+ if not build_path.exists():
203
+ raise click.ClickException(
204
+ "Build directory does not exist. "
205
+ "Run 'rai docs generate' first."
206
+ )
207
+
208
+ index_html = build_path / "index.html"
209
+ if not index_html.exists():
210
+ raise click.ClickException(
211
+ "index.html not found. "
212
+ "This doesn't appear to be a valid build directory. "
213
+ "Run 'rai docs generate' first."
214
+ )
215
+
216
+ # Import FastAPI here to avoid requiring it if not using server
217
+ try:
218
+ from fastapi import FastAPI
219
+ from fastapi.staticfiles import StaticFiles
220
+ import uvicorn
221
+ except ImportError:
222
+ raise click.ClickException(
223
+ "FastAPI and uvicorn are required for serving documentation. "
224
+ "Install with: pip install fastapi uvicorn"
225
+ )
226
+
227
+ app = FastAPI(title="RAI Documentation")
228
+ app.mount("/", StaticFiles(directory=str(build_path), html=True), name="static")
229
+
230
+ url = f"http://{host}:{port}"
231
+ console.print(f"[green]✓ Documentation server running at {url}[/green]")
232
+ console.print("[yellow]Press Ctrl+C to stop the server[/yellow]")
233
+
234
+ if open_browser:
235
+ try:
236
+ webbrowser.open(url)
237
+ except Exception:
238
+ console.print(f"[yellow]Could not open browser automatically. Visit {url}[/yellow]")
239
+
240
+ try:
241
+ uvicorn.run(app, host=host, port=port, log_level="warning")
242
+ except KeyboardInterrupt:
243
+ console.print("\n[yellow]Server stopped[/yellow]")
244
+ except OSError as e:
245
+ if "Address already in use" in str(e):
246
+ raise click.ClickException(
247
+ f"Port {port} is already in use. "
248
+ "Try a different port with --port option."
249
+ )
250
+ raise
251
+
252
+
253
+ def dev_docs(
254
+ docs_dir: Optional[Path] = None,
255
+ host: str = "127.0.0.1",
256
+ port: int = 5173,
257
+ open_browser: bool = True,
258
+ ) -> None:
259
+ """
260
+ Start development server with hot module replacement.
261
+
262
+ This runs Vite's dev server, which provides:
263
+ - Hot module replacement (HMR) - changes reflect instantly
264
+ - Fast refresh - no need to rebuild on every change
265
+ - Source maps for debugging
266
+
267
+ Args:
268
+ docs_dir: Optional path to docs directory
269
+ host: Host to bind to
270
+ port: Port to serve on (default: 5173, Vite's default)
271
+ open_browser: Whether to open browser automatically
272
+ """
273
+ if docs_dir is None:
274
+ docs_dir = find_docs_dir()
275
+
276
+ # Check prerequisites
277
+ if not has_nodejs():
278
+ raise click.ClickException(
279
+ "Node.js is not installed. "
280
+ "Please install Node.js from https://nodejs.org/"
281
+ )
282
+
283
+ if not has_npm():
284
+ raise click.ClickException(
285
+ "npm is not installed. "
286
+ "npm should come with Node.js installation."
287
+ )
288
+
289
+ # Check if package.json exists
290
+ package_json = docs_dir / "package.json"
291
+ if not package_json.exists():
292
+ raise click.ClickException(
293
+ f"package.json not found in {docs_dir}. "
294
+ "This doesn't appear to be a valid SolidJS project."
295
+ )
296
+
297
+ # Install dependencies if node_modules doesn't exist
298
+ node_modules = docs_dir / "node_modules"
299
+ if not node_modules.exists():
300
+ console.print("[blue]Installing dependencies...[/blue]")
301
+ try:
302
+ subprocess.run(
303
+ ["npm", "install"],
304
+ cwd=docs_dir,
305
+ check=True,
306
+ stdout=subprocess.PIPE,
307
+ stderr=subprocess.PIPE,
308
+ )
309
+ console.print("[green]✓ Dependencies installed[/green]")
310
+ except subprocess.CalledProcessError as e:
311
+ error_msg = e.stderr.decode() if e.stderr else "Unknown error"
312
+ raise click.ClickException(
313
+ f"Failed to install dependencies: {error_msg}"
314
+ )
315
+
316
+ # Start dev server
317
+ url = f"http://{host}:{port}"
318
+ console.print("[green]✓ Starting development server...[/green]")
319
+ console.print(f"[blue]Server will be available at {url}[/blue]")
320
+ console.print("[yellow]Press Ctrl+C to stop the server[/yellow]")
321
+ console.print(
322
+ "[dim]Note: Changes to files will automatically reload in the browser[/dim]"
323
+ )
324
+
325
+ if open_browser:
326
+ # Open browser after a short delay to let server start
327
+ import threading
328
+ import time
329
+
330
+ def open_browser_delayed():
331
+ time.sleep(1.5) # Give server time to start
332
+ try:
333
+ webbrowser.open(url)
334
+ except Exception:
335
+ console.print(f"[yellow]Could not open browser automatically. Visit {url}[/yellow]")
336
+
337
+ threading.Thread(target=open_browser_delayed, daemon=True).start()
338
+
339
+ try:
340
+ # Run npm run dev with custom host/port
341
+ # Vite accepts --host and --port flags directly
342
+ subprocess.run(
343
+ ["npm", "run", "dev", "--", "--host", host, "--port", str(port)],
344
+ cwd=docs_dir,
345
+ check=True,
346
+ )
347
+ except KeyboardInterrupt:
348
+ console.print("\n[yellow]Development server stopped[/yellow]")
349
+ except subprocess.CalledProcessError as e:
350
+ error_msg = e.stderr.decode() if e.stderr else e.stdout.decode() if e.stdout else "Unknown error"
351
+ raise click.ClickException(f"Dev server failed: {error_msg}")
352
+ except OSError as e:
353
+ if "Address already in use" in str(e):
354
+ raise click.ClickException(
355
+ f"Port {port} is already in use. "
356
+ "Try a different port with --port option."
357
+ )
358
+ raise
359
+
360
+
361
+ def cleanup_docs(docs_dir: Optional[Path] = None) -> None:
362
+ """
363
+ Clean up generated files and dependencies.
364
+
365
+ Silently removes:
366
+ - node_modules/ directory
367
+ - dist/ directory (build output)
368
+
369
+ Args:
370
+ docs_dir: Optional path to docs directory
371
+ """
372
+ if docs_dir is None:
373
+ docs_dir = find_docs_dir()
374
+
375
+ removed_items = []
376
+
377
+ # Remove node_modules
378
+ node_modules = docs_dir / "node_modules"
379
+ if node_modules.exists() and node_modules.is_dir():
380
+ try:
381
+ shutil.rmtree(node_modules)
382
+ removed_items.append("node_modules")
383
+ except Exception as e:
384
+ raise click.ClickException(f"Failed to remove node_modules: {e}")
385
+
386
+ # Remove dist
387
+ dist = docs_dir / "dist"
388
+ if dist.exists() and dist.is_dir():
389
+ try:
390
+ shutil.rmtree(dist)
391
+ removed_items.append("dist")
392
+ except Exception as e:
393
+ raise click.ClickException(f"Failed to remove dist: {e}")
394
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relationalai
3
- Version: 1.0.0a3
3
+ Version: 1.0.0a4
4
4
  Summary: RelationalAI Library and CLI
5
5
  Author-email: RelationalAI <support@relational.ai>
6
6
  Requires-Python: >=3.10
@@ -15,10 +15,9 @@ Requires-Dist: numpy<2
15
15
  Requires-Dist: pandas
16
16
  Requires-Dist: colorama
17
17
  Requires-Dist: inquirerpy
18
- Requires-Dist: click==8.2.1
18
+ Requires-Dist: click
19
19
  Requires-Dist: gravis
20
20
  Requires-Dist: toml
21
- Requires-Dist: tomlkit
22
21
  Requires-Dist: requests
23
22
  Requires-Dist: pyarrow
24
23
  Requires-Dist: websockets
@@ -35,6 +34,9 @@ Requires-Dist: sqlglot
35
34
  Requires-Dist: pyyaml
36
35
  Requires-Dist: pydantic<2.12.0,>=2.0.0
37
36
  Requires-Dist: pydantic-settings<2.11.0,>=2.0.0
37
+ Requires-Dist: tomlkit
38
+ Requires-Dist: fastapi
39
+ Requires-Dist: uvicorn[standard]
38
40
  Provides-Extra: dev
39
41
  Requires-Dist: pytest; extra == "dev"
40
42
  Requires-Dist: pytest-cov; extra == "dev"
@@ -2,7 +2,7 @@ relationalai/__init__.py,sha256=hHhZc1h-IDj0pzPF67763g0a4Pdh7W-4GQpyqL60dec,152
2
2
  relationalai/config/__init__.py,sha256=QL7MxBN7xy50h4Sqg3jotuyg-6EisCEtJCHCWQrL1ME,1524
3
3
  relationalai/config/config.py,sha256=CUBkOYWu3IgCAa4oHVwEozfMbZgAX3hENo-Va2gsdIs,10477
4
4
  relationalai/config/config_fields.py,sha256=Gskc1Ugsi4xowepDlma1HNaYdQ_wS2RiMpUaG6aNdv4,4081
5
- relationalai/config/shims.py,sha256=Xoqvie0Rlc7Qr3FvipPdcDiGwHJmFk3m5TQ_vNRPZgA,14
5
+ relationalai/config/shims.py,sha256=YM3rtPNpRSrQomRw5xNSekU5Da3dcYbxxzGryQt7RME,41
6
6
  relationalai/config/connections/__init__.py,sha256=5709qbuJxqg2WedSoejpdDrXpvwowG_xDS8adoexeOA,1171
7
7
  relationalai/config/connections/base.py,sha256=9DkPLosMc9yb6uFFks_FP_e-47K5wMD14tW_cJ05s_A,759
8
8
  relationalai/config/connections/duckdb.py,sha256=PhhJXARTJf2ObQa9CYMJlZMGohMNCg879cKHvUr1M5Q,891
@@ -13,22 +13,25 @@ relationalai/config/external/dbt_models.py,sha256=Px_C5nID_3te3o1wfsYJjrKkZcTPUY
13
13
  relationalai/config/external/snowflake_converter.py,sha256=Vnaqgazd-XwZybqbOU2YK3qYf0if-KwdTORaP0xTff4,1280
14
14
  relationalai/config/external/snowflake_models.py,sha256=6hCaVMdhESxbW-HF0-s51Z4WIc97bS1FrZRKZ4DXE6A,2681
15
15
  relationalai/config/external/utils.py,sha256=IJsZUCLI4Y2erETERQKcd-Alo4wNMTi1AHkdb92Z-n0,422
16
- relationalai/semantics/__init__.py,sha256=N3rTlD4oIyIkCqTNNWWUjQbJMyEbot6XNR_NvmtiyDo,4340
16
+ relationalai/semantics/__init__.py,sha256=Td5zPCjwRbYV-UA05kXwnuL1BW-iRxiWEV6sJksrzTY,4640
17
17
  relationalai/semantics/backends/lqp/annotations.py,sha256=TUk_fuXyBWs7H4BHx-4jZGonSD8MdgPswt-D2k7QC-4,390
18
18
  relationalai/semantics/backends/sql/sql_compiler.py,sha256=p_1YjROlcmu0kwvI32qUWAmMQntp_A6H3_vgGDDLiQk,10628
19
19
  relationalai/semantics/frontend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- relationalai/semantics/frontend/base.py,sha256=yUBeXvF0cRYzKk4sTSh7Ja5PUSwEszvyuQjT1E25Cjc,65783
21
- relationalai/semantics/frontend/core.py,sha256=mwPG9HxQWrh77O5n7j_M9lt96gTyYsoUE_BFz7qcfXw,7396
22
- relationalai/semantics/frontend/front_compiler.py,sha256=q84SDS1AQllKSnZya1k8gUG_SD12zIl9QbWIU9zFliU,57403
23
- relationalai/semantics/frontend/pprint.py,sha256=IMkKwkZs3ohOIjYUIjT10poa1L39F_PrLdSjV-D4wpk,14977
20
+ relationalai/semantics/frontend/base.py,sha256=xxs0udBqK4ErwA9pPAtFXV-Ky4keEOJQ0k4gsOKrMUQ,66007
21
+ relationalai/semantics/frontend/core.py,sha256=nTFDsF18WemplUmxOeCbUTHGhrCmxWIeRjC5amKkdDE,8399
22
+ relationalai/semantics/frontend/front_compiler.py,sha256=RIa12ifKoWJ0HqwrBSiDniKF5K-AGEJ1L8VC4wOyuck,58615
23
+ relationalai/semantics/frontend/pprint.py,sha256=mc2i4MlmPpsjXWx6bO_J7lMrxUeDfO2du4eaPZi6pFU,14982
24
24
  relationalai/semantics/metamodel/__init__.py,sha256=mISE8niHeEAD6QjX8pi_apFfMIVCZ7dGeCXFGtFPTvk,364
25
25
  relationalai/semantics/metamodel/builtins.py,sha256=rmeVdjw3kDSsx7TjpnFpy8W6OrHuUV1GQwazWhaWrG0,7009
26
26
  relationalai/semantics/metamodel/metamodel.py,sha256=TgIufxJslJZkf5_RleTU19i2h0DTW6CodSYqwyVcLEc,13899
27
27
  relationalai/semantics/metamodel/metamodel_analyzer.py,sha256=tusDRO2az1yiF951SDg-cHOaNQ1M7qw1HG1cxV3YuxU,16228
28
28
  relationalai/semantics/metamodel/metamodel_compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  relationalai/semantics/metamodel/pprint.py,sha256=65t7tZTfBP2qOlKShYM8nwrbsiS3gKaCYkUJepu0a2o,15684
30
- relationalai/semantics/metamodel/rewriter.py,sha256=CEkYxsyFPF-nEHktJxwzOY6RQzo9Nr4Welhw2YDq9q8,8313
31
- relationalai/semantics/metamodel/typer.py,sha256=Q35spCuIRMGE2BslgWiyZvQWb3Z0VzCx1YF_Lzk1Sts,55268
30
+ relationalai/semantics/metamodel/rewriter.py,sha256=14T2fzoBLMvYZNOy8VjRq_NS7rwU9EECbkQXJxfdvDw,8436
31
+ relationalai/semantics/metamodel/typer.py,sha256=Acc1NiS9oCrqt9ZP_uI4GyT0sduOwmu1tS5U-3Irndo,57654
32
+ relationalai/semantics/reasoners/__init__.py,sha256=T1ysWgmX85pULUqdxZroPexY45DvfzsV3J2xJQHr3nQ,377
33
+ relationalai/semantics/reasoners/graph/__init__.py,sha256=1hFTc-a8tj4LJv7bYUSGqRCKAuSygbhqwFH25lebWDs,1356
34
+ relationalai/semantics/reasoners/graph/core.py,sha256=prUtjt8nb87Xp9z38Y6bD_iqFaYWCZLwBGhWOmCs20I,399827
32
35
  relationalai/semantics/std/__init__.py,sha256=_cbDmeCT634GtdGJ0CqgfJw6pFaJR0ajR3B_bWCr_9A,2627
33
36
  relationalai/semantics/std/aggregates.py,sha256=uCwoBLmuRBBq9lTf5ACD3yosvtp5Wi8IQUCF_dKWS80,6259
34
37
  relationalai/semantics/std/common.py,sha256=raWuvX2u4juKPU0BKtqF5xvJSDjKHF1BWQVFioBmOf8,1658
@@ -44,14 +47,15 @@ relationalai/semantics/std/strings.py,sha256=w0k06gDfyMS6GODPjhG0gl2SkJ0aH5aYezS
44
47
  relationalai/shims/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
48
  relationalai/shims/executor.py,sha256=LtpBx74mhbJ1bCUVvetDlk5iDAEi_x-wNZ6iq5WPW2s,7531
46
49
  relationalai/shims/helpers.py,sha256=1v6D-wDq3nCwDz4egdGUeleRC0fK6wfo9fIQIeQCfZA,4508
47
- relationalai/shims/hoister.py,sha256=YXXi9pWpKQ7Sd5DzRVl_78z4OYCoKzFfIMMp6OIN3EM,8797
48
- relationalai/shims/mm2v0.py,sha256=_fqEfd6b3sVwmkuozfggoopRdNeqbsrtJgj8b9Teij8,63957
50
+ relationalai/shims/hoister.py,sha256=maebjwse3GvOFCHJWgBXBTwpiqeny8DAyoFW6HtUzI8,9423
51
+ relationalai/shims/mm2v0.py,sha256=G1Gct9rtH6KF1z0yN6yZj0WFTACfeJhcbRCZTyc1eRU,64436
49
52
  relationalai/tools/debugger.py,sha256=B8QIE1-WFdfkiPoFFn1yztr9t6yf5PI32Y9Y9XzQOJk,10798
50
53
  relationalai/tools/typer_debugger.py,sha256=__oLODEBwgt6pYIn1jgIPlenFmiK7iqXfTY_mozHxXA,3182
51
54
  relationalai/tools/cli/__init__.py,sha256=jkIlp7wftUuSIlczwZD4_siT5cs6mzpsNGrQ_kMkLZ4,74
52
- relationalai/tools/cli/cli.py,sha256=aNXpFb-8GaBjv1J8qk32EeRXu4bDuBnlVsVkNasnfvw,2278
55
+ relationalai/tools/cli/cli.py,sha256=N2quQWjcgq7qqiW1WhkDGOVZZL0nfNfUc59zsUj95pY,5958
53
56
  relationalai/tools/cli/config_template.py,sha256=l52KOI3ZBkhtM9V7EQSUOEddHVdMnjl21hPnnQSwdBE,949
54
57
  relationalai/tools/cli/dev.py,sha256=1GjfjTLi4BXbV5fXKlF5g1bc8-YsA9oKOBSntquBd8g,340
58
+ relationalai/tools/cli/docs.py,sha256=GSUMHVqHMzrpYw1wOHTKfbkZW-dozRfzoKpgb2o4wvQ,12648
55
59
  relationalai/tools/cli/components/__init__.py,sha256=H91hUt3lohr6kAVGOHgTKpDgwRc2Ka4a-iw2domfm5o,143
56
60
  relationalai/tools/cli/components/progress_reader.py,sha256=mrAFI4p1T7tgDNd_S9QAF9QXuF5RlWGSg5fzQY1_MRY,56767
57
61
  relationalai/tools/cli/components/utils.py,sha256=n_st7K3B8bDLsi8rjYgilPtx7TJRcU5xZlauGUrjyv8,1700
@@ -72,7 +76,7 @@ v0/relationalai/debugging.py,sha256=AuzbwLT1s71tctuIbYNhfWY2vR_opB7ItbmlqFVHgrY,
72
76
  v0/relationalai/dependencies.py,sha256=tL113efcISkJUiDXYHmRdU_usdD7gmee-VRHA7N4EFA,16574
73
77
  v0/relationalai/docutils.py,sha256=1gVv9mk0ytdMB2W7_NvslJefmSQtTOg8LHTCDcGCjyE,1554
74
78
  v0/relationalai/dsl.py,sha256=9BdpqzDNcW4717GyxVAYIMZ7ctE_bvXS-5sNLIbXQZ0,66128
75
- v0/relationalai/errors.py,sha256=2rFE3tWDhhZ3GCzG1UNuCkns3xxsscUU6wx_3VvoVWI,95976
79
+ v0/relationalai/errors.py,sha256=bwKNODY5aROWRRW-Df2zxHk5ACxWnR0zOhOdOHO9Yi0,96831
76
80
  v0/relationalai/metagen.py,sha256=o10PNvR_myr_61DC8g6lkB093bFo9qXGUkZKgKyfXiE,26821
77
81
  v0/relationalai/metamodel.py,sha256=P1hliwHd1nYxbXON4LZeaYZD6T6pZm97HgmFBFrWyCk,32886
78
82
  v0/relationalai/rel.py,sha256=ePmAXx4NxOdsPcHNHyGH3Jkp_cB3QzfKu5p_EQSHPh0,38293
@@ -90,6 +94,7 @@ v0/relationalai/clients/__init__.py,sha256=LQ_yHsutRMpoW2mOTmOPGF8mrbP0OiV5E68t8
90
94
  v0/relationalai/clients/client.py,sha256=gk_V9KS7_MM2dLL2OCO7EPLHD9dsRwR6R-30SW8lDwU,35759
91
95
  v0/relationalai/clients/config.py,sha256=PriDlD0_VZFV0fEQng6U-F5xjGwz5gC2F5_lM159AjM,24481
92
96
  v0/relationalai/clients/direct_access_client.py,sha256=p3hqjwdiXQQytRke1wIeNRM-HlZLAxH1U6-yPhsPOdQ,6454
97
+ v0/relationalai/clients/exec_txn_poller.py,sha256=veEHerCeKIJe9X7Yjnxab-lXUI4lWBW8tmY-tgMwKwQ,3123
93
98
  v0/relationalai/clients/hash_util.py,sha256=NXVtUFgRokZz-45CwV-r5Z5KvmEXD2dh4ypNOrEWAwY,1501
94
99
  v0/relationalai/clients/local.py,sha256=vo5ikSWg38l3xQAh9yL--4sMAj_T5Tn7YEZiw7TCH08,23504
95
100
  v0/relationalai/clients/profile_polling.py,sha256=c1ixxAOKvy-dDfVqkKENwfcsvBB8jhLA1oT9RIFIrY8,2571
@@ -98,15 +103,15 @@ v0/relationalai/clients/types.py,sha256=VU2LRtlnFsBRYdokxUccrsUkEYLE9N62EUiWtA_q
98
103
  v0/relationalai/clients/util.py,sha256=Vrw_kr-Oqp_DKTkJ_rTbzpDm4_Z4h8w10T8GUyGLxbA,12323
99
104
  v0/relationalai/clients/resources/__init__.py,sha256=pymn8gB86Q3C2bVoFei0KAL8pX_U04uDY9TE4TKzTBs,260
100
105
  v0/relationalai/clients/resources/azure/azure.py,sha256=TDapfM5rLoHrPrXg5cUe827m3AO0gSqQjNid1VUlUFo,20631
101
- v0/relationalai/clients/resources/snowflake/__init__.py,sha256=9VR-hSIw4ZSEWisKcWhNEcRVBmBfueXNCTOOfLt-8rs,871
106
+ v0/relationalai/clients/resources/snowflake/__init__.py,sha256=Ofyf1RZu9GLQdvsjpHDUHEQHHVODb9vKYI4hMOxczH4,923
102
107
  v0/relationalai/clients/resources/snowflake/cache_store.py,sha256=A-qd11wcwN3TkIqvlN0_iFUU3aEjJal3T2pqFBwkkzQ,3966
103
108
  v0/relationalai/clients/resources/snowflake/cli_resources.py,sha256=xTIcCzvgbkxuNAEvzZoRpj0n-js0hZCK30q7IZXztbI,3252
104
- v0/relationalai/clients/resources/snowflake/direct_access_resources.py,sha256=Xvh1e6TxUW2dTSS-9HadrfWVKrxNQ5GikqM4yjohJkM,29849
109
+ v0/relationalai/clients/resources/snowflake/direct_access_resources.py,sha256=a_vfArYM8IAd7gAwx4VEis0KOOr2Vej7vMwY3csxGmQ,30134
105
110
  v0/relationalai/clients/resources/snowflake/engine_state_handlers.py,sha256=SQBu4GfbyABU6xrEV-koivC-ubsVrfCBTF0FEQgJM5g,12054
106
111
  v0/relationalai/clients/resources/snowflake/error_handlers.py,sha256=581G2xOihUoiPlucC_Z2FOzhKu_swdIc3uORd0yJQuA,8805
107
112
  v0/relationalai/clients/resources/snowflake/resources_factory.py,sha256=4LGd4IQ6z8hGeGlO1TIjSFJEeUNHutaB7j9q1a9rYfQ,3385
108
- v0/relationalai/clients/resources/snowflake/snowflake.py,sha256=UiSE2jbJVGbe2_UfLI9Gud9aA5SJysuVu3F0McOz95k,133046
109
- v0/relationalai/clients/resources/snowflake/use_index_poller.py,sha256=4lPMgaeuxUdWo-ds_78OJpc7pyogWhSVgGzuTzj13wE,48460
113
+ v0/relationalai/clients/resources/snowflake/snowflake.py,sha256=buPPwLR9_gX5zo9sN90rGD1qIkn1qX4yOcaXmNlOP4U,134362
114
+ v0/relationalai/clients/resources/snowflake/use_index_poller.py,sha256=AE7z2pL4QioPRDL7-O9J4KHSuxbYbRAvd886mZPpCQI,48871
110
115
  v0/relationalai/clients/resources/snowflake/use_index_resources.py,sha256=69PNWHI_uf-Aw_evfwC6j8HLVdjhp84vs8hLkjnhwbg,6462
111
116
  v0/relationalai/clients/resources/snowflake/util.py,sha256=BEnm1B1-nqqHdm41RNxblbb-zqXbtqEGGZmTdAYeN_M,13841
112
117
  v0/relationalai/early_access/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -372,7 +377,7 @@ v0/relationalai/semantics/lqp/__init__.py,sha256=XgcQZxK-zz_LqPDVtwREhsIvjTuUIt4
372
377
  v0/relationalai/semantics/lqp/builtins.py,sha256=Xst9-UxkCAwDqL-hFOby8B3GKh705oidFUjEozxn71Y,585
373
378
  v0/relationalai/semantics/lqp/compiler.py,sha256=xpGIQAqTMwLi3Vrbe79Yal7O_mfNxpBLioZjqeV2hzc,955
374
379
  v0/relationalai/semantics/lqp/constructors.py,sha256=x7G85vbJzLIXzUCMKVdJVsr4PrcUsAOzCCfvimHLMVU,2366
375
- v0/relationalai/semantics/lqp/executor.py,sha256=3tJVxFsUMdevC-HGYR2BjexC4nBoblUBSf87xlIZ_g0,21469
380
+ v0/relationalai/semantics/lqp/executor.py,sha256=OkxegnEp4_U7uslhDS0cBi8OQOg499NPYEYomWpEqlI,21550
376
381
  v0/relationalai/semantics/lqp/intrinsics.py,sha256=8kgH4ndYKBCNnoNOn7iWOU0wnMTSiov0kYaKocQ45RY,880
377
382
  v0/relationalai/semantics/lqp/ir.py,sha256=6W9mUH0W7u5eIfF1S3o33uSOfQuM3UcqEkxrxpr1X_8,1867
378
383
  v0/relationalai/semantics/lqp/model2lqp.py,sha256=J_QofcYgq_Yb5dhOOP4lXvxsxFMEGHVnaNXoCMK_ak8,38639
@@ -387,7 +392,7 @@ v0/relationalai/semantics/lqp/rewrite/__init__.py,sha256=V9ERED9qdh4VvY9Ud_M8Zn8
387
392
  v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py,sha256=zOm-OUzWBhk2iMc_eURII1-okIGJ-teDbEcJ30WDjUU,2325
388
393
  v0/relationalai/semantics/lqp/rewrite/cdc.py,sha256=cx_fpQrzkzkeAGHE_kV10nd1-0Jduws6yiIRq650f4c,10412
389
394
  v0/relationalai/semantics/lqp/rewrite/extract_common.py,sha256=johHGsS9SDDVbonMIE8Ck9rCuidllwdeJZDruW_5kQs,14705
390
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py,sha256=lDO1e5pC_Z-nYMXt5zeLBxWQUjNgkhLMiaRLYkF7ZqU,21070
395
+ v0/relationalai/semantics/lqp/rewrite/extract_keys.py,sha256=8C5te8XWU5JiLpc7WojIVKg0_wyMM-gdlxmJdHQSnQQ,22165
391
396
  v0/relationalai/semantics/lqp/rewrite/function_annotations.py,sha256=WAHpqRVBqpUIDL2MA1aBmyOTvcpH_yfdeWBS1vAEtzA,4675
392
397
  v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py,sha256=LTwMvU19FMdgCG8nJwV2wyiW8Hu9oCj-hIKKSMOYZYE,12727
393
398
  v0/relationalai/semantics/lqp/rewrite/quantify_vars.py,sha256=3l_pDMc4r-izuE274MP_ffNgZ6g7Rk5ysg3CXqKKlBU,12016
@@ -420,7 +425,7 @@ v0/relationalai/semantics/reasoners/graph/core.py,sha256=Bth2oe5ulItBChZCoFTgP-o
420
425
  v0/relationalai/semantics/reasoners/optimization/__init__.py,sha256=HZlyy3Od7ArXI_HMhWrxrQPnyZGScgiVdmMul51lx7Q,2218
421
426
  v0/relationalai/semantics/reasoners/optimization/common.py,sha256=gmJrJAPNyaBqik2_QaxjCoFPZKqdroxmNXUeoDGwOtY,3130
422
427
  v0/relationalai/semantics/reasoners/optimization/solvers_dev.py,sha256=MqCvdDk1l_XSBlHd5-XpHVfcvD8INjCNbCttaomv6SY,24517
423
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py,sha256=1RobVkhUDP0OCRBS7f6MMM8wXQD56y5WKWZEO0fFB6k,48015
428
+ v0/relationalai/semantics/reasoners/optimization/solvers_pb.py,sha256=MbubEIDJbXpe1gJ3lT_yBhVSDzA_EVuDvxRU4bLOjUk,60159
424
429
  v0/relationalai/semantics/rel/__init__.py,sha256=pMlVTC_TbQ45mP1LpzwFBBgPxpKc0H3uJDvvDXEWzvs,55
425
430
  v0/relationalai/semantics/rel/builtins.py,sha256=0M9r5GQb9CnkkbdhO8Psw9TgiZk5CC3z5RMncUa6uuM,1571
426
431
  v0/relationalai/semantics/rel/compiler.py,sha256=t7gitonfRewTk8IQ53DTqPegytwax1jGu43VgQOqBWA,43062
@@ -491,8 +496,8 @@ v0/relationalai/util/span_tracker.py,sha256=7vyvEBdI9uCj1dxVZCQT_W5vPMxIzxewrlRR
491
496
  v0/relationalai/util/spans_file_handler.py,sha256=w34WVw19ubtrjPBkKmg0Hb-9MldICmowF1OW85KCJN0,3209
492
497
  v0/relationalai/util/timeout.py,sha256=2o6BVNFnFc-B2j-i1pEkZcQbMRto9ps2emci0XwiA4I,783
493
498
  v0/relationalai/util/tracing_handler.py,sha256=wJQN52PRw8R2XB1_qd-POgfGZStrj9OFL7wa5Xr6SEM,1732
494
- relationalai-1.0.0a3.dist-info/METADATA,sha256=4hBJy9pOGv9uDUiDfIbGTGOX0M_N94_EptAIzzR7csc,1368
495
- relationalai-1.0.0a3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
496
- relationalai-1.0.0a3.dist-info/entry_points.txt,sha256=u_anMN5_VCOQNA5E2mTLT2LWc9r1i-F9yQmy3ZNAAck,91
497
- relationalai-1.0.0a3.dist-info/top_level.txt,sha256=Y9cfzWf-p2omqqmVy_98m287xf0OJbj6OB5RRdkGql4,16
498
- relationalai-1.0.0a3.dist-info/RECORD,,
499
+ relationalai-1.0.0a4.dist-info/METADATA,sha256=IBtzvgqfMTby0BJHn885Ub5UgZ9O3f_flCGqY84qCWM,1417
500
+ relationalai-1.0.0a4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
501
+ relationalai-1.0.0a4.dist-info/entry_points.txt,sha256=u_anMN5_VCOQNA5E2mTLT2LWc9r1i-F9yQmy3ZNAAck,91
502
+ relationalai-1.0.0a4.dist-info/top_level.txt,sha256=Y9cfzWf-p2omqqmVy_98m287xf0OJbj6OB5RRdkGql4,16
503
+ relationalai-1.0.0a4.dist-info/RECORD,,
@@ -0,0 +1,91 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Dict, Optional, TYPE_CHECKING
5
+
6
+ from v0.relationalai import debugging
7
+ from v0.relationalai.clients.util import poll_with_specified_overhead
8
+ from v0.relationalai.tools.cli_controls import create_progress
9
+ from v0.relationalai.util.format import format_duration
10
+
11
+ if TYPE_CHECKING:
12
+ from v0.relationalai.clients.resources.snowflake import Resources
13
+
14
+ # Polling behavior constants
15
+ POLL_OVERHEAD_RATE = 0.1 # Overhead rate for exponential backoff
16
+
17
+ # Text color constants
18
+ GREEN_COLOR = '\033[92m'
19
+ GRAY_COLOR = '\033[90m'
20
+ ENDC = '\033[0m'
21
+
22
+
23
+ class ExecTxnPoller:
24
+ """
25
+ Encapsulates the polling logic for exec_async transaction completion.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ resource: "Resources",
31
+ txn_id: str,
32
+ headers: Optional[Dict] = None,
33
+ txn_start_time: Optional[float] = None,
34
+ ):
35
+ self.res = resource
36
+ self.txn_id = txn_id
37
+ self.headers = headers or {}
38
+ self.txn_start_time = txn_start_time or time.time()
39
+
40
+ def poll(self) -> bool:
41
+ """
42
+ Poll for transaction completion with interactive progress display.
43
+
44
+ Returns:
45
+ True if transaction completed successfully, False otherwise
46
+ """
47
+
48
+ # Don't show duration summary - we handle our own completion message
49
+ with create_progress(
50
+ description="Evaluating Query...",
51
+ success_message="", # We'll handle this ourselves
52
+ leading_newline=False,
53
+ trailing_newline=False,
54
+ show_duration_summary=False,
55
+ ) as progress:
56
+ def check_status() -> bool:
57
+ """Check if transaction is complete."""
58
+ elapsed = time.time() - self.txn_start_time
59
+ # Update the main status with elapsed time
60
+ progress.update_main_status(
61
+ query_progress_message(self.txn_id, elapsed)
62
+ )
63
+ return self.res._check_exec_async_status(self.txn_id, headers=self.headers)
64
+
65
+ with debugging.span("wait", txn_id=self.txn_id):
66
+ poll_with_specified_overhead(check_status, overhead_rate=POLL_OVERHEAD_RATE)
67
+
68
+ # Calculate final duration
69
+ total_duration = time.time() - self.txn_start_time
70
+
71
+ # Update to success message with duration
72
+ progress.update_main_status(
73
+ query_complete_message(self.txn_id, total_duration)
74
+ )
75
+
76
+ return True
77
+
78
+ def query_progress_message(id: str, duration: float) -> str:
79
+ return (
80
+ # Print with whitespace to align with the end of the transaction ID
81
+ f"Evaluating Query... {format_duration(duration):>18}\n" +
82
+ f"{GRAY_COLOR}Query: {id}{ENDC}"
83
+ )
84
+
85
+ def query_complete_message(id: str, duration: float, status_header: bool = False) -> str:
86
+ return (
87
+ (f"{GREEN_COLOR}✅ " if status_header else "") +
88
+ # Print with whitespace to align with the end of the transaction ID
89
+ f"Query Complete: {format_duration(duration):>24}\n" +
90
+ f"{GRAY_COLOR}Query: {id}{ENDC}"
91
+ )
@@ -2,7 +2,7 @@
2
2
  Snowflake resources module.
3
3
  """
4
4
  # Import order matters - Resources must be imported first since other classes depend on it
5
- from .snowflake import Resources, Provider, Graph, SnowflakeClient, APP_NAME, PYREL_ROOT_DB, ExecContext, INTERNAL_ENGINE_SIZES, ENGINE_SIZES_AWS, ENGINE_SIZES_AZURE, PrimaryKey
5
+ from .snowflake import Resources, Provider, Graph, SnowflakeClient, APP_NAME, PYREL_ROOT_DB, ExecContext, INTERNAL_ENGINE_SIZES, ENGINE_SIZES_AWS, ENGINE_SIZES_AZURE, PrimaryKey, PRINT_TXN_PROGRESS_FLAG
6
6
 
7
7
  # These imports depend on Resources, so they come after
8
8
  from .cli_resources import CLIResources
@@ -14,7 +14,7 @@ __all__ = [
14
14
  'Resources', 'DirectAccessResources', 'Provider', 'Graph', 'SnowflakeClient',
15
15
  'APP_NAME', 'PYREL_ROOT_DB', 'CLIResources', 'UseIndexResources', 'ExecContext',
16
16
  'INTERNAL_ENGINE_SIZES', 'ENGINE_SIZES_AWS', 'ENGINE_SIZES_AZURE', 'PrimaryKey',
17
- 'create_resources_instance',
17
+ 'PRINT_TXN_PROGRESS_FLAG', 'create_resources_instance',
18
18
  ]
19
19
 
20
20