dataface 0.1.2__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.
- d3_format/__init__.py +14 -0
- d3_format/errors.py +19 -0
- d3_format/format.py +551 -0
- d3_format/spec.py +159 -0
- dataface/DATAFACE_SYNTAX.md +1135 -0
- dataface/__init__.py +93 -0
- dataface/_docs_site.py +20 -0
- dataface/_install_hint.py +26 -0
- dataface/agent_api/__init__.py +79 -0
- dataface/agent_api/_init_templates/__init__.py +0 -0
- dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
- dataface/agent_api/_init_templates/dataface.yml +15 -0
- dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
- dataface/agent_api/_init_templates/index.md +24 -0
- dataface/agent_api/_paths.py +118 -0
- dataface/agent_api/_project_agents_md.py +43 -0
- dataface/agent_api/_session_store.py +486 -0
- dataface/agent_api/_state.py +28 -0
- dataface/agent_api/chat.py +221 -0
- dataface/agent_api/dashboards.py +257 -0
- dataface/agent_api/describe.py +366 -0
- dataface/agent_api/describe_query.py +120 -0
- dataface/agent_api/docs/__init__.py +25 -0
- dataface/agent_api/docs/_loader.py +292 -0
- dataface/agent_api/docs/yaml-reference.md +2757 -0
- dataface/agent_api/file_refs.py +118 -0
- dataface/agent_api/init.py +126 -0
- dataface/agent_api/inspect.py +128 -0
- dataface/agent_api/mcp_install.py +170 -0
- dataface/agent_api/query.py +274 -0
- dataface/agent_api/schema.py +658 -0
- dataface/agent_api/schema_search.py +284 -0
- dataface/agent_api/search.py +270 -0
- dataface/agent_api/skill_install.py +141 -0
- dataface/agent_api/skill_render.py +90 -0
- dataface/agent_api/skills.py +293 -0
- dataface/agent_api/surface_aliases.yaml +128 -0
- dataface/agent_api/validate.py +175 -0
- dataface/agent_api/validate_query.py +84 -0
- dataface/ai/__init__.py +39 -0
- dataface/ai/agent.py +139 -0
- dataface/ai/context.py +45 -0
- dataface/ai/events.py +62 -0
- dataface/ai/external_mcp.py +610 -0
- dataface/ai/generate_sql.py +96 -0
- dataface/ai/llm.py +403 -0
- dataface/ai/mcp/__init__.py +51 -0
- dataface/ai/mcp/server.py +289 -0
- dataface/ai/memories.py +85 -0
- dataface/ai/prompts.py +177 -0
- dataface/ai/schema_context.py +138 -0
- dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
- dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
- dataface/ai/skills/dashboard-build/SKILL.md +212 -0
- dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
- dataface/ai/skills/dashboard-design/SKILL.md +182 -0
- dataface/ai/skills/dashboard-review/SKILL.md +113 -0
- dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
- dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
- dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
- dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
- dataface/ai/skills/drill-down-link/SKILL.md +112 -0
- dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
- dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
- dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
- dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
- dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
- dataface/ai/skills/kpi-row/SKILL.md +101 -0
- dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
- dataface/ai/skills/report-design/SKILL.md +184 -0
- dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
- dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
- dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
- dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
- dataface/ai/skills/time-series-trend/SKILL.md +93 -0
- dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
- dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
- dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
- dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
- dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
- dataface/ai/tool_schemas.py +132 -0
- dataface/ai/tools/__init__.py +312 -0
- dataface/ai/yaml_utils.py +57 -0
- dataface/cli/__init__.py +3 -0
- dataface/cli/_console.py +48 -0
- dataface/cli/_error_format.py +83 -0
- dataface/cli/_extras.py +190 -0
- dataface/cli/_json_output.py +8 -0
- dataface/cli/_parsing.py +17 -0
- dataface/cli/_version_info.py +56 -0
- dataface/cli/commands/__init__.py +3 -0
- dataface/cli/commands/_agent_input.py +205 -0
- dataface/cli/commands/_agent_server.py +115 -0
- dataface/cli/commands/chat.py +645 -0
- dataface/cli/commands/describe.py +107 -0
- dataface/cli/commands/docs.py +131 -0
- dataface/cli/commands/extension.py +179 -0
- dataface/cli/commands/init.py +240 -0
- dataface/cli/commands/inspect.py +94 -0
- dataface/cli/commands/mcp_init.py +167 -0
- dataface/cli/commands/query.py +386 -0
- dataface/cli/commands/render.py +291 -0
- dataface/cli/commands/schema.py +411 -0
- dataface/cli/commands/search.py +49 -0
- dataface/cli/commands/serve.py +114 -0
- dataface/cli/commands/skills.py +133 -0
- dataface/cli/commands/skills_init.py +161 -0
- dataface/cli/commands/validate.py +63 -0
- dataface/cli/main.py +1501 -0
- dataface/core/__init__.py +75 -0
- dataface/core/compile/__init__.py +244 -0
- dataface/core/compile/_jinja_helpers.py +78 -0
- dataface/core/compile/channel.py +222 -0
- dataface/core/compile/chart_focus.py +101 -0
- dataface/core/compile/chart_resolved.py +169 -0
- dataface/core/compile/chart_type_detection.py +489 -0
- dataface/core/compile/chart_update.py +261 -0
- dataface/core/compile/colors.py +64 -0
- dataface/core/compile/compiler.py +904 -0
- dataface/core/compile/config.py +823 -0
- dataface/core/compile/custom_chart_types.py +208 -0
- dataface/core/compile/data_table_attachment.py +1287 -0
- dataface/core/compile/detect.py +110 -0
- dataface/core/compile/errors.py +302 -0
- dataface/core/compile/filter_injection.py +319 -0
- dataface/core/compile/introspection.py +527 -0
- dataface/core/compile/jinja.py +511 -0
- dataface/core/compile/labels_env.py +52 -0
- dataface/core/compile/markdown.py +154 -0
- dataface/core/compile/meta.py +388 -0
- dataface/core/compile/models/__init__.py +0 -0
- dataface/core/compile/models/chart/__init__.py +0 -0
- dataface/core/compile/models/chart/authored.py +2137 -0
- dataface/core/compile/models/chart/compiled.py +398 -0
- dataface/core/compile/models/config.py +347 -0
- dataface/core/compile/models/face/__init__.py +0 -0
- dataface/core/compile/models/face/authored.py +659 -0
- dataface/core/compile/models/face/compiled.py +522 -0
- dataface/core/compile/models/factories.py +201 -0
- dataface/core/compile/models/markers.py +40 -0
- dataface/core/compile/models/palette.py +36 -0
- dataface/core/compile/models/primitives.py +415 -0
- dataface/core/compile/models/query/__init__.py +0 -0
- dataface/core/compile/models/query/authored.py +246 -0
- dataface/core/compile/models/query/compiled.py +710 -0
- dataface/core/compile/models/refs.py +137 -0
- dataface/core/compile/models/source.py +611 -0
- dataface/core/compile/models/style/__init__.py +0 -0
- dataface/core/compile/models/style/authored.py +481 -0
- dataface/core/compile/models/style/compiled.py +3399 -0
- dataface/core/compile/models/style/merged.py +1682 -0
- dataface/core/compile/models/theme.py +362 -0
- dataface/core/compile/models/variable/__init__.py +0 -0
- dataface/core/compile/models/variable/authored.py +254 -0
- dataface/core/compile/models/vega_lite/__init__.py +0 -0
- dataface/core/compile/models/vega_lite/config.py +510 -0
- dataface/core/compile/models/vega_lite/contracts.py +171 -0
- dataface/core/compile/normalize_charts.py +494 -0
- dataface/core/compile/normalize_layout.py +1000 -0
- dataface/core/compile/normalize_queries.py +297 -0
- dataface/core/compile/normalize_variables.py +489 -0
- dataface/core/compile/normalizer.py +543 -0
- dataface/core/compile/palette.py +1100 -0
- dataface/core/compile/parameterized.py +658 -0
- dataface/core/compile/parser.py +228 -0
- dataface/core/compile/schema.py +20 -0
- dataface/core/compile/schema_renderers/__init__.py +0 -0
- dataface/core/compile/schema_renderers/json_schema.py +163 -0
- dataface/core/compile/schema_renderers/prompt.py +152 -0
- dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
- dataface/core/compile/sizing.py +2126 -0
- dataface/core/compile/sources.py +518 -0
- dataface/core/compile/sql_authoring_lint.py +56 -0
- dataface/core/compile/style_cascade.py +471 -0
- dataface/core/compile/typography.py +299 -0
- dataface/core/compile/validator.py +301 -0
- dataface/core/compile/variables.py +53 -0
- dataface/core/compile/vega_config.py +98 -0
- dataface/core/compile/vega_lite/__init__.py +6 -0
- dataface/core/compile/vega_lite/validation.py +95 -0
- dataface/core/compile/yaml_error_formatter.py +838 -0
- dataface/core/connections.py +38 -0
- dataface/core/dashboard.py +358 -0
- dataface/core/defaults/default_config.yml +101 -0
- dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
- dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
- dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
- dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
- dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
- dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
- dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
- dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
- dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
- dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
- dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
- dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
- dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
- dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
- dataface/core/defaults/palettes/sequential/amber.yml +22 -0
- dataface/core/defaults/palettes/sequential/blue.yml +22 -0
- dataface/core/defaults/palettes/sequential/brown.yml +22 -0
- dataface/core/defaults/palettes/sequential/gray.yml +22 -0
- dataface/core/defaults/palettes/sequential/green.yml +22 -0
- dataface/core/defaults/palettes/sequential/purple.yml +22 -0
- dataface/core/defaults/palettes/sequential/rust.yml +22 -0
- dataface/core/defaults/palettes/sequential/teal.yml +22 -0
- dataface/core/defaults/palettes/tone/negative.yml +32 -0
- dataface/core/defaults/palettes/tone/positive.yml +22 -0
- dataface/core/defaults/palettes/tone/warning.yml +22 -0
- dataface/core/defaults/themes/_base.yaml +786 -0
- dataface/core/defaults/themes/bi.yaml +16 -0
- dataface/core/defaults/themes/carbong100.yaml +41 -0
- dataface/core/defaults/themes/cream.yaml +122 -0
- dataface/core/defaults/themes/dark.yaml +40 -0
- dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
- dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
- dataface/core/defaults/themes/editorial.yaml +147 -0
- dataface/core/defaults/themes/light.yaml +30 -0
- dataface/core/defaults/themes/looker.yaml +17 -0
- dataface/core/defaults/themes/stark.yaml +134 -0
- dataface/core/errors/__init__.py +67 -0
- dataface/core/errors/codes_compile.py +56 -0
- dataface/core/errors/codes_execute.py +177 -0
- dataface/core/errors/codes_render.py +106 -0
- dataface/core/errors/codes_unknown.py +15 -0
- dataface/core/errors/hints.py +74 -0
- dataface/core/errors/registry.py +42 -0
- dataface/core/errors/structured.py +92 -0
- dataface/core/execute/__init__.py +91 -0
- dataface/core/execute/adapters/__init__.py +49 -0
- dataface/core/execute/adapters/adapter_registry.py +400 -0
- dataface/core/execute/adapters/base.py +245 -0
- dataface/core/execute/adapters/csv_adapter.py +239 -0
- dataface/core/execute/adapters/dbt_adapter.py +283 -0
- dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
- dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
- dataface/core/execute/adapters/dbt_utils.py +150 -0
- dataface/core/execute/adapters/http_adapter.py +224 -0
- dataface/core/execute/adapters/metricflow_adapter.py +94 -0
- dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
- dataface/core/execute/adapters/sql_adapter.py +710 -0
- dataface/core/execute/adapters/values_adapter.py +58 -0
- dataface/core/execute/batch.py +744 -0
- dataface/core/execute/cache_backend.py +135 -0
- dataface/core/execute/cache_keys.py +66 -0
- dataface/core/execute/dbt_jinja.py +21 -0
- dataface/core/execute/dialects/__init__.py +121 -0
- dataface/core/execute/dialects/athena.py +75 -0
- dataface/core/execute/dialects/base.py +302 -0
- dataface/core/execute/dialects/bigquery.py +38 -0
- dataface/core/execute/dialects/databricks.py +68 -0
- dataface/core/execute/dialects/duckdb.py +35 -0
- dataface/core/execute/dialects/mysql.py +68 -0
- dataface/core/execute/dialects/postgres.py +39 -0
- dataface/core/execute/dialects/redshift.py +12 -0
- dataface/core/execute/dialects/snowflake.py +51 -0
- dataface/core/execute/dialects/sqlserver.py +92 -0
- dataface/core/execute/duckdb_cache.py +712 -0
- dataface/core/execute/duckdb_config.py +26 -0
- dataface/core/execute/errors.py +213 -0
- dataface/core/execute/executor.py +1249 -0
- dataface/core/execute/parallel.py +162 -0
- dataface/core/execute/setup_sql.py +58 -0
- dataface/core/execute/source_registry.py +72 -0
- dataface/core/execute/source_resolver.py +255 -0
- dataface/core/execute/sql_guard.py +387 -0
- dataface/core/execute/sql_literals.py +199 -0
- dataface/core/fonts.py +52 -0
- dataface/core/inspect/__init__.py +32 -0
- dataface/core/inspect/cache_factory.py +98 -0
- dataface/core/inspect/db_types.py +162 -0
- dataface/core/inspect/dbt_schema.py +96 -0
- dataface/core/inspect/defaults.yml +37 -0
- dataface/core/inspect/fanout_risk.py +109 -0
- dataface/core/inspect/manifest_utils.py +77 -0
- dataface/core/inspect/partials/categorical.yml +40 -0
- dataface/core/inspect/partials/date.yml +40 -0
- dataface/core/inspect/partials/numeric.yml +55 -0
- dataface/core/inspect/partition_types.py +38 -0
- dataface/core/inspect/query_validator.py +975 -0
- dataface/core/inspect/renderer.py +354 -0
- dataface/core/inspect/resolver.py +808 -0
- dataface/core/inspect/search.py +461 -0
- dataface/core/inspect/sources/__init__.py +32 -0
- dataface/core/inspect/sources/dbt.py +738 -0
- dataface/core/inspect/sources/duckdb_utils.py +66 -0
- dataface/core/inspect/templates/__init__.py +1 -0
- dataface/core/inspect/templates/categorical_column.yml +196 -0
- dataface/core/inspect/templates/charts.yml +109 -0
- dataface/core/inspect/templates/date_column.yml +248 -0
- dataface/core/inspect/templates/model.yml +138 -0
- dataface/core/inspect/templates/numeric_column.yml +261 -0
- dataface/core/inspect/templates/quality.yml +80 -0
- dataface/core/inspect/templates/string_column.yml +263 -0
- dataface/core/project_roots.py +165 -0
- dataface/core/render/__init__.py +87 -0
- dataface/core/render/board_links.py +176 -0
- dataface/core/render/chart/__init__.py +27 -0
- dataface/core/render/chart/arc_attached_table.py +251 -0
- dataface/core/render/chart/artifacts.py +16 -0
- dataface/core/render/chart/callout.py +225 -0
- dataface/core/render/chart/decisions.py +358 -0
- dataface/core/render/chart/geo.py +700 -0
- dataface/core/render/chart/kpi.py +916 -0
- dataface/core/render/chart/labels.py +76 -0
- dataface/core/render/chart/pipeline.py +818 -0
- dataface/core/render/chart/presentation.py +36 -0
- dataface/core/render/chart/profile.py +3438 -0
- dataface/core/render/chart/render_single.py +347 -0
- dataface/core/render/chart/renderers.py +193 -0
- dataface/core/render/chart/rendering.py +565 -0
- dataface/core/render/chart/serialization.py +90 -0
- dataface/core/render/chart/spark.py +496 -0
- dataface/core/render/chart/spark_bar.py +370 -0
- dataface/core/render/chart/spec_builders.py +154 -0
- dataface/core/render/chart/standard_renderer.py +2645 -0
- dataface/core/render/chart/table.py +2957 -0
- dataface/core/render/chart/table_support.py +1452 -0
- dataface/core/render/chart/tick_values.py +66 -0
- dataface/core/render/chart/time_unit_detect.py +809 -0
- dataface/core/render/chart/title_overflow.py +157 -0
- dataface/core/render/chart/type_inference.py +122 -0
- dataface/core/render/chart/validation.py +99 -0
- dataface/core/render/chart/vega_lite.py +125 -0
- dataface/core/render/chart/vega_lite_types.py +268 -0
- dataface/core/render/chart/vl_field_maps.py +346 -0
- dataface/core/render/chart_interactivity.py +24 -0
- dataface/core/render/control_registry.py +287 -0
- dataface/core/render/converters/__init__.py +24 -0
- dataface/core/render/converters/chart.py +276 -0
- dataface/core/render/converters/html.py +98 -0
- dataface/core/render/converters/pdf.py +40 -0
- dataface/core/render/converters/png.py +41 -0
- dataface/core/render/errors.py +144 -0
- dataface/core/render/face_api.py +160 -0
- dataface/core/render/faces.py +1194 -0
- dataface/core/render/font_measurement.py +48 -0
- dataface/core/render/font_support.py +197 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/InterVariable.ttf +0 -0
- dataface/core/render/fonts/InterVariable.woff2 +0 -0
- dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
- dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
- dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
- dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
- dataface/core/render/fonts/_emoji_font_face.css +43 -0
- dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
- dataface/core/render/format_utils.py +329 -0
- dataface/core/render/geo_defaults.yml +28 -0
- dataface/core/render/json_format.py +146 -0
- dataface/core/render/layout_sizing.py +865 -0
- dataface/core/render/layouts.py +541 -0
- dataface/core/render/markdown_defaults.yml +16 -0
- dataface/core/render/missing_vars_prompt.py +79 -0
- dataface/core/render/placeholder.py +389 -0
- dataface/core/render/render_result.py +14 -0
- dataface/core/render/renderer.py +467 -0
- dataface/core/render/script_embedding.py +16 -0
- dataface/core/render/svg_utils.py +212 -0
- dataface/core/render/template_loader.py +69 -0
- dataface/core/render/templates/controls/_styles.css +606 -0
- dataface/core/render/templates/controls/checkbox.html +16 -0
- dataface/core/render/templates/controls/date.html +16 -0
- dataface/core/render/templates/controls/number.html +19 -0
- dataface/core/render/templates/controls/readonly.html +9 -0
- dataface/core/render/templates/controls/select.html +21 -0
- dataface/core/render/templates/controls/slider.html +22 -0
- dataface/core/render/templates/controls/text.html +16 -0
- dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
- dataface/core/render/templates/scripts/variables.js +976 -0
- dataface/core/render/templates/svg/grid_pattern.svg +3 -0
- dataface/core/render/templates/svg/styles.css +51 -0
- dataface/core/render/terminal.py +311 -0
- dataface/core/render/terminal_charts.py +563 -0
- dataface/core/render/terminal_defaults.yml +2 -0
- dataface/core/render/terminal_layouts.py +299 -0
- dataface/core/render/terminal_text.py +31 -0
- dataface/core/render/text/__init__.py +1 -0
- dataface/core/render/text/case.py +113 -0
- dataface/core/render/text_format.py +129 -0
- dataface/core/render/utils.py +106 -0
- dataface/core/render/variable_controls.py +946 -0
- dataface/core/render/variable_input_refinement.py +140 -0
- dataface/core/render/warnings/__init__.py +15 -0
- dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
- dataface/core/render/warnings/base.py +44 -0
- dataface/core/render/warnings/fanout_risk.py +15 -0
- dataface/core/render/warnings/from_query_diagnostic.py +56 -0
- dataface/core/render/warnings/missing_join_predicate.py +13 -0
- dataface/core/render/warnings/query_parse_error.py +14 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
- dataface/core/render/warnings/reaggregation.py +14 -0
- dataface/core/render/warnings/registry.py +45 -0
- dataface/core/render/warnings/suppression.py +46 -0
- dataface/core/render/warnings/temporal_single_point.py +63 -0
- dataface/core/render/warnings/unreferenced_chart.py +15 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
- dataface/core/render/yaml_format.py +167 -0
- dataface/core/resolve_face.py +195 -0
- dataface/core/schema/__init__.py +0 -0
- dataface/core/schema/guidance.py +151 -0
- dataface/core/scoped_paths.py +59 -0
- dataface/core/serve/__init__.py +14 -0
- dataface/core/serve/bootstrap.py +39 -0
- dataface/core/serve/embedded.py +57 -0
- dataface/core/serve/port.py +129 -0
- dataface/core/serve/server.py +938 -0
- dataface/core/serve/templates/__init__.py +0 -0
- dataface/core/serve/templates/directory.yml +6 -0
- dataface/core/serve/templates/error.html.j2 +217 -0
- dataface/core/utils.py +121 -0
- dataface/core/validate.py +64 -0
- dataface/integrations/__init__.py +0 -0
- dataface/integrations/highlighting.py +351 -0
- dataface/integrations/markdown.py +537 -0
- dataface/py.typed +0 -0
- dataface-0.1.2.dist-info/METADATA +375 -0
- dataface-0.1.2.dist-info/RECORD +455 -0
- dataface-0.1.2.dist-info/WHEEL +4 -0
- dataface-0.1.2.dist-info/entry_points.txt +2 -0
- dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
- mdsvg/__init__.py +168 -0
- mdsvg/fonts.py +656 -0
- mdsvg/images.py +299 -0
- mdsvg/parser.py +629 -0
- mdsvg/playground.py +284 -0
- mdsvg/py.typed +2 -0
- mdsvg/renderer.py +1623 -0
- mdsvg/style.py +355 -0
- mdsvg/types.py +200 -0
- mdsvg/utils.py +86 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""YAML parsing module.
|
|
2
|
+
|
|
3
|
+
Stage: COMPILE (Step 1 of 4)
|
|
4
|
+
Purpose: Parse YAML strings into AuthoredFace input types.
|
|
5
|
+
|
|
6
|
+
Entry Points:
|
|
7
|
+
- parse_yaml(content: str) -> AuthoredFace
|
|
8
|
+
|
|
9
|
+
Inputs:
|
|
10
|
+
- YAML string (face definition)
|
|
11
|
+
|
|
12
|
+
Outputs:
|
|
13
|
+
- AuthoredFace (input type with optional fields)
|
|
14
|
+
|
|
15
|
+
Dependencies:
|
|
16
|
+
- yaml (PyYAML)
|
|
17
|
+
- .types (AuthoredFace)
|
|
18
|
+
|
|
19
|
+
Errors:
|
|
20
|
+
- ParseError: Invalid YAML syntax (with line numbers, context, suggestions)
|
|
21
|
+
|
|
22
|
+
See also:
|
|
23
|
+
- docs/docs/contributing/architecture.md for the pipeline overview
|
|
24
|
+
- compile/validator.py for the next step
|
|
25
|
+
|
|
26
|
+
Refs #94
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import re
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
import yaml
|
|
33
|
+
|
|
34
|
+
from dataface.core.compile.errors import ParseError
|
|
35
|
+
from dataface.core.compile.models.face.authored import AuthoredFace
|
|
36
|
+
from dataface.core.compile.models.refs import normalize_query_value
|
|
37
|
+
from dataface.core.errors.codes_compile import DF_COMPILE_EXTRA_FIELD
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def parse_yaml(content: str) -> AuthoredFace:
|
|
41
|
+
"""Parse YAML content into a AuthoredFace object.
|
|
42
|
+
|
|
43
|
+
Stage: COMPILE (Step 1 of 4: Parsing)
|
|
44
|
+
|
|
45
|
+
This is the first step of compilation. It converts a raw YAML string
|
|
46
|
+
into a structured AuthoredFace object. Only basic syntax validation happens
|
|
47
|
+
here - schema validation is the next step.
|
|
48
|
+
|
|
49
|
+
Enhanced error messages include:
|
|
50
|
+
- Line numbers where errors occur
|
|
51
|
+
- YAML context showing the problematic snippet
|
|
52
|
+
- Helpful suggestions ("Did you mean?")
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
content: Raw YAML string to parse
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
AuthoredFace object with parsed structure
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ParseError: If YAML syntax is invalid or parsing fails
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
>>> yaml_content = '''
|
|
65
|
+
... title: My Dataface
|
|
66
|
+
... queries:
|
|
67
|
+
... sales: SELECT * FROM sales
|
|
68
|
+
... charts:
|
|
69
|
+
... revenue:
|
|
70
|
+
... query: sales
|
|
71
|
+
... type: line
|
|
72
|
+
... rows:
|
|
73
|
+
... - revenue
|
|
74
|
+
... '''
|
|
75
|
+
>>> face = parse_yaml(yaml_content)
|
|
76
|
+
>>> face.title
|
|
77
|
+
'My Dataface'
|
|
78
|
+
"""
|
|
79
|
+
# Step 1a: Parse YAML to dict
|
|
80
|
+
try:
|
|
81
|
+
parsed_data = yaml.safe_load(content)
|
|
82
|
+
except yaml.YAMLError as e:
|
|
83
|
+
# Extract line number from YAML error and provide context
|
|
84
|
+
line_num = _extract_line_from_yaml_error(str(e))
|
|
85
|
+
context = _get_yaml_context_for_error(content, line_num) if line_num else None
|
|
86
|
+
suggestion = _get_yaml_parse_suggestion(str(e))
|
|
87
|
+
raise ParseError(
|
|
88
|
+
f"Invalid YAML syntax: {e}",
|
|
89
|
+
line=line_num,
|
|
90
|
+
context=context,
|
|
91
|
+
suggestion=suggestion,
|
|
92
|
+
) from e
|
|
93
|
+
|
|
94
|
+
# Step 1b: Handle empty content
|
|
95
|
+
if parsed_data is None:
|
|
96
|
+
raise ParseError("Empty YAML document")
|
|
97
|
+
|
|
98
|
+
if not isinstance(parsed_data, dict):
|
|
99
|
+
raise ParseError(f"YAML must be a mapping, got {type(parsed_data).__name__}")
|
|
100
|
+
|
|
101
|
+
# Step 1c: Normalize queries (infer types)
|
|
102
|
+
if "queries" in parsed_data and isinstance(parsed_data["queries"], dict):
|
|
103
|
+
parsed_data["queries"] = _normalize_query_definitions(parsed_data["queries"])
|
|
104
|
+
|
|
105
|
+
# Step 1d: Convert to AuthoredFace with enhanced error messages
|
|
106
|
+
from pydantic import ValidationError as PydanticValidationError
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
return AuthoredFace(**parsed_data)
|
|
110
|
+
except PydanticValidationError as e:
|
|
111
|
+
# Enhance with line-number context and suggestions first.
|
|
112
|
+
try:
|
|
113
|
+
from dataface.core.compile.yaml_error_formatter import (
|
|
114
|
+
format_validation_error,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
enhanced_msg = format_validation_error(e, content)
|
|
118
|
+
err = ParseError(enhanced_msg)
|
|
119
|
+
except ParseError:
|
|
120
|
+
raise
|
|
121
|
+
except ImportError:
|
|
122
|
+
err = ParseError(f"Failed to parse face structure: {e}")
|
|
123
|
+
|
|
124
|
+
# Stamp the typed code when the primary failure is an extra field — the
|
|
125
|
+
# enhanced message is preserved; only the code changes from DF-UNKNOWN-INTERNAL.
|
|
126
|
+
pydantic_errors = e.errors()
|
|
127
|
+
if pydantic_errors and pydantic_errors[0].get("type") == "extra_forbidden":
|
|
128
|
+
err.code = DF_COMPILE_EXTRA_FIELD
|
|
129
|
+
|
|
130
|
+
raise err from e
|
|
131
|
+
except (TypeError, ValueError) as e:
|
|
132
|
+
# Other parsing errors (wrong types, invalid values)
|
|
133
|
+
raise ParseError(f"Failed to parse face structure: {e}") from e
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _extract_line_from_yaml_error(error_msg: str) -> int | None:
|
|
137
|
+
"""Extract line number from a YAML error message.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
error_msg: The YAML error message
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Line number or None if not found
|
|
144
|
+
"""
|
|
145
|
+
# PyYAML errors often contain "line X"
|
|
146
|
+
match = re.search(r"line\s+(\d+)", error_msg, re.I)
|
|
147
|
+
if match:
|
|
148
|
+
return int(match.group(1))
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _get_yaml_context_for_error(
|
|
153
|
+
content: str,
|
|
154
|
+
line_num: int,
|
|
155
|
+
context_lines: int = 2,
|
|
156
|
+
) -> str:
|
|
157
|
+
"""Get YAML context around an error line.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
content: Full YAML content
|
|
161
|
+
line_num: Line number of the error (1-indexed)
|
|
162
|
+
context_lines: Number of lines to show before/after
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Formatted context string
|
|
166
|
+
"""
|
|
167
|
+
from dataface.core.compile.yaml_error_formatter import get_yaml_context
|
|
168
|
+
|
|
169
|
+
return get_yaml_context(content, line_num, context_lines)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _get_yaml_parse_suggestion(error_msg: str) -> str | None:
|
|
173
|
+
"""Get a helpful suggestion for a YAML parse error.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
error_msg: The error message
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Suggestion string or None
|
|
180
|
+
"""
|
|
181
|
+
error_lower = error_msg.lower()
|
|
182
|
+
|
|
183
|
+
if "indent" in error_lower:
|
|
184
|
+
return "💡 Check your indentation - YAML requires consistent spacing (typically 2 spaces)"
|
|
185
|
+
elif "expected" in error_lower and "block" in error_lower:
|
|
186
|
+
return "💡 This often happens with incorrect indentation or missing colons"
|
|
187
|
+
elif "mapping" in error_lower:
|
|
188
|
+
return "💡 Check for missing colons after key names or incorrect nesting"
|
|
189
|
+
elif "duplicate" in error_lower:
|
|
190
|
+
return "💡 You have duplicate keys - each key name must be unique at the same level"
|
|
191
|
+
elif "found character" in error_lower:
|
|
192
|
+
return (
|
|
193
|
+
"💡 Check for special characters that need quoting, or invalid YAML syntax"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _normalize_query_definitions(queries: dict[str, Any]) -> dict[str, Any]:
|
|
200
|
+
"""Normalize query definitions to consistent format.
|
|
201
|
+
|
|
202
|
+
Converts shorthand query formats to full format:
|
|
203
|
+
- String: "SELECT ..." -> {"type": "sql", "sql": "SELECT ..."}
|
|
204
|
+
- Dict without type: Infers type from keys
|
|
205
|
+
|
|
206
|
+
Delegates to normalize_query_value (models/refs.py) — the shared helper
|
|
207
|
+
used by both this function and AuthoredFace._normalize_queries.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
queries: Raw query definitions from YAML
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Normalized query definitions with type field
|
|
214
|
+
"""
|
|
215
|
+
return {
|
|
216
|
+
name: normalize_query_value(query_def) for name, query_def in queries.items()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
_SQL_PREFIX_RE = re.compile(
|
|
221
|
+
r"^\s*(SELECT|WITH|PRAGMA|INSERT|UPDATE|DELETE|CREATE)\b",
|
|
222
|
+
re.IGNORECASE,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def looks_like_sql(s: str) -> bool:
|
|
227
|
+
"""Return True when a string starts like a raw SQL statement."""
|
|
228
|
+
return bool(_SQL_PREFIX_RE.match(s))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Schema documentation generation for AI integrations.
|
|
2
|
+
|
|
3
|
+
Delegates to the two-layer IR pipeline:
|
|
4
|
+
introspect() → AuthorableSchema → render_prompt() → str
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataface.core.compile.introspection import introspect
|
|
8
|
+
from dataface.core.compile.schema_renderers.prompt import render_prompt
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_schema_for_prompt() -> str:
|
|
12
|
+
"""Get complete schema documentation for AI prompts."""
|
|
13
|
+
return render_prompt(introspect())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = ["get_schema_for_prompt"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
print(get_schema_for_prompt())
|
|
File without changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""JSON Schema renderer — Layer 2 of the two-layer schema IR.
|
|
2
|
+
|
|
3
|
+
Converts an AuthorableSchema IR to a draft-07 JSON Schema dict.
|
|
4
|
+
Callers: IDE schema generation, validation tooling.
|
|
5
|
+
|
|
6
|
+
Entry point: render_json_schema(schema: AuthorableSchema) -> dict
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import dataclasses
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pydantic_core import PydanticUndefined
|
|
15
|
+
|
|
16
|
+
from dataface.core.compile.introspection import AuthorableSchema, SchemaField
|
|
17
|
+
|
|
18
|
+
_PRIMITIVES: dict[str, dict[str, Any]] = {
|
|
19
|
+
"str": {"type": "string"},
|
|
20
|
+
"int": {"type": "integer"},
|
|
21
|
+
"float": {"type": "number"},
|
|
22
|
+
"bool": {"type": "boolean"},
|
|
23
|
+
"None": {"type": "null"},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _schema_for_token(token: str) -> dict[str, Any]:
|
|
28
|
+
"""Map a single non-union type token to a JSON Schema dict."""
|
|
29
|
+
if token in _PRIMITIVES:
|
|
30
|
+
return _PRIMITIVES[token]
|
|
31
|
+
if token.startswith("list["):
|
|
32
|
+
return {"type": "array"}
|
|
33
|
+
if token.startswith("dict["):
|
|
34
|
+
return {"type": "object"}
|
|
35
|
+
return {}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _type_schema(field: SchemaField) -> dict[str, Any]:
|
|
39
|
+
"""Build the JSON Schema type object for a single field."""
|
|
40
|
+
nullable = "None" in field.type_repr
|
|
41
|
+
|
|
42
|
+
extra = [_PRIMITIVES[t] for t in field.extra_union_types if t in _PRIMITIVES]
|
|
43
|
+
|
|
44
|
+
if field.enum_values is not None:
|
|
45
|
+
base: dict[str, Any] = {"enum": field.enum_values}
|
|
46
|
+
branches: list[dict[str, Any]] = [base] + extra
|
|
47
|
+
if nullable:
|
|
48
|
+
branches.append({"type": "null"})
|
|
49
|
+
return {"anyOf": branches} if len(branches) > 1 else branches[0]
|
|
50
|
+
|
|
51
|
+
if field.nested_models:
|
|
52
|
+
refs: list[dict[str, Any]] = [
|
|
53
|
+
{"$ref": f"#/$defs/{n}"} for n in field.nested_models
|
|
54
|
+
]
|
|
55
|
+
inner: dict[str, Any] = refs[0] if len(refs) == 1 else {"anyOf": refs}
|
|
56
|
+
if field.container == "list":
|
|
57
|
+
arr: dict[str, Any] = {"type": "array", "items": inner}
|
|
58
|
+
parts: list[dict[str, Any]] = [arr] + extra
|
|
59
|
+
if nullable:
|
|
60
|
+
parts.append({"type": "null"})
|
|
61
|
+
return {"anyOf": parts} if len(parts) > 1 else parts[0]
|
|
62
|
+
if field.container == "dict":
|
|
63
|
+
obj: dict[str, Any] = {"type": "object", "additionalProperties": inner}
|
|
64
|
+
parts = [obj] + extra
|
|
65
|
+
if nullable:
|
|
66
|
+
parts.append({"type": "null"})
|
|
67
|
+
return {"anyOf": parts} if len(parts) > 1 else parts[0]
|
|
68
|
+
all_branches: list[dict[str, Any]] = refs + extra
|
|
69
|
+
if nullable:
|
|
70
|
+
all_branches.append({"type": "null"})
|
|
71
|
+
return {"anyOf": all_branches} if len(all_branches) > 1 else all_branches[0]
|
|
72
|
+
|
|
73
|
+
return _map_type_repr(field.type_repr)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _map_type_repr(type_repr: str) -> dict[str, Any]:
|
|
77
|
+
"""Map a type_repr string to a JSON Schema type dict."""
|
|
78
|
+
nullable = " | None" in type_repr
|
|
79
|
+
base = type_repr.replace(" | None", "").strip()
|
|
80
|
+
|
|
81
|
+
if base in _PRIMITIVES:
|
|
82
|
+
schema: dict[str, Any] = _PRIMITIVES[base]
|
|
83
|
+
elif base.startswith("list["):
|
|
84
|
+
schema = {"type": "array"}
|
|
85
|
+
elif base.startswith("dict["):
|
|
86
|
+
schema = {"type": "object"}
|
|
87
|
+
elif " | " in base:
|
|
88
|
+
parts = [p.strip() for p in base.split(" | ")]
|
|
89
|
+
schemas = [s for p in parts if (s := _schema_for_token(p))]
|
|
90
|
+
schema = {"anyOf": schemas} if schemas else {}
|
|
91
|
+
else:
|
|
92
|
+
schema = {}
|
|
93
|
+
|
|
94
|
+
if not nullable or not schema:
|
|
95
|
+
return schema
|
|
96
|
+
if "anyOf" in schema:
|
|
97
|
+
schema = dict(schema)
|
|
98
|
+
schema["anyOf"] = list(schema["anyOf"]) + [{"type": "null"}]
|
|
99
|
+
return schema
|
|
100
|
+
return {"anyOf": [schema, {"type": "null"}]}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _model_to_def(model_name: str, ir: AuthorableSchema) -> dict[str, Any]:
|
|
104
|
+
"""Build a JSON Schema $defs entry for one AuthorableModel."""
|
|
105
|
+
model = ir.models[model_name]
|
|
106
|
+
required = [f.name for f in model.fields if f.required and not f.is_extra_key]
|
|
107
|
+
properties: dict[str, Any] = {}
|
|
108
|
+
additional_properties: dict[str, Any] | None = None
|
|
109
|
+
for field in model.fields:
|
|
110
|
+
prop: dict[str, Any] = _type_schema(field)
|
|
111
|
+
if field.description:
|
|
112
|
+
prop = dict(prop)
|
|
113
|
+
prop["description"] = field.description
|
|
114
|
+
if (
|
|
115
|
+
not field.required
|
|
116
|
+
and field.default is not dataclasses.MISSING
|
|
117
|
+
and field.default is not None
|
|
118
|
+
and field.default is not PydanticUndefined
|
|
119
|
+
):
|
|
120
|
+
prop = dict(prop)
|
|
121
|
+
prop["default"] = field.default
|
|
122
|
+
if field.is_extra_key:
|
|
123
|
+
additional_properties = prop
|
|
124
|
+
else:
|
|
125
|
+
properties[field.name] = prop
|
|
126
|
+
|
|
127
|
+
defn: dict[str, Any] = {"type": "object", "properties": properties}
|
|
128
|
+
if model.doc:
|
|
129
|
+
defn["description"] = model.doc
|
|
130
|
+
if required:
|
|
131
|
+
defn["required"] = required
|
|
132
|
+
if additional_properties is not None:
|
|
133
|
+
defn["additionalProperties"] = additional_properties
|
|
134
|
+
return defn
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def render_json_schema(schema: AuthorableSchema) -> dict[str, Any]:
|
|
138
|
+
"""Render an AuthorableSchema IR as a draft-07 JSON Schema.
|
|
139
|
+
|
|
140
|
+
Produces a self-contained schema with the root model's properties
|
|
141
|
+
inlined and all reachable authored models in $defs.
|
|
142
|
+
"""
|
|
143
|
+
root = _model_to_def(schema.root, schema)
|
|
144
|
+
defs = {
|
|
145
|
+
name: _model_to_def(name, schema)
|
|
146
|
+
for name in schema.models
|
|
147
|
+
if name != schema.root
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
result: dict[str, Any] = {
|
|
151
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
152
|
+
"title": schema.root,
|
|
153
|
+
}
|
|
154
|
+
if root.get("description"):
|
|
155
|
+
result["description"] = root["description"]
|
|
156
|
+
result["type"] = "object"
|
|
157
|
+
result["properties"] = root.get("properties", {})
|
|
158
|
+
if root.get("required"):
|
|
159
|
+
result["required"] = root["required"]
|
|
160
|
+
if defs:
|
|
161
|
+
result["$defs"] = defs
|
|
162
|
+
|
|
163
|
+
return result
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Prompt renderer — Layer 2 of the two-layer schema IR.
|
|
2
|
+
|
|
3
|
+
Converts an AuthorableSchema IR to a concise text reference for AI prompts.
|
|
4
|
+
Replaces the hand-written schema.get_schema_for_prompt().
|
|
5
|
+
|
|
6
|
+
Entry point: render_prompt(schema: AuthorableSchema) -> str
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from dataface.core.compile.introspection import AuthorableSchema, SchemaField
|
|
14
|
+
|
|
15
|
+
# Source connector configs are leaf models with no cross-references; emit them last.
|
|
16
|
+
_SOURCE_CONFIG_NAMES = [
|
|
17
|
+
"PostgresSourceConfig",
|
|
18
|
+
"SnowflakeSourceConfig",
|
|
19
|
+
"BigQuerySourceConfig",
|
|
20
|
+
"RedshiftSourceConfig",
|
|
21
|
+
"MySQLSourceConfig",
|
|
22
|
+
"DuckDBSourceConfig",
|
|
23
|
+
"CsvSourceConfig",
|
|
24
|
+
"ParquetSourceConfig",
|
|
25
|
+
"JsonSourceConfig",
|
|
26
|
+
"HttpSourceConfig",
|
|
27
|
+
"DbtProfileSourceConfig",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _display_name(class_name: str) -> str:
|
|
32
|
+
"""Strip 'Authored' prefix and 'Patch' suffix for user-facing model headings."""
|
|
33
|
+
name = class_name
|
|
34
|
+
if name.startswith("Authored"):
|
|
35
|
+
name = name[len("Authored") :]
|
|
36
|
+
if name.endswith("Patch"):
|
|
37
|
+
name = name[: -len("Patch")]
|
|
38
|
+
return name
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _bfs(
|
|
42
|
+
start: str, schema: AuthorableSchema, seen: set[str], order: list[str]
|
|
43
|
+
) -> None:
|
|
44
|
+
queue = [start]
|
|
45
|
+
while queue:
|
|
46
|
+
name = queue.pop(0)
|
|
47
|
+
if name in seen or name not in schema.models:
|
|
48
|
+
continue
|
|
49
|
+
seen.add(name)
|
|
50
|
+
order.append(name)
|
|
51
|
+
for field in schema.models[name].fields:
|
|
52
|
+
for nested in field.nested_models:
|
|
53
|
+
if nested not in seen:
|
|
54
|
+
queue.append(nested)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _ordered_model_names(schema: AuthorableSchema) -> list[str]:
|
|
58
|
+
"""Return model names in a sensible top-down order.
|
|
59
|
+
|
|
60
|
+
1. BFS from root (AuthoredFace + all models reachable via type annotations)
|
|
61
|
+
2. BFS from AuthoredChart (not reachable via AuthoredFace annotations)
|
|
62
|
+
3. PaginationConfig
|
|
63
|
+
4. Source connector configs (grouped, leaf-level)
|
|
64
|
+
5. Any remaining models
|
|
65
|
+
"""
|
|
66
|
+
order: list[str] = []
|
|
67
|
+
seen: set[str] = set()
|
|
68
|
+
|
|
69
|
+
_bfs(schema.root, schema, seen, order)
|
|
70
|
+
_bfs("AuthoredChart", schema, seen, order)
|
|
71
|
+
_bfs("PaginationConfig", schema, seen, order)
|
|
72
|
+
for name in _SOURCE_CONFIG_NAMES:
|
|
73
|
+
_bfs(name, schema, seen, order)
|
|
74
|
+
for name in schema.models:
|
|
75
|
+
if name not in seen:
|
|
76
|
+
order.append(name)
|
|
77
|
+
|
|
78
|
+
return order
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _type_cell(field: SchemaField) -> str:
|
|
82
|
+
if field.enum_values is not None:
|
|
83
|
+
return "enum: " + ", ".join(f'"{v}"' for v in field.enum_values)
|
|
84
|
+
t = re.sub(r"\s*\|\s*None\b", "", field.type_repr)
|
|
85
|
+
t = re.sub(r"\bNone\s*\|\s*", "", t)
|
|
86
|
+
for model_name in field.nested_models:
|
|
87
|
+
display = _display_name(model_name)
|
|
88
|
+
# Use word-boundary replacement to avoid matching model_name as a substring
|
|
89
|
+
# of a longer class name (e.g. "BarChart" inside "SparkBarChart").
|
|
90
|
+
t = re.sub(
|
|
91
|
+
r"\b" + re.escape(model_name) + r"\b",
|
|
92
|
+
f"[{display}](#{_anchor(display)})",
|
|
93
|
+
t,
|
|
94
|
+
)
|
|
95
|
+
return t.replace("|", "\\|")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _anchor(display_name: str) -> str:
|
|
99
|
+
return display_name.lower()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _render_model(name: str, schema: AuthorableSchema) -> str:
|
|
103
|
+
model = schema.models[name]
|
|
104
|
+
display = _display_name(name)
|
|
105
|
+
lines = [f'<a id="{_anchor(display)}"></a>', f"## {display}"]
|
|
106
|
+
if model.doc:
|
|
107
|
+
lines.append(model.doc)
|
|
108
|
+
lines.append("")
|
|
109
|
+
|
|
110
|
+
required = [f for f in model.fields if f.required]
|
|
111
|
+
optional = [f for f in model.fields if not f.required]
|
|
112
|
+
fields = required + optional
|
|
113
|
+
|
|
114
|
+
show_optional = any(not f.required for f in fields)
|
|
115
|
+
|
|
116
|
+
header = "| Field | Type |"
|
|
117
|
+
sep = "|-------|------|"
|
|
118
|
+
if show_optional:
|
|
119
|
+
header += " Optional |"
|
|
120
|
+
sep += ":--------:|"
|
|
121
|
+
header += " Description |"
|
|
122
|
+
sep += "-------------|"
|
|
123
|
+
|
|
124
|
+
lines.append(header)
|
|
125
|
+
lines.append(sep)
|
|
126
|
+
|
|
127
|
+
for field in fields:
|
|
128
|
+
desc = (field.description or "").replace("|", "\\|")
|
|
129
|
+
row = f"| `{field.name}` | {_type_cell(field)} |"
|
|
130
|
+
if show_optional:
|
|
131
|
+
row += f" {'✓' if not field.required else ''} |"
|
|
132
|
+
row += f" {desc} |"
|
|
133
|
+
lines.append(row)
|
|
134
|
+
|
|
135
|
+
return "\n".join(lines)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def render_prompt(schema: AuthorableSchema) -> str:
|
|
139
|
+
"""Render an AuthorableSchema IR as a YAML schema reference table.
|
|
140
|
+
|
|
141
|
+
Suitable for inclusion in AI prompts. Each authored model is rendered as a
|
|
142
|
+
markdown table with required fields first, then optional fields.
|
|
143
|
+
Models are ordered top-down: root first, then referenced sub-models (BFS),
|
|
144
|
+
then chart patch models, then source connector configs.
|
|
145
|
+
"""
|
|
146
|
+
order = _ordered_model_names(schema)
|
|
147
|
+
parts = ["# Dataface YAML Schema Reference", ""]
|
|
148
|
+
for i, name in enumerate(order):
|
|
149
|
+
if i > 0:
|
|
150
|
+
parts.append("")
|
|
151
|
+
parts.append(_render_model(name, schema))
|
|
152
|
+
return "\n".join(parts)
|