dataface 0.2.1.dev599__py3-none-any.whl → 0.2.1.dev647__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 (88) hide show
  1. dataface/DATAFACE_SYNTAX.md +10 -15
  2. dataface/agent_api/_paths.py +54 -13
  3. dataface/agent_api/cache.py +55 -10
  4. dataface/agent_api/describe.py +16 -16
  5. dataface/agent_api/docs/yaml-reference.md +37 -16
  6. dataface/agent_api/mcp_install.py +142 -41
  7. dataface/agent_api/project_session.py +3 -1
  8. dataface/agent_api/query.py +14 -12
  9. dataface/agent_api/schema_hints.py +11 -10
  10. dataface/agent_api/validate.py +22 -18
  11. dataface/ai/agent.py +14 -8
  12. dataface/ai/llm.py +0 -17
  13. dataface/ai/mcp/server.py +22 -21
  14. dataface/ai/skills/dashboard-replicate/SKILL.md +38 -6
  15. dataface/ai/tools/__init__.py +1 -1
  16. dataface/cli/commands/chat.py +1 -1
  17. dataface/cli/commands/mcp_init.py +11 -27
  18. dataface/cli/commands/render.py +34 -13
  19. dataface/cli/filesystem_project.py +39 -0
  20. dataface/cli/main.py +25 -6
  21. dataface/core/compile/compiler.py +140 -11
  22. dataface/core/compile/config.py +89 -0
  23. dataface/core/compile/introspection.py +67 -55
  24. dataface/core/compile/merge.py +4 -2
  25. dataface/core/compile/models/cache.py +390 -0
  26. dataface/core/compile/models/chart/authored/__init__.py +16 -0
  27. dataface/core/compile/models/config.py +52 -0
  28. dataface/core/compile/models/face/authored.py +20 -0
  29. dataface/core/compile/models/query/authored.py +7 -3
  30. dataface/core/compile/models/query/normalized.py +20 -6
  31. dataface/core/compile/models/source.py +32 -0
  32. dataface/core/compile/models/style/resolved/_base.py +8 -1
  33. dataface/core/compile/models/style/resolved/_cascade.py +42 -3
  34. dataface/core/compile/models/style/theme/axis.py +15 -2
  35. dataface/core/compile/models/style/theme/board.py +8 -1
  36. dataface/core/compile/normalize_charts.py +55 -11
  37. dataface/core/compile/normalize_layout.py +71 -7
  38. dataface/core/compile/normalize_queries.py +79 -11
  39. dataface/core/compile/normalize_variables.py +73 -3
  40. dataface/core/compile/normalizer.py +57 -6
  41. dataface/core/compile/resolve/_resolve.py +177 -64
  42. dataface/core/compile/schema_renderers/completion_catalog.py +0 -2
  43. dataface/core/compile/schema_renderers/highlight_manifest.py +11 -4
  44. dataface/core/compile/schema_renderers/json_schema.py +120 -12
  45. dataface/core/compile/schema_renderers/prompt.py +38 -4
  46. dataface/core/compile/schema_renderers/vscode_schema.py +42 -22
  47. dataface/core/compile/schema_renderers/yaml_schema.py +26 -6
  48. dataface/core/compile/source_map.py +91 -10
  49. dataface/core/compile/style_cascade.py +75 -38
  50. dataface/core/compile/yaml_error_formatter.py +10 -13
  51. dataface/core/dashboard.py +43 -23
  52. dataface/core/defaults/default_config.yml +26 -0
  53. dataface/core/defaults/themes/editorial.yaml +13 -0
  54. dataface/core/defaults/themes/neon.yaml +6 -0
  55. dataface/core/defaults/themes/vivid.yaml +7 -0
  56. dataface/core/execute/_duckdb_cache_base.py +180 -70
  57. dataface/core/execute/cache_backend.py +97 -28
  58. dataface/core/execute/cache_keys.py +15 -3
  59. dataface/core/execute/executor.py +147 -103
  60. dataface/core/execute/file_source_materializer.py +4 -3
  61. dataface/core/execute/trivial_local_cache.py +108 -74
  62. dataface/core/inspect/renderer.py +1 -0
  63. dataface/core/project.py +78 -1
  64. dataface/core/registered_views/query_runner.py +16 -6
  65. dataface/core/registered_views/render_pipeline.py +7 -7
  66. dataface/core/render/chart/emitters/area.py +2 -4
  67. dataface/core/render/chart/emitters/bar.py +25 -35
  68. dataface/core/render/chart/emitters/heatmap.py +20 -1
  69. dataface/core/render/chart/emitters/line.py +2 -4
  70. dataface/core/render/chart/emitters/scatter.py +1 -1
  71. dataface/core/render/chart/features/endpoint_labels.py +46 -20
  72. dataface/core/render/chart/vl_field_maps.py +49 -60
  73. dataface/core/render/converters/html.py +4 -0
  74. dataface/core/render/dir_context.py +80 -143
  75. dataface/core/render/faces.py +14 -0
  76. dataface/core/render/nav.py +4 -3
  77. dataface/core/render/templates/page.css +7 -2
  78. dataface/core/render/templates/page.html +3 -2
  79. dataface/core/render/templates/scripts/chart_interactivity.js +18 -8
  80. dataface/core/serve/server.py +72 -39
  81. dataface/data/highlighting/face/v1.json +2 -12
  82. dataface/integrations/markdown.py +3 -1
  83. {dataface-0.2.1.dev599.dist-info → dataface-0.2.1.dev647.dist-info}/METADATA +2 -1
  84. {dataface-0.2.1.dev599.dist-info → dataface-0.2.1.dev647.dist-info}/RECORD +87 -87
  85. dataface/core/scoped_paths.py +0 -114
  86. {dataface-0.2.1.dev599.dist-info → dataface-0.2.1.dev647.dist-info}/WHEEL +0 -0
  87. {dataface-0.2.1.dev599.dist-info → dataface-0.2.1.dev647.dist-info}/entry_points.txt +0 -0
  88. {dataface-0.2.1.dev599.dist-info → dataface-0.2.1.dev647.dist-info}/licenses/LICENSE +0 -0
