dataface 0.1.5.dev88__py3-none-any.whl → 0.1.5.dev151__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.
- dataface/DATAFACE_SYNTAX.md +7 -6
- dataface/_docs_site.py +1 -1
- dataface/agent_api/__init__.py +4 -6
- dataface/agent_api/_init_templates/README.md +3 -3
- dataface/agent_api/_init_templates/{guide.yml → guide.yaml} +3 -4
- dataface/agent_api/_paths.py +111 -63
- dataface/agent_api/dashboards.py +3 -2
- dataface/agent_api/describe.py +10 -5
- dataface/agent_api/docs/yaml-reference.md +232 -239
- dataface/agent_api/init.py +3 -36
- dataface/agent_api/inspect.py +4 -7
- dataface/agent_api/mcp_install.py +3 -3
- dataface/agent_api/project.py +224 -0
- dataface/agent_api/query.py +2 -1
- dataface/agent_api/surface_aliases.yaml +3 -3
- dataface/agent_api/validate.py +13 -8
- dataface/ai/mcp/server.py +4 -3
- dataface/ai/tools/__init__.py +16 -6
- dataface/cli/commands/describe.py +4 -1
- dataface/cli/commands/extension.py +21 -115
- dataface/cli/commands/init.py +13 -51
- dataface/cli/commands/inspect.py +2 -1
- dataface/cli/commands/mcp_init.py +23 -14
- dataface/cli/commands/query.py +54 -44
- dataface/cli/commands/render.py +18 -13
- dataface/cli/commands/serve.py +8 -9
- dataface/cli/commands/skills_init.py +3 -5
- dataface/cli/commands/validate.py +4 -1
- dataface/cli/main.py +47 -42
- dataface/core/compile/compiler.py +0 -1
- dataface/core/compile/config.py +9 -61
- dataface/core/compile/introspection.py +8 -0
- dataface/core/compile/meta.py +2 -25
- dataface/core/compile/models/chart/authored.py +18 -238
- dataface/core/compile/models/chart/resolved.py +2 -4
- dataface/core/compile/models/config.py +12 -9
- dataface/core/compile/models/palette.py +1 -1
- dataface/core/compile/models/query/normalized.py +30 -13
- dataface/core/compile/models/refs.py +16 -10
- dataface/core/compile/models/style/authored.py +218 -0
- dataface/core/compile/models/style/theme.py +4 -2
- dataface/core/compile/models/variable/authored.py +27 -11
- dataface/core/compile/models/vega_lite/config.py +2 -14
- dataface/core/compile/normalize_queries.py +4 -1
- dataface/core/compile/normalize_variables.py +19 -4
- dataface/core/compile/palette.py +2 -2
- dataface/core/compile/parser.py +0 -1
- dataface/core/compile/schema_renderers/prompt.py +29 -11
- dataface/core/compile/validator.py +8 -7
- dataface/core/compile/vega_config.py +8 -25
- dataface/core/dashboard.py +5 -3
- dataface/core/defaults/default_config.yml +11 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +2 -2
- dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +1 -1
- dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +1 -1
- dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +1 -1
- dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +1 -1
- dataface/core/execute/adapters/adapter_registry.py +6 -13
- dataface/core/execute/adapters/csv_adapter.py +3 -3
- dataface/core/execute/adapters/dbt_adapter.py +8 -13
- dataface/core/execute/adapters/sql_adapter.py +11 -15
- dataface/core/execute/cache_keys.py +7 -2
- dataface/core/execute/duckdb_cache.py +2 -1
- dataface/core/execute/source_resolver.py +17 -4
- dataface/core/project_roots.py +32 -51
- dataface/core/render/chart/pipeline.py +0 -3
- dataface/core/render/chart/profile.py +11 -26
- dataface/core/render/chart/standard_renderer.py +3 -15
- dataface/core/render/chart/type_inference.py +3 -3
- dataface/core/render/faces.py +55 -1
- dataface/core/render/renderer.py +16 -1
- dataface/core/render/variable_controls.py +2 -0
- dataface/core/scoped_paths.py +14 -29
- dataface/core/serve/server.py +8 -1
- dataface/integrations/highlighting.py +112 -181
- dataface/integrations/markdown.py +4 -2
- {dataface-0.1.5.dev88.dist-info → dataface-0.1.5.dev151.dist-info}/METADATA +7 -8
- {dataface-0.1.5.dev88.dist-info → dataface-0.1.5.dev151.dist-info}/RECORD +81 -83
- dataface/agent_api/_init_templates/agents_dft_snippet.md +0 -26
- dataface/agent_api/_project_agents_md.py +0 -43
- dataface/core/compile/models/theme.py +0 -362
- {dataface-0.1.5.dev88.dist-info → dataface-0.1.5.dev151.dist-info}/WHEEL +0 -0
- {dataface-0.1.5.dev88.dist-info → dataface-0.1.5.dev151.dist-info}/entry_points.txt +0 -0
- {dataface-0.1.5.dev88.dist-info → dataface-0.1.5.dev151.dist-info}/licenses/LICENSE +0 -0
dataface/DATAFACE_SYNTAX.md
CHANGED
|
@@ -19,7 +19,7 @@ dft validate faces/my_dashboard.yml
|
|
|
19
19
|
To verify that your database connection works, run a simple query:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
dft query 'SELECT 1'
|
|
22
|
+
dft query <your_source> 'SELECT 1'
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
A successful result means your data source is reachable.
|
|
@@ -138,7 +138,7 @@ variables:
|
|
|
138
138
|
region:
|
|
139
139
|
input: select # See `dft docs variables` for all 14 input types
|
|
140
140
|
options: { static: [US, EU, APAC] }
|
|
141
|
-
default:
|
|
141
|
+
# No default: starts on All regions
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
Reference variables inside queries with bare `{{ region }}` — no `variables.` prefix.
|
|
@@ -694,7 +694,6 @@ layers:
|
|
|
694
694
|
- type: bar # bar | line | area | circle | square | tick | rule | trail | rect | image | scatter
|
|
695
695
|
y: actual
|
|
696
696
|
label: Actual
|
|
697
|
-
fill: "#0369a1" # static paint (use fill:, not color: {value:})
|
|
698
697
|
- type: line
|
|
699
698
|
y: target
|
|
700
699
|
label: Target
|
|
@@ -703,7 +702,7 @@ layers:
|
|
|
703
702
|
title: "Target"
|
|
704
703
|
```
|
|
705
704
|
|
|
706
|
-
Each layer accepts: `type`, `query` (optional layer-specific query), `x`, `y`, `label`, `color` (data channel, bare field name), `
|
|
705
|
+
Each layer accepts: `type`, `query` (optional layer-specific query), `x`, `y`, `label`, `color` (data channel, bare field name), `axis_y`. Vega-Lite `encoding:` is not allowed inside a layer — use the typed channels.
|
|
707
706
|
|
|
708
707
|
### Conditional formatting
|
|
709
708
|
|
|
@@ -845,7 +844,7 @@ variables:
|
|
|
845
844
|
description: "Restrict every query to one region."
|
|
846
845
|
options:
|
|
847
846
|
static: [US, EU, APAC]
|
|
848
|
-
default:
|
|
847
|
+
# No default: starts on All regions
|
|
849
848
|
```
|
|
850
849
|
|
|
851
850
|
Input types (14 total):
|
|
@@ -893,7 +892,7 @@ variables:
|
|
|
893
892
|
product:
|
|
894
893
|
input: select
|
|
895
894
|
options:
|
|
896
|
-
static: [
|
|
895
|
+
static: [Electronics, Clothing] # Hardcoded list; blank -- All -- is implicit
|
|
897
896
|
# OR
|
|
898
897
|
query: products_list # Query whose first column is the option list
|
|
899
898
|
column: product_name # Optional: which column in that query
|
|
@@ -906,6 +905,8 @@ variables:
|
|
|
906
905
|
|
|
907
906
|
Top-level option-source binding (alternative to `options:`): `column`, `query`, `dimension` (MetricFlow), `measure` (MetricFlow), `model` (dbt).
|
|
908
907
|
|
|
908
|
+
For `select` and `multiselect`, omitting `default` starts the variable unset. The renderer adds a blank `-- All --` option for that unset state; `{{ filter('column', variable) }}` emits `1=1` unless the SQL call site opts into `none='deny'`.
|
|
909
|
+
|
|
909
910
|
Disabled forms:
|
|
910
911
|
|
|
911
912
|
```yaml
|
dataface/_docs_site.py
CHANGED
dataface/agent_api/__init__.py
CHANGED
|
@@ -37,10 +37,6 @@ CLI/MCP files that contain logic beyond parse-and-dispatch.
|
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
39
|
from dataface.agent_api import skills as skills
|
|
40
|
-
from dataface.agent_api._paths import (
|
|
41
|
-
RenderSetup as RenderSetup,
|
|
42
|
-
setup_render_for_face as setup_render_for_face,
|
|
43
|
-
)
|
|
44
40
|
from dataface.agent_api.dashboards import (
|
|
45
41
|
RenderedDashboard as RenderedDashboard,
|
|
46
42
|
render_dashboard as render_dashboard,
|
|
@@ -55,8 +51,10 @@ from dataface.agent_api.init import (
|
|
|
55
51
|
InitResult as InitResult,
|
|
56
52
|
init_project as init_project,
|
|
57
53
|
)
|
|
54
|
+
from dataface.agent_api.project import Project as Project
|
|
58
55
|
from dataface.agent_api.query import QueryFaceResult, query_face
|
|
59
56
|
from dataface.agent_api.validate import ValidateDashboardArgs, ValidateResult
|
|
57
|
+
from dataface.core.compile.config import ProjectSourcesConfig as ProjectSourcesConfig
|
|
60
58
|
|
|
61
59
|
__all__ = [
|
|
62
60
|
"DescribeFaceArgs",
|
|
@@ -65,8 +63,9 @@ __all__ = [
|
|
|
65
63
|
"DocsResult",
|
|
66
64
|
"DocsSearchHit",
|
|
67
65
|
"InitResult",
|
|
66
|
+
"Project",
|
|
67
|
+
"ProjectSourcesConfig",
|
|
68
68
|
"QueryFaceResult",
|
|
69
|
-
"RenderSetup",
|
|
70
69
|
"RenderedDashboard",
|
|
71
70
|
"Topic",
|
|
72
71
|
"ValidateDashboardArgs",
|
|
@@ -75,5 +74,4 @@ __all__ = [
|
|
|
75
74
|
"init_project",
|
|
76
75
|
"query_face",
|
|
77
76
|
"render_dashboard",
|
|
78
|
-
"setup_render_for_face",
|
|
79
77
|
]
|
|
@@ -10,7 +10,7 @@ for your data project.
|
|
|
10
10
|
|
|
11
11
|
## Authoring modes
|
|
12
12
|
|
|
13
|
-
**YAML (`.
|
|
13
|
+
**YAML (`.yaml`)** — structured dashboards with queries, charts, and layout.
|
|
14
14
|
See the [dataface guide](guide) for a working example covering queries, charts, and variables.
|
|
15
15
|
|
|
16
16
|
**Markdown (`.md`)** — prose pages like this one. Add YAML frontmatter for
|
|
@@ -18,7 +18,7 @@ queries and charts, then embed them inline with `{% raw %}{{ chart my_chart }}{%
|
|
|
18
18
|
|
|
19
19
|
## Next steps
|
|
20
20
|
|
|
21
|
-
1. Open `faces/guide.
|
|
21
|
+
1. Open `faces/guide.yaml` and tweak the content.
|
|
22
22
|
2. Run `dft serve` and open the URL it prints.
|
|
23
|
-
3. Add new `.
|
|
23
|
+
3. Add new `.yaml` or `.md` files under `faces/` — they appear automatically.
|
|
24
24
|
4. Run `dft validate` to validate your face YAML for errors.
|
|
@@ -20,7 +20,7 @@ description: "A tour of Dataface: queries, charts, layout, KPIs, and variables."
|
|
|
20
20
|
text: |
|
|
21
21
|
Welcome to **Dataface** — dashboards as YAML files.
|
|
22
22
|
|
|
23
|
-
This file is your starter guide. Edit it, add new `.
|
|
23
|
+
This file is your starter guide. Edit it, add new `.yaml` files under
|
|
24
24
|
`faces/`, and run `dft serve` to preview everything in your browser.
|
|
25
25
|
|
|
26
26
|
# ── Queries ───────────────────────────────────────────────────────────────────
|
|
@@ -105,14 +105,13 @@ charts:
|
|
|
105
105
|
# ── Variables ─────────────────────────────────────────────────────────────────
|
|
106
106
|
# Variables add interactive dropdowns. Wire them to SQL queries with {{ varname }}.
|
|
107
107
|
# With `type: values` data above they show the UI but don't filter — swap to SQL
|
|
108
|
-
# queries and use `WHERE
|
|
108
|
+
# queries and use `WHERE {{ filter('category', segment) }}` to make them live.
|
|
109
109
|
variables:
|
|
110
110
|
segment:
|
|
111
111
|
label: Segment
|
|
112
112
|
input: select
|
|
113
|
-
default: All
|
|
114
113
|
options:
|
|
115
|
-
static: [
|
|
114
|
+
static: [Software, Services, Hardware]
|
|
116
115
|
|
|
117
116
|
# ── Layout ────────────────────────────────────────────────────────────────────
|
|
118
117
|
# `rows:` stacks content vertically.
|
dataface/agent_api/_paths.py
CHANGED
|
@@ -5,23 +5,67 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
from dataface.core.execute.adapters import
|
|
8
|
+
from dataface.core.execute.adapters import (
|
|
9
|
+
AdapterRegistry as AdapterRegistry,
|
|
10
|
+
build_adapter_registry as build_adapter_registry,
|
|
11
|
+
)
|
|
9
12
|
from dataface.core.project_roots import (
|
|
10
|
-
discover_render_context,
|
|
11
|
-
discovery_boundary_for_face,
|
|
12
|
-
|
|
13
|
+
discover_render_context as discover_render_context,
|
|
14
|
+
discovery_boundary_for_face as discovery_boundary_for_face,
|
|
15
|
+
find_dft_root as find_dft_root,
|
|
13
16
|
)
|
|
14
17
|
from dataface.core.scoped_paths import (
|
|
15
|
-
|
|
16
|
-
project_root_for as project_root_for,
|
|
17
|
-
resolve_scoped_path as resolve_scoped_path,
|
|
18
|
+
resolve_scoped_path as _core_resolve_scoped_path,
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
def resolve_project_dir(project_dir: Path | None) -> Path:
|
|
23
|
+
"""Resolve the caller's `project_dir`, walking up from cwd when omitted.
|
|
24
|
+
|
|
25
|
+
Boundary helper: every agent_api / CLI callsite that today forwards a
|
|
26
|
+
possibly-None `project_dir` into a core helper routes through this so the
|
|
27
|
+
cwd read stays at the agent_api boundary, not inside `dataface/core`.
|
|
28
|
+
"""
|
|
29
|
+
if project_dir is not None:
|
|
30
|
+
return project_dir.resolve()
|
|
31
|
+
cwd = Path.cwd().resolve()
|
|
32
|
+
return find_dft_root(cwd) or cwd
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def resolve_project_dir_from_paths(
|
|
36
|
+
paths: list[Path] | None, project_dir: Path | None
|
|
37
|
+
) -> Path:
|
|
38
|
+
"""Resolve project_dir for a multi-path CLI verb (validate / describe).
|
|
39
|
+
|
|
40
|
+
Explicit `--project-dir` wins. Otherwise, when all supplied paths are
|
|
41
|
+
absolute, walk up from the first path's parent to anchor on the project
|
|
42
|
+
that contains the file. Otherwise fall back to a cwd walk.
|
|
43
|
+
"""
|
|
44
|
+
if project_dir is not None:
|
|
45
|
+
return project_dir.resolve()
|
|
46
|
+
if paths and all(p.is_absolute() for p in paths):
|
|
47
|
+
anchor = paths[0].parent.resolve()
|
|
48
|
+
return find_dft_root(anchor) or anchor
|
|
49
|
+
return resolve_project_dir(None)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def resolve_scoped_path(path: Path, project_dir: Path | None = None) -> Path:
|
|
53
|
+
"""agent_api wrapper: resolves `None → cwd-walked root` at the boundary,
|
|
54
|
+
then delegates to `core.scoped_paths.resolve_scoped_path`.
|
|
55
|
+
|
|
56
|
+
Absolute paths with no explicit `project_dir` resolve directly (no
|
|
57
|
+
containment check) — the caller supplied the path, so no project context is
|
|
58
|
+
needed to validate containment.
|
|
59
|
+
"""
|
|
60
|
+
if path.is_absolute() and project_dir is None:
|
|
61
|
+
return path.resolve()
|
|
62
|
+
return _core_resolve_scoped_path(path, resolve_project_dir(project_dir))
|
|
63
|
+
|
|
64
|
+
|
|
21
65
|
def no_project_hint(project_dir: Path | None) -> str:
|
|
22
66
|
"""Return a hint string when no project marker is found, else empty string."""
|
|
23
67
|
check = project_dir if project_dir is not None else Path.cwd()
|
|
24
|
-
if
|
|
68
|
+
if find_dft_root(check) is not None:
|
|
25
69
|
return ""
|
|
26
70
|
return (
|
|
27
71
|
f" No Dataface project marker found at or above {check}."
|
|
@@ -30,89 +74,93 @@ def no_project_hint(project_dir: Path | None) -> str:
|
|
|
30
74
|
|
|
31
75
|
|
|
32
76
|
@dataclass(frozen=True)
|
|
33
|
-
class
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
Bundles the scoped path, scoped base, and adapter registry produced by
|
|
37
|
-
discovery. CLI/serve callers building this from a face on disk avoid
|
|
38
|
-
duplicating discovery boilerplate; tests can construct one directly.
|
|
39
|
-
"""
|
|
77
|
+
class FaceRenderContext:
|
|
78
|
+
"""Resources resolved from a face path + project root for rendering."""
|
|
40
79
|
|
|
41
|
-
|
|
80
|
+
face_file: Path
|
|
42
81
|
scoped_path: Path | None
|
|
43
|
-
scoped_base: Path
|
|
82
|
+
scoped_base: Path
|
|
83
|
+
project_root: Path
|
|
84
|
+
output_dir: Path
|
|
85
|
+
adapter_registry: AdapterRegistry
|
|
44
86
|
|
|
45
87
|
|
|
46
|
-
def
|
|
88
|
+
def build_face_render_context(
|
|
47
89
|
face_path: Path,
|
|
48
90
|
project_dir: Path | None = None,
|
|
49
91
|
*,
|
|
50
92
|
read_only: bool = True,
|
|
51
|
-
) ->
|
|
52
|
-
"""
|
|
93
|
+
) -> FaceRenderContext:
|
|
94
|
+
"""Resolve a face path, walk for dbt context, and build the adapter registry.
|
|
53
95
|
|
|
54
|
-
|
|
55
|
-
the
|
|
56
|
-
|
|
57
|
-
|
|
96
|
+
``project_dir=None`` means "walk freely from the face's parent" — used by
|
|
97
|
+
the CLI when ``--project-dir`` is omitted so a face under a dbt sub-project
|
|
98
|
+
still anchors on that sub-project's root. A given ``project_dir`` is
|
|
99
|
+
authoritative; the walk only contributes the dbt project path.
|
|
58
100
|
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
101
|
+
if face_path.is_absolute():
|
|
102
|
+
face_file = face_path.resolve()
|
|
103
|
+
elif ".." in face_path.parts:
|
|
104
|
+
face_file = (Path.cwd() / face_path).resolve()
|
|
62
105
|
else:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
106
|
+
anchor = (
|
|
107
|
+
project_dir.resolve()
|
|
108
|
+
if project_dir is not None
|
|
109
|
+
else resolve_project_dir(None)
|
|
110
|
+
)
|
|
111
|
+
face_file = (anchor / face_path).resolve()
|
|
112
|
+
|
|
113
|
+
walk_root, dbt_project_path = discover_render_context(
|
|
66
114
|
face_file.parent,
|
|
67
115
|
discovery_boundary_for_face(face_file.parent, project_dir),
|
|
68
116
|
)
|
|
69
|
-
if project_dir
|
|
70
|
-
project_root = project_dir.resolve()
|
|
117
|
+
project_root = project_dir.resolve() if project_dir is not None else walk_root
|
|
71
118
|
|
|
72
119
|
try:
|
|
73
120
|
scoped_path: Path | None = face_file.relative_to(project_root)
|
|
74
|
-
scoped_base: Path | None = project_root
|
|
75
121
|
except ValueError:
|
|
76
122
|
scoped_path = face_file
|
|
77
|
-
scoped_base = None
|
|
78
123
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
scoped_path=scoped_path,
|
|
88
|
-
scoped_base=scoped_base,
|
|
124
|
+
return FaceRenderContext(
|
|
125
|
+
face_file=face_file,
|
|
126
|
+
scoped_path=scoped_path,
|
|
127
|
+
scoped_base=project_root,
|
|
128
|
+
project_root=project_root,
|
|
129
|
+
output_dir=project_root,
|
|
130
|
+
adapter_registry=build_adapter_registry(
|
|
131
|
+
project_root, read_only=read_only, dbt_project_path=dbt_project_path
|
|
89
132
|
),
|
|
90
|
-
face_file,
|
|
91
133
|
)
|
|
92
134
|
|
|
93
135
|
|
|
94
|
-
|
|
136
|
+
@dataclass(frozen=True)
|
|
137
|
+
class YamlRenderContext:
|
|
138
|
+
"""Resources resolved from a project root for rendering inline YAML."""
|
|
139
|
+
|
|
140
|
+
project_root: Path
|
|
141
|
+
output_dir: Path
|
|
142
|
+
adapter_registry: AdapterRegistry
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def build_yaml_render_context(
|
|
95
146
|
project_dir: Path | None = None,
|
|
96
147
|
*,
|
|
97
148
|
read_only: bool = True,
|
|
98
|
-
) ->
|
|
99
|
-
"""
|
|
149
|
+
) -> YamlRenderContext:
|
|
150
|
+
"""Walk for dbt context and build the adapter registry for inline YAML.
|
|
100
151
|
|
|
101
|
-
|
|
102
|
-
|
|
152
|
+
``project_dir=None`` walks from cwd to discover the project root; a given
|
|
153
|
+
``project_dir`` is authoritative.
|
|
103
154
|
"""
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if project_dir:
|
|
107
|
-
project_root = output_dir
|
|
108
|
-
|
|
109
|
-
adapter_registry = build_adapter_registry(
|
|
110
|
-
project_root,
|
|
111
|
-
read_only=read_only,
|
|
112
|
-
dbt_project_path=dbt_project_path,
|
|
155
|
+
anchor = (
|
|
156
|
+
project_dir.resolve() if project_dir is not None else resolve_project_dir(None)
|
|
113
157
|
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
158
|
+
walk_root, dbt_project_path = discover_render_context(anchor, None)
|
|
159
|
+
project_root = anchor if project_dir is not None else walk_root
|
|
160
|
+
return YamlRenderContext(
|
|
161
|
+
project_root=project_root,
|
|
162
|
+
output_dir=project_root,
|
|
163
|
+
adapter_registry=build_adapter_registry(
|
|
164
|
+
project_root, read_only=read_only, dbt_project_path=dbt_project_path
|
|
165
|
+
),
|
|
118
166
|
)
|
dataface/agent_api/dashboards.py
CHANGED
|
@@ -108,7 +108,7 @@ class RenderDashboardArgs(BaseModel):
|
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
def list_dashboards(
|
|
111
|
-
directory: Path
|
|
111
|
+
directory: Path,
|
|
112
112
|
recursive: bool = True,
|
|
113
113
|
) -> ListDashboardsResult:
|
|
114
114
|
"""List all available dashboards in a directory."""
|
|
@@ -197,8 +197,9 @@ def list_dashboards(
|
|
|
197
197
|
|
|
198
198
|
def get_dashboard(
|
|
199
199
|
path: Path,
|
|
200
|
+
*,
|
|
201
|
+
project_dir: Path,
|
|
200
202
|
include_raw: bool = False,
|
|
201
|
-
project_dir: Path | None = None,
|
|
202
203
|
) -> CompiledDashboard:
|
|
203
204
|
"""Get the compiled structure of a dashboard."""
|
|
204
205
|
try:
|
dataface/agent_api/describe.py
CHANGED
|
@@ -84,7 +84,11 @@ class DescribeFaceArgs(BaseModel):
|
|
|
84
84
|
|
|
85
85
|
path: Path = Field(description="Path to the dashboard YAML file to describe")
|
|
86
86
|
project_dir: Path | None = Field(
|
|
87
|
-
None,
|
|
87
|
+
default=None,
|
|
88
|
+
description=(
|
|
89
|
+
"Project root for resolving relative paths. Optional on the wire; "
|
|
90
|
+
"the MCP server injects ctx.default_project_dir or cwd when omitted."
|
|
91
|
+
),
|
|
88
92
|
)
|
|
89
93
|
|
|
90
94
|
|
|
@@ -186,7 +190,7 @@ def _encoding_for_chart(chart: Chart) -> dict[str, Any]:
|
|
|
186
190
|
# ---------------------------------------------------------------------------
|
|
187
191
|
|
|
188
192
|
|
|
189
|
-
def describe_face(path: Path, project_dir: Path
|
|
193
|
+
def describe_face(path: Path, *, project_dir: Path) -> DescribeFaceResult:
|
|
190
194
|
"""Describe the structure of a face: queries, charts, variables, layout."""
|
|
191
195
|
from dataface.agent_api._paths import no_project_hint, resolve_scoped_path
|
|
192
196
|
from dataface.core.compile.compiler import compile_file
|
|
@@ -300,7 +304,8 @@ def describe_face(path: Path, project_dir: Path | None = None) -> DescribeFaceRe
|
|
|
300
304
|
|
|
301
305
|
def describe_paths(
|
|
302
306
|
paths: list[Path],
|
|
303
|
-
|
|
307
|
+
*,
|
|
308
|
+
project_dir: Path,
|
|
304
309
|
) -> list[DescribeFaceResult]:
|
|
305
310
|
"""Describe N face files / directories.
|
|
306
311
|
|
|
@@ -310,13 +315,13 @@ def describe_paths(
|
|
|
310
315
|
"""
|
|
311
316
|
out: list[DescribeFaceResult] = []
|
|
312
317
|
for p in paths:
|
|
313
|
-
out.extend(_describe_one_path(p, project_dir
|
|
318
|
+
out.extend(_describe_one_path(p, project_dir))
|
|
314
319
|
return out
|
|
315
320
|
|
|
316
321
|
|
|
317
322
|
def _describe_one_path(
|
|
318
323
|
path: Path,
|
|
319
|
-
project_dir: Path
|
|
324
|
+
project_dir: Path,
|
|
320
325
|
) -> list[DescribeFaceResult]:
|
|
321
326
|
"""Per-argv expansion: file → [one], dir → walk."""
|
|
322
327
|
# WHY: dataface.core.inspect.manifest_utils triggers the inspect package
|