dataface 0.1.6.dev975__py3-none-any.whl → 0.1.6.dev1010__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 (39) hide show
  1. dataface/agent_api/describe.py +0 -5
  2. dataface/agent_api/docs/yaml-reference.md +41 -15
  3. dataface/cli/_error_format.py +4 -0
  4. dataface/cli/commands/_agent_server.py +1 -0
  5. dataface/cli/commands/init.py +2 -0
  6. dataface/cli/commands/mcp_init.py +2 -0
  7. dataface/cli/commands/render.py +12 -0
  8. dataface/cli/commands/schema.py +2 -0
  9. dataface/cli/commands/serve.py +15 -0
  10. dataface/cli/commands/skills_init.py +2 -0
  11. dataface/cli/main.py +5 -0
  12. dataface/core/compile/__init__.py +4 -6
  13. dataface/core/compile/compiler.py +74 -12
  14. dataface/core/compile/introspection.py +6 -4
  15. dataface/core/compile/models/chart/authored/_base.py +9 -0
  16. dataface/core/compile/models/chart/normalized/_flat.py +2 -3
  17. dataface/core/compile/models/query/normalized.py +1 -77
  18. dataface/core/compile/models/source.py +136 -48
  19. dataface/core/compile/models/style/resolved/_cascade.py +1 -1
  20. dataface/core/compile/normalize_charts.py +11 -4
  21. dataface/core/compile/normalize_charts_v2.py +5 -1
  22. dataface/core/compile/normalize_queries.py +390 -27
  23. dataface/core/compile/normalize_variables.py +11 -3
  24. dataface/core/compile/normalizer.py +3 -1
  25. dataface/core/compile/palette.py +10 -4
  26. dataface/core/defaults/themes/cream.yaml +2 -2
  27. dataface/core/defaults/themes/editorial.yaml +4 -4
  28. dataface/core/defaults/themes/stark.yaml +1 -1
  29. dataface/core/execute/adapters/__init__.py +4 -3
  30. dataface/core/execute/adapters/adapter_registry.py +0 -3
  31. dataface/core/project.py +11 -0
  32. dataface/core/registered_views/loader.py +37 -0
  33. dataface/core/render/board_links.py +6 -3
  34. {dataface-0.1.6.dev975.dist-info → dataface-0.1.6.dev1010.dist-info}/METADATA +3 -1
  35. {dataface-0.1.6.dev975.dist-info → dataface-0.1.6.dev1010.dist-info}/RECORD +38 -39
  36. dataface/core/execute/adapters/metricflow_adapter.py +0 -94
  37. {dataface-0.1.6.dev975.dist-info → dataface-0.1.6.dev1010.dist-info}/WHEEL +0 -0
  38. {dataface-0.1.6.dev975.dist-info → dataface-0.1.6.dev1010.dist-info}/entry_points.txt +0 -0
  39. {dataface-0.1.6.dev975.dist-info → dataface-0.1.6.dev1010.dist-info}/licenses/LICENSE +0 -0