@@ -155,7 +155,7 @@ Reference variables inside queries with bare `{{ region }}` — no `variables.`
155
155
  charts:
156
156
  revenue_trend:
157
157
  query: revenue # Named query reference
158
- type: line # See `dft docs charts` for all 29 chart types
158
+ type: line # See `dft docs charts` for all 16 authorable chart types
159
159
  x: month
160
160
  y: total
161
161
  color: segment
@@ -531,25 +531,25 @@ charts:
531
531
  # bar/area families also accept style.orientation and style.stack
532
532
  ```
533
533
 
534
- ### Chart types (29 total)
534
+ ### Chart types (16 authorable)
535
535
 
536
536
  Set `type:` to one of:
537
537
 
538
- **Basic** (one mark per chart) `bar`, `line`, `area`, `scatter`, `pie`, `donut`, `kpi`, `table`.
538
+ **Basic** (one mark per chart) -- `bar`, `line`, `area`, `scatter`, `pie`, `donut`, `kpi`, `table`.
539
539
 
540
- **Statistical** `boxplot`, `errorbar`, `errorband`, `histogram`, `heatmap`.
540
+ **Statistical** -- `histogram`, `heatmap`.
541
541
 
542
- **Geographic** `geoshape`, `map`, `point_map`, `bubble_map`.
542
+ **Geographic** -- `geoshape`, `map`, `point_map`, `bubble_map`.
543
543
 
544
- **Marks** (direct Vega-Lite mark passthroughs) — `circle`, `square`, `tick`, `rule`, `trail`, `rect`, `arc`, `image`.
544
+ **Overlays** -- `bar`, `line`, `area`, and `scatter` accept a `layers:` field for mixed-mark or dual-axis charts (see [Combo charts](#combo-charts-barlinearea-with-layers) and [Composition](#composition)).
545
545
 
546
- **Overlays** `bar`, `line`, `area`, and `scatter` accept a `layers:` field for mixed-mark or dual-axis charts (see [Combo charts](#combo-charts-barlinearea-with-layers) and [Composition](#composition)).
546
+ **Sparklines** -- `spark_bar` (compact horizontal bars used in profiler cards).
547
547
 
548
- **Sparklines** `spark_bar` (compact horizontal bars used in profiler cards).
548
+ **Auxiliary** -- `callout` (message card with a `style.tone:` field; `message:` required).
549
549
 
550
- **Auxiliary** `callout` (message card with a `style.tone:` field; `message:` required), `auto` (auto-detect from data internal-only; not surfaced in UI dropdowns).
550
+ Note: `donut` is an internal alias for `pie` -- `donut` is accepted but normalizes to `pie` internally. `auto` is an internal sentinel (not authored). `boxplot`, `errorbar`, and `errorband` are Vega-Lite mark types that are not in the authorable surface.
551
551
 
552
- Aliases that map to underlying marks: `scatter` `circle`, `heatmap` `rect`, `pie` / `donut` `arc`, `histogram` `bar` with binning, `map` `geoshape`.
552
+ Type aliases: `scatter` uses a circle mark, `heatmap` uses rect, `pie`/`donut` use arc, `histogram` uses bar with binning, `map` maps to geoshape.
553
553
 
554
554
  ### Shared chart fields
555
555
 
@@ -709,11 +709,6 @@ color: event_count
709
709
  type: histogram
710
710
  x: amount
711
711
 
712
- # boxplot / errorbar / errorband — statistical primitives
713
- type: boxplot
714
- x: region
715
- y: latency
716
-
717
712
  # geoshape — choropleth via GeoJSON
718
713
  type: geoshape
719
714
  geo_source: us-states
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  from dataclasses import dataclass
6
7
  from pathlib import Path
7
8
 
@@ -13,15 +14,17 @@ from dataface.core.diagnostics import (
13
14
  Diagnostic,
14
15
  )
15
16
  from dataface.core.diagnostics.base import DatafaceError
16
- from dataface.core.project import FaceFile, Project
17
+ from dataface.core.project import (
18
+ FACES_SUBDIR,
19
+ FaceFile,
20
+ Project,
21
+ ProjectPath,
22
+ assert_relpath,
23
+ )
17
24
  from dataface.core.project_roots import (
18
25
  find_dft_root as find_dft_root,
19
26
  find_project_root as find_project_root,
20
27
  )
21
- from dataface.core.scoped_paths import (
22
- resolve_face_path as resolve_face_path,
23
- resolve_scoped_path as resolve_scoped_path,
24
- )
25
28
 
26
29
 
27
30
  def resolve_project_dir(project_dir: Path | None) -> Path:
@@ -72,6 +75,42 @@ def no_project_hint(project: Project) -> str:
72
75
  )
73
76
 
74
77
 
78
+ def resolve_face_path(path: Path, project: Project) -> ProjectPath:
79
+ """User/agent-supplied face path -> ProjectPath.
80
+
81
+ Absolute and ``..``-leading paths are explicit locations: they relativize
82
+ through the containment seam (``project.path_for_fspath``, a leading
83
+ ``..`` resolving against cwd for CLI shell-nav feel) and are returned
84
+ as-is — no ``faces/`` retry, even when the exact path doesn't exist. A
85
+ bare relative name (no explicit location) gets the faces-first retry: for
86
+ a relpath that doesn't exist in the project store and has no explicit
87
+ ``faces/`` prefix, retries under ``FACES_SUBDIR``. This lets a
88
+ ``search_dashboards`` ``face_path`` of ``'looker/x.yaml'`` (``faces/``
89
+ prefix stripped at search time) resolve to ``faces/looker/x.yaml``
90
+ without producing a ``faces/faces/...`` double prefix when the caller
91
+ already supplies the full ``faces/looker/x.yaml``.
92
+
93
+ Existence is checked via ``project.exists(...)`` so it works for all
94
+ ``Project`` implementations (FilesystemProject, CloudManagedProject
95
+ git-blob store, etc.) — not just the local filesystem. Paths that exist
96
+ at root resolve from root — non-faces paths like ``models/schema.sql``
97
+ are not rewritten under faces/.
98
+ """
99
+ if path.is_absolute():
100
+ return project.path_for_fspath(path)
101
+ if ".." in path.parts:
102
+ return project.path_for_fspath(Path(os.path.normpath(Path.cwd() / path)))
103
+ relpath = str(path)
104
+ assert_relpath(relpath)
105
+ if project.exists(relpath):
106
+ return project.path(relpath)
107
+ if not relpath.startswith(f"{FACES_SUBDIR}/"):
108
+ candidate = f"{FACES_SUBDIR}/{relpath}"
109
+ if project.exists(candidate):
110
+ return project.path(candidate)
111
+ return project.path(relpath) # caller reports not-found
112
+
113
+
75
114
  def resolve_face_or_error(path: Path, project: Project) -> FaceFile | Diagnostic:
76
115
  """Faces-first resolve a path to a stored ``FaceFile``, or a structured error.
77
116
 
@@ -85,21 +124,23 @@ def resolve_face_or_error(path: Path, project: Project) -> FaceFile | Diagnostic
85
124
  resolved = resolve_face_path(path, project)
86
125
  except ValueError as exc:
87
126
  return DatafaceError.from_code(ERR_INTERNAL, message=str(exc)).to_diagnostic()
88
- project_path = project.path_for_fspath(resolved)
89
- if not project_path.exists():
127
+ if not resolved.exists():
90
128
  return DatafaceError.from_code(
91
129
  ERR_FILE_NOT_FOUND,
92
- path=project_path.relpath,
130
+ path=resolved.relpath,
93
131
  hint=no_project_hint(project),
94
- ).to_diagnostic(file=project_path.relpath)
95
- if isinstance(project, FilesystemProject) and not resolved.is_file():
132
+ ).to_diagnostic(file=resolved.relpath)
133
+ if (
134
+ isinstance(project, FilesystemProject)
135
+ and not (project.root / resolved.relpath).is_file()
136
+ ):
96
137
  # A directory (or other non-file) resolves + exists on disk but read_face
97
138
  # would raise past the structured envelope. This is a filesystem-only
98
139
  # concern — a non-filesystem store's `exists()` already gates real faces.
99
140
  return DatafaceError.from_code(
100
- ERR_INTERNAL, message=f"Not a file: {project_path.relpath}"
101
- ).to_diagnostic(file=project_path.relpath)
102
- return project_path.read_face()
141
+ ERR_INTERNAL, message=f"Not a file: {resolved.relpath}"
142
+ ).to_diagnostic(file=resolved.relpath)
143
+ return resolved.read_face()
103
144
 
104
145
 
105
146
  @dataclass(frozen=True)
@@ -1,8 +1,13 @@
1
1
  """Cache constructors at the agent_api boundary."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from collections.abc import Generator
4
6
  from contextlib import contextmanager
5
7
  from pathlib import Path
8
+ from typing import TYPE_CHECKING
9
+
10
+ from dataface.core.compile.config import resolve_cache_boot
6
11
 
7
12
  # Re-exported: TrivialDuckDBCache is the declared return type of open_cache,
8
13
  # so embedders annotating a held cache reach it here without a core import.
@@ -10,29 +15,69 @@ from dataface.core.execute.trivial_local_cache import (
10
15
  TrivialDuckDBCache as TrivialDuckDBCache,
11
16
  )
12
17
 
13
- __all__ = ["TrivialDuckDBCache", "open_cache", "cache_ctx"]
18
+ if TYPE_CHECKING:
19
+ from dataface.cli.filesystem_project import FilesystemProject
20
+
21
+ __all__ = [
22
+ "TrivialDuckDBCache",
23
+ "open_cache",
24
+ "open_project_cache",
25
+ "project_cache_ctx",
26
+ ]
14
27
 
15
28
 
16
29
  def open_cache(path: Path | None) -> TrivialDuckDBCache:
17
- """Open the query-result cache.
30
+ """Open the query-result cache at an already-resolved path.
18
31
 
19
32
  ``path=None`` opens an in-memory cache — ephemeral, discarded when closed.
20
33
  A path opens (or creates, if absent) a persistent DuckDB file, reused
21
34
  across invocations. Callers own the returned cache's lifecycle (close it).
35
+ Does not consult project config — use ``open_project_cache`` for that.
22
36
  """
23
37
  return TrivialDuckDBCache(db_path=path)
24
38
 
25
39
 
26
- @contextmanager
27
- def cache_ctx(path: Path | None) -> Generator[TrivialDuckDBCache]:
28
- """Open a cache scoped to a single command invocation; closes it on exit.
40
+ def open_project_cache(
41
+ project: FilesystemProject,
42
+ *,
43
+ no_cache: bool = False,
44
+ cache_path: Path | None = None,
45
+ ) -> TrivialDuckDBCache | None:
46
+ """Open the query-result cache honoring project config, with overrides.
47
+
48
+ The caller owns the project instance. Pair this with
49
+ ``ProjectSession.from_project(project, cache=...)`` rather than
50
+ ``ProjectSession.open(project_dir, cache=...)`` — ``open()`` builds its own
51
+ project from the path, so composing it with this function constructs two.
29
52
 
30
- Use for CLI verbs and composition roots that need a cache scoped to
31
- a single command invocation. Long-lived processes (server boot, MCP
32
- server) call ``open_cache()`` directly and own the lifecycle.
53
+ Precedence: ``no_cache=True`` (the CLI's ``--no-cache``) skips the cache
54
+ entirely; ``cache_path`` (``--cache PATH``, or its ``DFT_CACHE_PATH`` env
55
+ backing) opens that persistent file. Otherwise the project's
56
+ ``dataface.yml`` ``cache:`` block picks the location — a configured ``path``
57
+ opens that file (created if absent); no path opens the zero-config
58
+ in-memory default. A project-level ``cache: false`` defaults the *cascade*
59
+ to off without closing the door on a nearer scope's opt-in, so it still
60
+ opens a store; the resolved per-query policy decides what gets written.
61
+
62
+ Returns:
63
+ ``None`` only when ``no_cache=True`` — callers pass this straight
64
+ through as ``ProjectSession(cache=...)``.
33
65
  """
34
- cache = open_cache(path)
66
+ boot = resolve_cache_boot(project, no_cache=no_cache, cache_path=cache_path)
67
+ return open_cache(boot.path) if boot.enabled else None
68
+
69
+
70
+ @contextmanager
71
+ def project_cache_ctx(
72
+ project: FilesystemProject,
73
+ *,
74
+ no_cache: bool = False,
75
+ cache_path: Path | None = None,
76
+ ) -> Generator[TrivialDuckDBCache | None]:
77
+ """Context-managed ``open_project_cache``; closes the cache on exit if opened."""
78
+ cache = open_project_cache(project, no_cache=no_cache, cache_path=cache_path)
35
79
  try:
36
80
  yield cache
37
81
  finally:
38
- cache.close()
82
+ if cache is not None:
83
+ cache.close()
@@ -198,8 +198,8 @@ def describe_face(path: Path, *, project: Project) -> DescribeFaceResult:
198
198
  ],
199
199
  )
200
200
 
201
- resolved_project_path = project.path_for_fspath(resolved)
202
- if not resolved_project_path.exists():
201
+ resolved_display = project.root / resolved.relpath
202
+ if not resolved.exists():
203
203
  hint = no_project_hint(project)
204
204
  return DescribeFaceResult(
205
205
  success=False,
@@ -207,21 +207,21 @@ def describe_face(path: Path, *, project: Project) -> DescribeFaceResult:
207
207
  errors=[
208
208
  DatafaceError.from_code(
209
209
  ERR_FILE_NOT_FOUND,
210
- path=str(resolved),
210
+ path=resolved.relpath,
211
211
  hint=hint,
212
212
  ).to_diagnostic()
213
213
  ],
214
214
  )
215
215
 
216
216
  try:
217
- result = compile_file(resolved_project_path.read_face())
217
+ result = compile_file(resolved.read_face())
218
218
  except OSError as exc:
219
219
  return DescribeFaceResult(
220
220
  success=False,
221
- path=resolved,
221
+ path=resolved_display,
222
222
  errors=[
223
223
  DatafaceError.from_code(ERR_INTERNAL, message=str(exc)).to_diagnostic(
224
- file=str(resolved)
224
+ file=resolved.relpath
225
225
  )
226
226
  ],
227
227
  )
@@ -229,7 +229,7 @@ def describe_face(path: Path, *, project: Project) -> DescribeFaceResult:
229
229
  if result.errors:
230
230
  return DescribeFaceResult(
231
231
  success=False,
232
- path=resolved,
232
+ path=resolved_display,
233
233
  errors=list(result.errors),
234
234
  )
235
235
 
@@ -237,11 +237,11 @@ def describe_face(path: Path, *, project: Project) -> DescribeFaceResult:
237
237
  if face is None:
238
238
  return DescribeFaceResult(
239
239
  success=False,
240
- path=resolved,
240
+ path=resolved_display,
241
241
  errors=[
242
242
  DatafaceError.from_code(
243
243
  ERR_INTERNAL, message="Compilation produced no face"
244
- ).to_diagnostic(file=str(resolved))
244
+ ).to_diagnostic(file=resolved.relpath)
245
245
  ],
246
246
  )
247
247
 
@@ -285,7 +285,7 @@ def describe_face(path: Path, *, project: Project) -> DescribeFaceResult:
285
285
 
286
286
  return DescribeFaceResult(
287
287
  success=True,
288
- path=resolved,
288
+ path=resolved_display,
289
289
  title=face.title or None,
290
290
  description=face.description or None,
291
291
  queries=query_descs,
@@ -339,13 +339,13 @@ def _describe_one_path(
339
339
  )
340
340
  ]
341
341
 
342
- if not resolved.is_dir():
343
- return [describe_face(resolved, project=project)]
342
+ resolved_display = project.root / resolved.relpath
343
+ if not resolved_display.is_dir():
344
+ return [describe_face(resolved_display, project=project)]
344
345
 
345
- under = str(resolved.relative_to(project.root))
346
346
  faces = sorted(
347
347
  project.root / pf.relpath
348
- for pf in project.iter_faces(under=under, recursive=True)
348
+ for pf in project.iter_faces(under=resolved.relpath, recursive=True)
349
349
  if pf.is_yaml
350
350
  and not pf.is_private
351
351
  and not (pf.parent / INSPECT_TEMPLATE_MANIFEST).exists()
@@ -354,11 +354,11 @@ def _describe_one_path(
354
354
  return [
355
355
  DescribeFaceResult(
356
356
  success=False,
357
- path=resolved,
357
+ path=resolved_display,
358
358
  errors=[
359
359
  DatafaceError.from_code(
360
360
  ERR_INTERNAL,
361
- message=f"No face files found in {resolved}",
361
+ message=f"No face files found in {resolved_display}",
362
362
  ).to_diagnostic()
363
363
  ],
364
364
  )
@@ -14,6 +14,7 @@ AuthoredFace (dataface) definition from YAML.
14
14
  | `text` | str | ✓ | Markdown text content for text-only sections. |
15
15
  | `allow_html` | bool | ✓ | Render the face's body text as raw HTML via foreignObject instead of markdown. TRUSTED-CONTENT ONLY: the HTML (including any Jinja-interpolated values) is rendered as-authored — this is NOT a security sandbox. mdsvg strips <script>/event-handlers as a best-effort guard, not a guarantee. Enable only on first-party faces you fully control. |
16
16
  | `source` | str | ✓ | Default source name for all queries in this face. Inheritable via meta.yaml cascade. |
17
+ | `cache` | [Cache](#cache) | ✓ | Cache policy for every query in this dashboard, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts the whole dashboard out. Inheritable via the meta.yaml cascade. |
17
18
  | `variables` | dict[str, [Variable](#variable) \| VariableRef] | ✓ | Variable definitions for dynamic filtering and UI controls. |
18
19
  | `queries` | dict[str, [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef] | ✓ | Named query definitions (SQL, CSV, MetricFlow, HTTP, etc.). |
19
20
  | `charts` | dict[str, [BarChart](#barchart) \| [LineChart](#linechart) \| [AreaChart](#areachart) \| [ScatterChart](#scatterchart) \| [HeatmapChart](#heatmapchart) \| [PieChart](#piechart) \| [KpiChart](#kpichart) \| [TableChart](#tablechart) \| [PointMapChart](#pointmapchart) \| [GeoshapeChart](#geoshapechart) \| [CalloutChart](#calloutchart) \| [SparkBarChart](#sparkbarchart) \| ChartRef] | ✓ | Named chart definitions. When no explicit layout is present, charts render as an implicit row layout in authored order. |
@@ -32,6 +33,14 @@ AuthoredFace (dataface) definition from YAML.
32
33
  | `extends` | str \| list[str] | ✓ | Face name(s) or relative path(s) this face inherits from, low to high priority. |
33
34
  | `auto_link` | bool | ✓ | When True, table charts with no explicit link: automatically link each row to its canonical /data/<source>/<schema>/<table>/detail/ page. Default off. Explicit link: always wins; set link: ~ to suppress per chart. |
34
35
 
36
+ <a id="cache"></a>
37
+ ## Cache
38
+ Cache policy at one scope: cache: 1h / forever / true / false (the only opt-out).
39
+
40
+ | Field | Type | Optional | Description |
41
+ |-------|------|:--------:|-------------|
42
+ | `ttl` | str \| enum: "forever" | ✓ | Expire cached results after this wall-clock age (lazy: the next read recomputes). Short-duration string: units s/m/h/d/w, m = minutes, compound allowed ('1h30m'). Omit to inherit from the parent scope; write 'forever' to override an inherited ttl with never-auto-expire (manual refresh always remains available). |
43
+
35
44
  <a id="variable"></a>
36
45
  ## Variable
37
46
  Variable definition from YAML.
@@ -67,7 +76,7 @@ Raw SQL query — the default query type.
67
76
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
68
77
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
69
78
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
70
- | `cache` | bool | ✓ | Set to false to opt this query out of result caching. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
79
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
71
80
  | `type` | enum: "sql" | ✓ | Query adapter type. |
72
81
  | `sql` | str | ✓ | SQL query string. Supports Jinja2 templates referencing variables. |
73
82
  | `setup_sql` | str | ✓ | Non-nestable SQL preamble executed before the main query (e.g., CREATE TEMP FUNCTION). |
@@ -82,7 +91,7 @@ dbt Semantic Layer (MetricFlow) query.
82
91
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
83
92
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
84
93
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
85
- | `cache` | bool | ✓ | Set to false to opt this query out of result caching. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
94
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
86
95
  | `type` | enum: "metricflow" | ✓ | Query adapter type. |
87
96
  | `metrics` | list[str] | ✓ | MetricFlow metric names to query. |
88
97
  | `dimensions` | list[str] | ✓ | MetricFlow dimensions to include in the result. |
@@ -99,7 +108,7 @@ REST API query.
99
108
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
100
109
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
101
110
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
102
- | `cache` | bool | ✓ | Set to false to opt this query out of result caching. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
111
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
103
112
  | `type` | enum: "http" | ✓ | Query adapter type. |
104
113
  | `url` | str | ✓ | HTTP endpoint URL for REST API queries. |
105
114
  | `method` | enum: "GET", "POST", "PUT", "DELETE", "PATCH" | ✓ | HTTP method (GET, POST, PUT, DELETE, PATCH). |
@@ -118,7 +127,7 @@ Inline rows embedded in YAML.
118
127
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
119
128
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
120
129
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
121
- | `cache` | bool | ✓ | Set to false to opt this query out of result caching. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
130
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
122
131
  | `type` | enum: "values" | ✓ | Query adapter type. |
123
132
  | `rows` | list[dict[str, Any]] | ✓ | Inline data rows (list of row dicts). |
124
133
  | `values` | list[list[Any]] | ✓ | Inline row-oriented data (list of lists) for the compact 'columns + values' syntax. |
@@ -133,7 +142,7 @@ dbt source schema query.
133
142
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
134
143
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
135
144
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
136
- | `cache` | bool | ✓ | Set to false to opt this query out of result caching. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
145
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
137
146
  | `type` | enum: "schema" | ✓ | Query adapter type. |
138
147
  | `schema` | str | ✓ | Schema name for schema queries (YAML key: schema). |
139
148
  | `table` | str | ✓ | Table name for schema queries. |
@@ -148,7 +157,7 @@ LookML semantic query — compiled to SQL through likeml at compile time.
148
157
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
149
158
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
150
159
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
151
- | `cache` | bool | ✓ | Set to false to opt this query out of result caching. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
160
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
152
161
  | `type` | enum: "lookml" | ✓ | Query adapter type. |
153
162
  | `explore` | str | ✓ | LookML explore name. Presence of this key infers type: lookml. |
154
163
  | `merge` | list[dict[str, Any]] | ✓ | LookML merge sources — a list of source specs. Each is either an explore spec ({explore, fields, filters?, merge_on?}) or a query reference ({query, columns, merge_on?}) for a derived source such as a table-calc wrapper. First is the primary; each secondary's `merge_on` maps its key to the primary's. Presence of this key infers type: lookml. Compiled to a FULL OUTER JOIN via likeml. |
@@ -1057,7 +1066,7 @@ Authored overlay for BoardStyle. Face-level structural dimensions. Do NOT cascad
1057
1066
 
1058
1067
  | Field | Type | Optional | Description |
1059
1068
  |-------|------|:--------:|-------------|
1060
- | `width` | float | ✓ | Maximum dashboard width in pixels. |
1069
+ | `width` | float | ✓ | Maximum dashboard width in pixels. Caps the measured layout width, setting the board's proportions. Rendered as an on-screen pixel maximum everywhere except the dft HTML page (dft serve, dft render --format html), which scales the board to its container. |
1061
1070
  | `min_height` | float | ✓ | Minimum board height in pixels. |
1062
1071
  | `margin` | float | ✓ | Board outer margin in pixels. |
1063
1072
  | `card_padding` | float | ✓ | Padding added to each card side in pixels. |
@@ -2227,7 +2236,7 @@ Authored overlay for AxisElementStyle. Axis label or title: font + padding + VL-
2227
2236
  | `padding` | float | ✓ | Padding between axis labels and ticks in pixels; None inherits from parent axis. |
2228
2237
  | `max_width` | float | ✓ | Maximum label width in pixels; None uses Vega-Lite's default (180px). |
2229
2238
  | `angle` | float | ✓ | Label rotation angle in degrees; None uses Vega-Lite's default. |
2230
- | `align` | str | ✓ | Horizontal text alignment of labels; None uses Vega-Lite's default. |
2239
+ | `align` | enum: "left", "right", "center", "inward", "outward" | ✓ | Horizontal text alignment of labels. 'left'/'right'/'center' are absolute. 'inward' hugs the plot (own-side: left on a left axis, right on a right axis); 'outward' hugs away from the plot (Vega-Lite's default growth direction). inward/outward are a no-op on an axis with no left/right edge. None uses Vega-Lite's default. |
2231
2240
  | `baseline` | str | ✓ | Vertical text baseline of labels; None uses Vega-Lite's default. |
2232
2241
  | `overlap` | [AxisLabelOverlapConfig](#axislabeloverlapconfig) | ✓ | Label overlap strategy enablement. None inherits from the theme cascade. Set individual bools to enable/disable; the resolver applies them in fixed order tilt→skip. Temporal-bucketed axes ignore this and step the label cadence instead. |
2233
2242
  | `separation` | float | ✓ | Minimum pixel separation between labels; None uses Vega-Lite's default (0px). |
@@ -2799,6 +2808,7 @@ Postgres source configuration.
2799
2808
  | `dbname` | str | | Database name (matches dbt 'dbname'). |
2800
2809
  | `user` | str | | Database user name. |
2801
2810
  | `password` | str | | Database password. |
2811
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2802
2812
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2803
2813
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2804
2814
  | `port` | int | ✓ | Database port number. |
@@ -2816,6 +2826,7 @@ Snowflake source configuration.
2816
2826
  | `user` | str | | Snowflake user name. |
2817
2827
  | `database` | str | | Snowflake database name. |
2818
2828
  | `warehouse` | str | | Snowflake virtual warehouse name. |
2829
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2819
2830
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2820
2831
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2821
2832
  | `password` | str | ✓ | Snowflake password. Omit when using OAuth or key-pair auth. |
@@ -2831,6 +2842,7 @@ BigQuery source configuration.
2831
2842
  | `type` | enum: "bigquery" | | Source type identifier. |
2832
2843
  | `project` | str | | GCP project ID. |
2833
2844
  | `dataset` | str | | BigQuery dataset name (equivalent to schema). |
2845
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2834
2846
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2835
2847
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2836
2848
  | `keyfile` | str | ✓ | Path to service account JSON key file. |
@@ -2849,6 +2861,7 @@ Redshift source configuration.
2849
2861
  | `dbname` | str | | Redshift database name. |
2850
2862
  | `user` | str | | Redshift user name. |
2851
2863
  | `password` | str | | Redshift password. |
2864
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2852
2865
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2853
2866
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2854
2867
  | `port` | int | ✓ | Redshift port number. |
@@ -2865,6 +2878,7 @@ MySQL source configuration.
2865
2878
  | `database` | str | | MySQL database name. |
2866
2879
  | `user` | str | | MySQL user name. |
2867
2880
  | `password` | str | | MySQL password. |
2881
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2868
2882
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2869
2883
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2870
2884
  | `port` | int | ✓ | MySQL port number. |
@@ -2877,6 +2891,7 @@ DuckDB source configuration.
2877
2891
  | Field | Type | Optional | Description |
2878
2892
  |-------|------|:--------:|-------------|
2879
2893
  | `type` | enum: "duckdb" | | Source type identifier. |
2894
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2880
2895
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2881
2896
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2882
2897
  | `path` | str | ✓ | DuckDB file path or ':memory:' for an in-memory database. |
@@ -2892,6 +2907,7 @@ SQLite source configuration.
2892
2907
  | `path` | str | | Path to the SQLite database file. |
2893
2908
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2894
2909
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2910
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2895
2911
 
2896
2912
  <a id="csvsourceconfig"></a>
2897
2913
  ## CsvSourceConfig
@@ -2901,6 +2917,7 @@ CSV file source configuration.
2901
2917
  |-------|------|:--------:|-------------|
2902
2918
  | `type` | enum: "csv" | | Source type identifier. |
2903
2919
  | `files` | dict[str, str] | | Mapping of table_name → file path. Each key is the SQL table name; the value is the path relative to the project root. Required and must not be empty. |
2920
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2904
2921
  | `delimiter` | str | ✓ | Field delimiter character. |
2905
2922
  | `encoding` | str | ✓ | File encoding. |
2906
2923
 
@@ -2908,19 +2925,21 @@ CSV file source configuration.
2908
2925
  ## ParquetSourceConfig
2909
2926
  Parquet file source configuration.
2910
2927
 
2911
- | Field | Type | Description |
2912
- |-------|------|-------------|
2913
- | `type` | enum: "parquet" | Source type identifier. |
2914
- | `files` | dict[str, str] | Mapping of table_name → file path. Each key is the SQL table name; the value is the path relative to the project root. Required and must not be empty. |
2928
+ | Field | Type | Optional | Description |
2929
+ |-------|------|:--------:|-------------|
2930
+ | `type` | enum: "parquet" | | Source type identifier. |
2931
+ | `files` | dict[str, str] | | Mapping of table_name → file path. Each key is the SQL table name; the value is the path relative to the project root. Required and must not be empty. |
2932
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2915
2933
 
2916
2934
  <a id="jsonsourceconfig"></a>
2917
2935
  ## JsonSourceConfig
2918
2936
  JSON file source configuration.
2919
2937
 
2920
- | Field | Type | Description |
2921
- |-------|------|-------------|
2922
- | `type` | enum: "json" | Source type identifier. |
2923
- | `files` | dict[str, str] | Mapping of table_name → file path. Each key is the SQL table name; the value is the path relative to the project root. Required and must not be empty. |
2938
+ | Field | Type | Optional | Description |
2939
+ |-------|------|:--------:|-------------|
2940
+ | `type` | enum: "json" | | Source type identifier. |
2941
+ | `files` | dict[str, str] | | Mapping of table_name → file path. Each key is the SQL table name; the value is the path relative to the project root. Required and must not be empty. |
2942
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2924
2943
 
2925
2944
  <a id="httpsourceconfig"></a>
2926
2945
  ## HttpSourceConfig
@@ -2930,6 +2949,7 @@ HTTP/REST API source configuration.
2930
2949
  |-------|------|:--------:|-------------|
2931
2950
  | `type` | enum: "http" | | Source type identifier. |
2932
2951
  | `url` | str | | Base URL for HTTP requests. |
2952
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2933
2953
  | `headers` | dict[str, str] | ✓ | Default HTTP headers (e.g. Authorization). |
2934
2954
 
2935
2955
  <a id="dbtprofilesourceconfig"></a>
@@ -2940,6 +2960,7 @@ Reference to a dbt profile.
2940
2960
  |-------|------|:--------:|-------------|
2941
2961
  | `type` | enum: "dbt_profile" | | Source type identifier. |
2942
2962
  | `profile` | str | | dbt profile name from profiles.yml. |
2963
+ | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2943
2964
  | `target` | str | ✓ | dbt target to use; defaults to the profile's default target. |
2944
2965
  | `profiles_dir` | str | ✓ | Directory containing profiles.yml, relative to the dataface project root. Use when profiles.yml is in a subdirectory (e.g. services/dbt). Resolution order: profiles_dir → $DBT_PROFILES_DIR → project root → ~/.dbt. |
2945
2966