@@ -14,7 +14,6 @@ from dataface.core.compile.models.face.normalized import Layout
14
14
  from dataface.core.compile.models.query.normalized import (
15
15
  DbtModelQuery,
16
16
  HttpQuery,
17
- MetricFlowQuery,
18
17
  Query,
19
18
  SqlQuery,
20
19
  ValuesQuery,
@@ -135,10 +134,6 @@ def _build_query_description(name: str, query: Query) -> QueryDescription:
135
134
  return QueryDescription(
136
135
  name=name, type="http", summary=f"HTTP request: {query.url}"
137
136
  )
138
- case MetricFlowQuery():
139
- return QueryDescription(
140
- name=name, type="metricflow", summary="MetricFlow query"
141
- )
142
137
  case ValuesQuery():
143
138
  return QueryDescription(name=name, type="values", summary="Inline values")
144
139
  case _:
@@ -40,7 +40,7 @@ Sources section for faces.
40
40
  | Field | Type | Optional | Description |
41
41
  |-------|------|:--------:|-------------|
42
42
  | `default` | str | ✓ | Default source name used for all queries that don't specify a source. |
43
- | `<name>` | [PostgresSourceConfig](#postgressourceconfig) \| [SnowflakeSourceConfig](#snowflakesourceconfig) \| [BigQuerySourceConfig](#bigquerysourceconfig) \| [RedshiftSourceConfig](#redshiftsourceconfig) \| [MySQLSourceConfig](#mysqlsourceconfig) \| [DuckDBSourceConfig](#duckdbsourceconfig) \| [SQLiteSourceConfig](#sqlitesourceconfig) \| [CsvSourceConfig](#csvsourceconfig) \| [ParquetSourceConfig](#parquetsourceconfig) \| [JsonSourceConfig](#jsonsourceconfig) \| [HttpSourceConfig](#httpsourceconfig) \| [DbtProfileSourceConfig](#dbtprofilesourceconfig) \| [LookMLSourceConfig](#lookmlsourceconfig) | ✓ | Additional named entry. |
43
+ | `<name>` | [PostgresSourceConfig](#postgressourceconfig) \| [SnowflakeSourceConfig](#snowflakesourceconfig) \| [BigQuerySourceConfig](#bigquerysourceconfig) \| [RedshiftSourceConfig](#redshiftsourceconfig) \| [MySQLSourceConfig](#mysqlsourceconfig) \| [DuckDBSourceConfig](#duckdbsourceconfig) \| [SQLiteSourceConfig](#sqlitesourceconfig) \| [CsvSourceConfig](#csvsourceconfig) \| [ParquetSourceConfig](#parquetsourceconfig) \| [JsonSourceConfig](#jsonsourceconfig) \| [HttpSourceConfig](#httpsourceconfig) \| [DbtProfileSourceConfig](#dbtprofilesourceconfig) | ✓ | Additional named entry. |
44
44
 
45
45
  <a id="variable"></a>
46
46
  ## Variable
@@ -114,6 +114,7 @@ Authored patch for bar and histogram charts.
114
114
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
115
115
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
116
116
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
117
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
117
118
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
118
119
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
119
120
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -141,6 +142,7 @@ Authored patch for line charts.
141
142
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
142
143
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
143
144
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
145
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
144
146
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
145
147
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
146
148
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -168,6 +170,7 @@ Authored patch for area charts.
168
170
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
169
171
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
170
172
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
173
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
171
174
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
172
175
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
173
176
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -195,6 +198,7 @@ Authored patch for scatter charts.
195
198
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
196
199
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
197
200
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
201
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
198
202
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
199
203
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
200
204
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -224,6 +228,7 @@ Authored patch for heatmap charts.
224
228
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
225
229
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
226
230
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
231
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
227
232
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
228
233
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
229
234
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -252,6 +257,7 @@ Authored patch for pie and donut charts.
252
257
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
253
258
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
254
259
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
260
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
255
261
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
256
262
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
257
263
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -273,6 +279,7 @@ Authored patch for KPI (key performance indicator) charts.
273
279
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
274
280
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
275
281
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
282
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
276
283
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
277
284
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
278
285
  | `label` | str | ✓ | KPI label rendered above the headline value. |
@@ -292,6 +299,7 @@ Authored patch for table charts.
292
299
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
293
300
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
294
301
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
302
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
295
303
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
296
304
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
297
305
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -312,6 +320,7 @@ Authored patch for point_map and bubble_map charts.
312
320
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
313
321
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
314
322
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
323
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
315
324
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
316
325
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
317
326
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -339,6 +348,7 @@ Authored patch for map and geoshape charts.
339
348
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
340
349
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
341
350
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
351
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
342
352
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
343
353
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
344
354
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -365,6 +375,7 @@ Authored patch for layered multi-mark charts.
365
375
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
366
376
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
367
377
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
378
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
368
379
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
369
380
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
370
381
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -399,6 +410,7 @@ Authored patch for spark_bar charts (compact horizontal bars).
399
410
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
400
411
  | `query` | str \| [Query](#query) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
401
412
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
413
+ | `model` | str | ✓ | Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`. |
402
414
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
403
415
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
404
416
  | `title` | str | ✓ | Chart title displayed above the chart (not used on type: kpi). |
@@ -2613,6 +2625,8 @@ Postgres source configuration.
2613
2625
  | `dbname` | str | | Database name (matches dbt 'dbname'). |
2614
2626
  | `user` | str | | Database user name. |
2615
2627
  | `password` | str | | Database password. |
2628
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2629
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2616
2630
  | `port` | int | ✓ | Database port number. |
2617
2631
  | `schema` | str | ✓ | Default schema for queries. |
2618
2632
  | `sslmode` | enum: "disable", "allow", "prefer", "require", "verify-ca", "verify-full" | ✓ | libpq SSL mode forwarded to psycopg2. None lets libpq decide its default. |
@@ -2628,6 +2642,8 @@ Snowflake source configuration.
2628
2642
  | `user` | str | | Snowflake user name. |
2629
2643
  | `database` | str | | Snowflake database name. |
2630
2644
  | `warehouse` | str | | Snowflake virtual warehouse name. |
2645
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2646
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2631
2647
  | `password` | str | ✓ | Snowflake password. Omit when using OAuth or key-pair auth. |
2632
2648
  | `schema` | str | ✓ | Default schema for queries. |
2633
2649
  | `role` | str | ✓ | Snowflake role to assume for the session. |
@@ -2641,6 +2657,8 @@ BigQuery source configuration.
2641
2657
  | `type` | enum: "bigquery" | | Source type identifier. |
2642
2658
  | `project` | str | | GCP project ID. |
2643
2659
  | `dataset` | str | | BigQuery dataset name (equivalent to schema). |
2660
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2661
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2644
2662
  | `keyfile` | str | ✓ | Path to service account JSON key file. |
2645
2663
  | `keyfile_json` | dict[str, Any] | ✓ | Inline service account JSON dict. |
2646
2664
  | `location` | str | ✓ | Dataset location (e.g. US, EU). |
@@ -2657,6 +2675,8 @@ Redshift source configuration.
2657
2675
  | `dbname` | str | | Redshift database name. |
2658
2676
  | `user` | str | | Redshift user name. |
2659
2677
  | `password` | str | | Redshift password. |
2678
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2679
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2660
2680
  | `port` | int | ✓ | Redshift port number. |
2661
2681
  | `schema` | str | ✓ | Default schema for queries. |
2662
2682
 
@@ -2671,6 +2691,8 @@ MySQL source configuration.
2671
2691
  | `database` | str | | MySQL database name. |
2672
2692
  | `user` | str | | MySQL user name. |
2673
2693
  | `password` | str | | MySQL password. |
2694
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2695
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2674
2696
  | `port` | int | ✓ | MySQL port number. |
2675
2697
  | `schema` | str | ✓ | Default schema for queries. |
2676
2698
 
@@ -2681,6 +2703,8 @@ DuckDB source configuration.
2681
2703
  | Field | Type | Optional | Description |
2682
2704
  |-------|------|:--------:|-------------|
2683
2705
  | `type` | enum: "duckdb" | | Source type identifier. |
2706
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2707
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2684
2708
  | `path` | str | ✓ | DuckDB file path or ':memory:' for an in-memory database. |
2685
2709
  | `schema` | str | ✓ | Default schema for unqualified table names (sets search_path). |
2686
2710
 
@@ -2688,10 +2712,12 @@ DuckDB source configuration.
2688
2712
  ## SQLiteSourceConfig
2689
2713
  SQLite source configuration.
2690
2714
 
2691
- | Field | Type | Description |
2692
- |-------|------|-------------|
2693
- | `type` | enum: "sqlite" | Source type identifier. |
2694
- | `path` | str | Path to the SQLite database file. |
2715
+ | Field | Type | Optional | Description |
2716
+ |-------|------|:--------:|-------------|
2717
+ | `type` | enum: "sqlite" | | Source type identifier. |
2718
+ | `path` | str | | Path to the SQLite database file. |
2719
+ | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2720
+ | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2695
2721
 
2696
2722
  <a id="csvsourceconfig"></a>
2697
2723
  ## CsvSourceConfig
@@ -2743,16 +2769,6 @@ Reference to a dbt profile.
2743
2769
  | `target` | str | ✓ | dbt target to use; defaults to the profile's default target. |
2744
2770
  | `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. |
2745
2771
 
2746
- <a id="lookmlsourceconfig"></a>
2747
- ## LookMLSourceConfig
2748
- Reference to a directory of LookML (.lkml) model files.
2749
-
2750
- | Field | Type | Description |
2751
- |-------|------|-------------|
2752
- | `type` | enum: "lookml" | Source type identifier. |
2753
- | `path` | str | Relative path to the directory of .lkml files, relative to the project root. |
2754
- | `base_source` | str | Name of an existing Dataface warehouse source to execute compiled queries against. |
2755
-
2756
2772
  <a id="variableref"></a>
2757
2773
  ## VariableRef
2758
2774
  Cross-file variable reference. Bare string is coerced automatically.
@@ -2776,3 +2792,13 @@ Cross-file chart reference. Bare string is coerced automatically.
2776
2792
  | Field | Type | Description |
2777
2793
  |-------|------|-------------|
2778
2794
  | `ref` | str | Reference path: '&lt;file&gt;.charts.&lt;name&gt;'. A bare string value is coerced automatically. |
2795
+
2796
+ <a id="lookmlmodelconfig"></a>
2797
+ ## LookMLModelConfig
2798
+ One LookML model: a named collection of explores under a warehouse source.
2799
+
2800
+ | Field | Type | Optional | Description |
2801
+ |-------|------|:--------:|-------------|
2802
+ | `lkml` | str | ✓ | Path to a specific .lkml model file, relative to the project root. |
2803
+ | `group_label` | str | ✓ | LookML group_label selecting this model's explores. Not yet enforced at compile time. |
2804
+ | `explores` | list[str] | ✓ | Explicit list of explore names in this model. |
@@ -17,7 +17,11 @@ from typing import Literal
17
17
  from rich.panel import Panel
18
18
 
19
19
  from dataface.cli._console import dft_console
20
+
21
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
20
22
  from dataface.core.errors import StructuredError
23
+
24
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
21
25
  from dataface.core.warnings.base import RenderWarning
22
26
 
23
27
 
@@ -21,6 +21,7 @@ from dataclasses import dataclass
21
21
 
22
22
  import uvicorn
23
23
 
24
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
24
25
  from dataface.core.serve.embedded import build_embedded_server
25
26
 
26
27
  # Seconds to wait for uvicorn to flip ``started``. A real bind failure surfaces
@@ -9,6 +9,8 @@ from pathlib import Path
9
9
  import typer
10
10
 
11
11
  from dataface.agent_api.init import init_project
12
+
13
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
12
14
  from dataface.core.project_roots import find_dft_root, find_repo_root
13
15
 
14
16
 
@@ -10,6 +10,8 @@ from pathlib import Path
10
10
  import typer
11
11
 
12
12
  from dataface.agent_api import mcp_install
13
+
14
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
13
15
  from dataface.core.project_roots import (
14
16
  DFT_ROOT_MARKERS,
15
17
  find_dft_root,
@@ -14,11 +14,23 @@ from dataface.agent_api._paths import (
14
14
  from dataface.agent_api.cache import cache_ctx
15
15
  from dataface.agent_api.dashboards import RenderedDashboard
16
16
  from dataface.cli._error_format import print_structured_errors
17
+
18
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
17
19
  from dataface.core.dashboard import RenderFormat
20
+
21
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
18
22
  from dataface.core.errors import StructuredError
23
+
24
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
19
25
  from dataface.core.render.warnings import registry as _warnings_registry
26
+
27
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
20
28
  from dataface.core.warnings import unreferenced_chart
29
+
30
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
21
31
  from dataface.core.warnings.base import RenderWarning
32
+
33
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
22
34
  from dataface.core.warnings.from_query_diagnostic import KNOWN_RENDER_CODES
23
35
 
24
36
  # Compile-time warning producers don't live on detector modules in the registry,
@@ -33,6 +33,8 @@ from dataface.agent_api.schema import SchemaResponse, is_exact_target
33
33
  from dataface.agent_api.schema_search import SchemaSearchResult
34
34
  from dataface.cli._error_format import print_structured_errors
35
35
  from dataface.cli._parsing import parse_kv_pairs
36
+
37
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
36
38
  from dataface.core.errors import DF_UNKNOWN_INTERNAL, StructuredError
37
39
 
38
40
 
@@ -8,7 +8,11 @@ import typer
8
8
  import uvicorn
9
9
 
10
10
  from dataface.cli._error_format import print_structured_errors
11
+
12
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
11
13
  from dataface.core.errors import DF_SERVE_STARTUP_FAILED
14
+
15
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
12
16
  from dataface.core.errors.base import DatafaceError
13
17
 
14
18
 
@@ -56,11 +60,22 @@ def serve_command(
56
60
  Returns:
57
61
  None (exits with code 0 on success, 1 on errors)
58
62
  """
63
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
59
64
  from dataface.core.compile.config import list_built_in_themes, load_config
65
+
66
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
60
67
  from dataface.core.errors import DF_SERVE_INVALID_DEFAULT_THEME
68
+
69
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
61
70
  from dataface.core.project import Project
71
+
72
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
62
73
  from dataface.core.project_roots import find_dft_root, infer_dialect_from_dbt
74
+
75
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
63
76
  from dataface.core.serve.port import resolve_port
77
+
78
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
64
79
  from dataface.core.serve.server import create_server
65
80
 
66
81
  # Resolve the project root: --project-dir if given, else discover from cwd.
@@ -7,6 +7,8 @@ from pathlib import Path
7
7
  import typer
8
8
 
9
9
  from dataface.agent_api import skill_install
10
+
11
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
10
12
  from dataface.core.project_roots import find_dft_root, find_repo_root
11
13
 
12
14
  _VALID_TARGETS = skill_install.SKILL_INSTALL_TARGETS
dataface/cli/main.py CHANGED
@@ -33,7 +33,11 @@ from dataface.cli.commands import (
33
33
  )
34
34
  from dataface.cli.commands.mcp_init import run_init as _mcp_run_init
35
35
  from dataface.cli.commands.skills_init import run_init_skills as _skills_run_init
36
+
37
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
36
38
  from dataface.core.dashboard import RenderFormat
39
+
40
+ # tach-ignore(cli->core debt: route via agent_api; burn-down task)
37
41
  from dataface.core.project_roots import find_dft_root
38
42
 
39
43
  # Evaluated once at process startup — mid-session env changes won't take effect.
@@ -615,6 +619,7 @@ def mcp_serve(
615
619
  }
616
620
  """
617
621
  try:
622
+ # tach-ignore(cli->ai debt: migrate lazy `dft mcp serve` import to dataface.cli._extras; burn-down task)
618
623
  from dataface.ai.mcp import run_server
619
624
  except ImportError as e:
620
625
  typer.echo("MCP server requires additional dependencies.", err=True)
@@ -90,11 +90,9 @@ from dataface.core.compile.models.query.normalized import (
90
90
  AnyQuery,
91
91
  DbtModelQuery,
92
92
  HttpQuery,
93
- MetricFlowQuery,
94
93
  SqlQuery,
95
94
  is_dbt_model_query,
96
95
  is_http_query,
97
- is_metricflow_query,
98
96
  is_sql_query,
99
97
  )
100
98
  from dataface.core.compile.models.source import (
@@ -105,7 +103,8 @@ from dataface.core.compile.models.source import (
105
103
  DuckDBSourceConfig,
106
104
  HttpSourceConfig,
107
105
  JsonSourceConfig,
108
- LookMLSourceConfig,
106
+ LookMLModelConfig,
107
+ LookMLModelsMixin,
109
108
  MySQLSourceConfig,
110
109
  ParquetSourceConfig,
111
110
  PostgresSourceConfig,
@@ -173,7 +172,6 @@ __all__ = [
173
172
  "ChartDependencies",
174
173
  # Unified query types
175
174
  "SqlQuery",
176
- "MetricFlowQuery",
177
175
  "HttpQuery",
178
176
  "DbtModelQuery",
179
177
  "VALID_QUERY_TYPES",
@@ -191,7 +189,8 @@ __all__ = [
191
189
  "JsonSourceConfig",
192
190
  "HttpSourceConfig",
193
191
  "DbtProfileSourceConfig",
194
- "LookMLSourceConfig",
192
+ "LookMLModelConfig",
193
+ "LookMLModelsMixin",
195
194
  "VALID_SOURCE_TYPES",
196
195
  "parse_source_config",
197
196
  "is_database_source",
@@ -213,7 +212,6 @@ __all__ = [
213
212
  "is_face",
214
213
  "is_chart",
215
214
  "is_sql_query",
216
- "is_metricflow_query",
217
215
  "is_http_query",
218
216
  "is_dbt_model_query",
219
217
  # Enums
@@ -35,7 +35,7 @@ from dataclasses import dataclass, field
35
35
  from typing import TYPE_CHECKING, Any
36
36
 
37
37
  import yaml
38
- from pydantic import ValidationError as PydanticValidationError
38
+ from pydantic import BaseModel, ValidationError as PydanticValidationError
39
39
 
40
40
  from dataface.core.compile.authoring_warnings import detect_authoring_warnings
41
41
  from dataface.core.compile.config import ProjectSourcesConfig
@@ -57,11 +57,16 @@ from dataface.core.compile.models.face.authored import (
57
57
  QueryOrRef,
58
58
  )
59
59
  from dataface.core.compile.models.face.normalized import Face
60
+ from dataface.core.compile.models.query.authored import AuthoredQuery
60
61
  from dataface.core.compile.models.query.normalized import AnyQuery
61
62
  from dataface.core.compile.models.refs import ChartRef, QueryRef, VariableRef
62
63
  from dataface.core.compile.models.variable.authored import Variable
63
64
  from dataface.core.compile.normalize_charts import collect_lookml_channel_refs
64
- from dataface.core.compile.normalize_variables import VariableReferenceErrors
65
+ from dataface.core.compile.normalize_queries import find_lookml_model_source
66
+ from dataface.core.compile.normalize_variables import (
67
+ VariableReferenceErrors,
68
+ synthetic_query_name,
69
+ )
65
70
  from dataface.core.compile.normalizer import normalize_face, normalize_query
66
71
  from dataface.core.compile.parser import (
67
72
  load_yaml_mapping,
@@ -200,23 +205,79 @@ def compile_authored_face(
200
205
  except CompilationError as e:
201
206
  return CompileResult(errors=[e.to_structured()])
202
207
 
208
+ # ════════════════════════════════════════════════════════════════════
209
+ # STEP 3b.5: Desugar chart-level `model: model.explore` sugar into a
210
+ # synthesized named lookml query, before Surface B channel collection
211
+ # (below) so the synthesized query is visible to that pass. Runs
212
+ # unconditionally (not gated on lookml_model_names) so an unknown/
213
+ # ambiguous `model:` name still raises a clear error.
214
+ # ════════════════════════════════════════════════════════════════════
215
+ try:
216
+ collectible_charts = dict(chart_registry)
217
+ _collect_inline_layout_charts(authored, collectible_charts)
218
+ if authored.queries is None:
219
+ authored.queries = {}
220
+ for _chart_key, _chart_def in collectible_charts.items():
221
+ # Use BaseModel (not _BaseChartFields) for dump so callout charts and
222
+ # other non-_BaseChartFields models are serialised to a dict first.
223
+ if isinstance(_chart_def, BaseModel):
224
+ _chart_dict = _chart_def.model_dump(exclude_none=True)
225
+ elif isinstance(_chart_def, dict):
226
+ _chart_dict = _chart_def
227
+ else:
228
+ continue
229
+ _model_value = _chart_dict.get("model")
230
+ if not _model_value:
231
+ continue
232
+ if _chart_dict.get("query") is not None:
233
+ raise CompilationError(
234
+ f"chart '{_chart_key}' sets both `model:` and `query:` — "
235
+ "`model:` is sugar for a query, so only one may be set."
236
+ )
237
+ _parts = _model_value.split(".")
238
+ if len(_parts) != 2 or not all(_parts):
239
+ raise CompilationError(
240
+ f"chart '{_chart_key}' has `model: {_model_value!r}` — expected "
241
+ "`model.explore` (a LookML model name and explore name "
242
+ "joined by a dot)."
243
+ )
244
+ _model_name, _explore_name = _parts
245
+ if find_lookml_model_source(_model_name, sources_registry) is None:
246
+ raise CompilationError(
247
+ f"chart '{_chart_key}' has `model: {_model_value}` naming "
248
+ f"unknown model '{_model_name}' — no source declares it in its "
249
+ "'models:' map."
250
+ )
251
+ _synth_name = synthetic_query_name("model_query", _chart_key)
252
+ authored.queries[_synth_name] = AuthoredQuery(
253
+ source=_model_name, explore=_explore_name
254
+ )
255
+ # Mutate in place: _BaseChartFields declares query + model; plain
256
+ # dicts use key assignment. Non-_BaseChartFields BaseModels (e.g.
257
+ # CalloutChart) have no `model:` field and are skipped above.
258
+ if isinstance(_chart_def, _BaseChartFields):
259
+ _chart_def.query = _synth_name
260
+ _chart_def.model = None
261
+ elif isinstance(_chart_def, dict):
262
+ _chart_def["query"] = _synth_name
263
+ _chart_def.pop("model", None)
264
+ except CompilationError as e:
265
+ return CompileResult(errors=[e.to_structured()])
266
+
203
267
  # ════════════════════════════════════════════════════════════════════
204
268
  # STEP 3c: Collect LookML dotted-ref chart channels into fields-less
205
269
  # lookml queries (Surface B), before query normalization lowers them.
206
270
  # ════════════════════════════════════════════════════════════════════
207
- lookml_source_names = {
208
- name
209
- for name, cfg in sources_registry.items()
210
- if isinstance(cfg, dict) and cfg.get("type") == "lookml"
211
- }
212
- if lookml_source_names:
271
+ lookml_model_names: set[str] = set()
272
+ for _cfg in sources_registry.values():
273
+ if isinstance(_cfg, dict) and isinstance(_cfg.get("models"), dict):
274
+ lookml_model_names.update(_cfg["models"].keys())
275
+ if lookml_model_names:
213
276
  try:
214
277
  raw_queries: dict[str, QueryOrRef] = {}
215
278
  _collect_raw_queries(authored, raw_queries)
216
- collectible_charts = dict(chart_registry)
217
- _collect_inline_layout_charts(authored, collectible_charts)
218
279
  collect_lookml_channel_refs(
219
- collectible_charts, raw_queries, lookml_source_names
280
+ collectible_charts, raw_queries, lookml_model_names
220
281
  )
221
282
  except CompilationError as e:
222
283
  return CompileResult(errors=[e.to_structured()])
@@ -852,6 +913,7 @@ def _process_queries_for_registry(
852
913
  query_def,
853
914
  default_source=effective_default_source,
854
915
  sources=sources,
916
+ base_dir=base_dir,
855
917
  )
856
918
 
857
919
 
@@ -1016,7 +1078,7 @@ def load_from_reference(
1016
1078
  item_def = section[item_name]
1017
1079
 
1018
1080
  if section_name == "queries":
1019
- return normalize_query(item_name, item_def)
1081
+ return normalize_query(item_name, item_def, base_dir=base_dir)
1020
1082
  if section_name == "charts":
1021
1083
  return item_def
1022
1084
  # section_name == "variables"
@@ -28,7 +28,7 @@ from dataface.core.compile.models.source import (
28
28
  DuckDBSourceConfig,
29
29
  HttpSourceConfig,
30
30
  JsonSourceConfig,
31
- LookMLSourceConfig,
31
+ LookMLModelConfig,
32
32
  MySQLSourceConfig,
33
33
  ParquetSourceConfig,
34
34
  PostgresSourceConfig,
@@ -96,8 +96,9 @@ def _unwrap_annotated(annotation: Any) -> Any:
96
96
  def _is_authored_model(cls: Any) -> bool:
97
97
  """Return True if cls should be included in the IR.
98
98
 
99
- Includes authored.* models, primitives, _PatchBase subclasses, and compiled
100
- style models from style/theme/ (so inherit-chain metadata is introspectable).
99
+ Includes authored.* models, primitives, _PatchBase subclasses, compiled
100
+ style models from style/theme/, and LookMLModelConfig so
101
+ dict[str, LookMLModelConfig] fields emit additionalProperties refs in JSON Schema.
101
102
  """
102
103
  if not (isinstance(cls, type) and issubclass(cls, BaseModel)):
103
104
  return False
@@ -109,6 +110,7 @@ def _is_authored_model(cls: Any) -> bool:
109
110
  or module.endswith("primitives")
110
111
  or module.endswith("style.theme")
111
112
  or ".style.theme." in module
113
+ or issubclass(cls, LookMLModelConfig)
112
114
  )
113
115
 
114
116
 
@@ -501,7 +503,7 @@ def introspect() -> AuthorableSchema:
501
503
  JsonSourceConfig,
502
504
  HttpSourceConfig,
503
505
  DbtProfileSourceConfig,
504
- LookMLSourceConfig,
506
+ LookMLModelConfig,
505
507
  # Compiled style root — walks style/theme/ types so inherit-chain metadata
506
508
  # (Inherit/InheritSlot markers) is visible in the schema IR.
507
509
  Style,
@@ -121,6 +121,15 @@ class _BaseChartFields(BaseModel):
121
121
  default=None, description="Click-through URL template for drill-down links."
122
122
  ),
123
123
  ]
124
+ # Semantic-layer chart sugar: normalizer desugars this into a synthesized
125
+ # named query and clears the field. Mutually exclusive with `query:`.
126
+ model: Annotated[
127
+ str | None,
128
+ Field(
129
+ default=None,
130
+ description="Semantic-layer entry point: 'model.explore' (LookML model name and explore joined by a dot). Mutually exclusive with `query:`.",
131
+ ),
132
+ ]
124
133
 
125
134
  @model_validator(mode="before")
126
135
  @classmethod
@@ -25,7 +25,6 @@ from dataface.core.compile.models.chart.authored import (
25
25
  from dataface.core.compile.models.primitives import FormatConfig
26
26
  from dataface.core.compile.models.query.normalized import (
27
27
  AnyQuery,
28
- MetricFlowQuery,
29
28
  SchemaQuery,
30
29
  SqlQuery,
31
30
  )
@@ -84,7 +83,7 @@ class Chart(BaseModel):
84
83
  id: str = Field(description="Unique identifier within the dashboard.")
85
84
  query: AnyQuery | None = Field(
86
85
  default=None,
87
- description="Resolved query object (SqlQuery, MetricFlowQuery, etc.). None for blank/placeholder charts.",
86
+ description="Resolved query object (SqlQuery, HttpQuery, etc.). None for blank/placeholder charts.",
88
87
  )
89
88
  query_name: str | None = Field(
90
89
  default=None,
@@ -378,7 +377,7 @@ class Chart(BaseModel):
378
377
  if isinstance(self.query, SqlQuery) and self.query.sql:
379
378
  query_dict["sql"] = self.query.sql
380
379
  if (
381
- isinstance(self.query, (SqlQuery, MetricFlowQuery, SchemaQuery))
380
+ isinstance(self.query, (SqlQuery, SchemaQuery))
382
381
  and isinstance(self.query.source, str)
383
382
  and self.query.source
384
383
  ):