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.
Files changed (455) hide show
  1. d3_format/__init__.py +14 -0
  2. d3_format/errors.py +19 -0
  3. d3_format/format.py +551 -0
  4. d3_format/spec.py +159 -0
  5. dataface/DATAFACE_SYNTAX.md +1135 -0
  6. dataface/__init__.py +93 -0
  7. dataface/_docs_site.py +20 -0
  8. dataface/_install_hint.py +26 -0
  9. dataface/agent_api/__init__.py +79 -0
  10. dataface/agent_api/_init_templates/__init__.py +0 -0
  11. dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
  12. dataface/agent_api/_init_templates/dataface.yml +15 -0
  13. dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
  14. dataface/agent_api/_init_templates/index.md +24 -0
  15. dataface/agent_api/_paths.py +118 -0
  16. dataface/agent_api/_project_agents_md.py +43 -0
  17. dataface/agent_api/_session_store.py +486 -0
  18. dataface/agent_api/_state.py +28 -0
  19. dataface/agent_api/chat.py +221 -0
  20. dataface/agent_api/dashboards.py +257 -0
  21. dataface/agent_api/describe.py +366 -0
  22. dataface/agent_api/describe_query.py +120 -0
  23. dataface/agent_api/docs/__init__.py +25 -0
  24. dataface/agent_api/docs/_loader.py +292 -0
  25. dataface/agent_api/docs/yaml-reference.md +2757 -0
  26. dataface/agent_api/file_refs.py +118 -0
  27. dataface/agent_api/init.py +126 -0
  28. dataface/agent_api/inspect.py +128 -0
  29. dataface/agent_api/mcp_install.py +170 -0
  30. dataface/agent_api/query.py +274 -0
  31. dataface/agent_api/schema.py +658 -0
  32. dataface/agent_api/schema_search.py +284 -0
  33. dataface/agent_api/search.py +270 -0
  34. dataface/agent_api/skill_install.py +141 -0
  35. dataface/agent_api/skill_render.py +90 -0
  36. dataface/agent_api/skills.py +293 -0
  37. dataface/agent_api/surface_aliases.yaml +128 -0
  38. dataface/agent_api/validate.py +175 -0
  39. dataface/agent_api/validate_query.py +84 -0
  40. dataface/ai/__init__.py +39 -0
  41. dataface/ai/agent.py +139 -0
  42. dataface/ai/context.py +45 -0
  43. dataface/ai/events.py +62 -0
  44. dataface/ai/external_mcp.py +610 -0
  45. dataface/ai/generate_sql.py +96 -0
  46. dataface/ai/llm.py +403 -0
  47. dataface/ai/mcp/__init__.py +51 -0
  48. dataface/ai/mcp/server.py +289 -0
  49. dataface/ai/memories.py +85 -0
  50. dataface/ai/prompts.py +177 -0
  51. dataface/ai/schema_context.py +138 -0
  52. dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
  53. dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
  54. dataface/ai/skills/dashboard-build/SKILL.md +212 -0
  55. dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
  56. dataface/ai/skills/dashboard-design/SKILL.md +182 -0
  57. dataface/ai/skills/dashboard-review/SKILL.md +113 -0
  58. dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
  59. dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
  60. dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
  61. dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
  62. dataface/ai/skills/drill-down-link/SKILL.md +112 -0
  63. dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
  64. dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
  65. dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
  66. dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
  67. dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
  68. dataface/ai/skills/kpi-row/SKILL.md +101 -0
  69. dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
  70. dataface/ai/skills/report-design/SKILL.md +184 -0
  71. dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
  72. dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
  73. dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
  74. dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
  75. dataface/ai/skills/time-series-trend/SKILL.md +93 -0
  76. dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
  77. dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
  78. dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
  79. dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
  80. dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
  81. dataface/ai/tool_schemas.py +132 -0
  82. dataface/ai/tools/__init__.py +312 -0
  83. dataface/ai/yaml_utils.py +57 -0
  84. dataface/cli/__init__.py +3 -0
  85. dataface/cli/_console.py +48 -0
  86. dataface/cli/_error_format.py +83 -0
  87. dataface/cli/_extras.py +190 -0
  88. dataface/cli/_json_output.py +8 -0
  89. dataface/cli/_parsing.py +17 -0
  90. dataface/cli/_version_info.py +56 -0
  91. dataface/cli/commands/__init__.py +3 -0
  92. dataface/cli/commands/_agent_input.py +205 -0
  93. dataface/cli/commands/_agent_server.py +115 -0
  94. dataface/cli/commands/chat.py +645 -0
  95. dataface/cli/commands/describe.py +107 -0
  96. dataface/cli/commands/docs.py +131 -0
  97. dataface/cli/commands/extension.py +179 -0
  98. dataface/cli/commands/init.py +240 -0
  99. dataface/cli/commands/inspect.py +94 -0
  100. dataface/cli/commands/mcp_init.py +167 -0
  101. dataface/cli/commands/query.py +386 -0
  102. dataface/cli/commands/render.py +291 -0
  103. dataface/cli/commands/schema.py +411 -0
  104. dataface/cli/commands/search.py +49 -0
  105. dataface/cli/commands/serve.py +114 -0
  106. dataface/cli/commands/skills.py +133 -0
  107. dataface/cli/commands/skills_init.py +161 -0
  108. dataface/cli/commands/validate.py +63 -0
  109. dataface/cli/main.py +1501 -0
  110. dataface/core/__init__.py +75 -0
  111. dataface/core/compile/__init__.py +244 -0
  112. dataface/core/compile/_jinja_helpers.py +78 -0
  113. dataface/core/compile/channel.py +222 -0
  114. dataface/core/compile/chart_focus.py +101 -0
  115. dataface/core/compile/chart_resolved.py +169 -0
  116. dataface/core/compile/chart_type_detection.py +489 -0
  117. dataface/core/compile/chart_update.py +261 -0
  118. dataface/core/compile/colors.py +64 -0
  119. dataface/core/compile/compiler.py +904 -0
  120. dataface/core/compile/config.py +823 -0
  121. dataface/core/compile/custom_chart_types.py +208 -0
  122. dataface/core/compile/data_table_attachment.py +1287 -0
  123. dataface/core/compile/detect.py +110 -0
  124. dataface/core/compile/errors.py +302 -0
  125. dataface/core/compile/filter_injection.py +319 -0
  126. dataface/core/compile/introspection.py +527 -0
  127. dataface/core/compile/jinja.py +511 -0
  128. dataface/core/compile/labels_env.py +52 -0
  129. dataface/core/compile/markdown.py +154 -0
  130. dataface/core/compile/meta.py +388 -0
  131. dataface/core/compile/models/__init__.py +0 -0
  132. dataface/core/compile/models/chart/__init__.py +0 -0
  133. dataface/core/compile/models/chart/authored.py +2137 -0
  134. dataface/core/compile/models/chart/compiled.py +398 -0
  135. dataface/core/compile/models/config.py +347 -0
  136. dataface/core/compile/models/face/__init__.py +0 -0
  137. dataface/core/compile/models/face/authored.py +659 -0
  138. dataface/core/compile/models/face/compiled.py +522 -0
  139. dataface/core/compile/models/factories.py +201 -0
  140. dataface/core/compile/models/markers.py +40 -0
  141. dataface/core/compile/models/palette.py +36 -0
  142. dataface/core/compile/models/primitives.py +415 -0
  143. dataface/core/compile/models/query/__init__.py +0 -0
  144. dataface/core/compile/models/query/authored.py +246 -0
  145. dataface/core/compile/models/query/compiled.py +710 -0
  146. dataface/core/compile/models/refs.py +137 -0
  147. dataface/core/compile/models/source.py +611 -0
  148. dataface/core/compile/models/style/__init__.py +0 -0
  149. dataface/core/compile/models/style/authored.py +481 -0
  150. dataface/core/compile/models/style/compiled.py +3399 -0
  151. dataface/core/compile/models/style/merged.py +1682 -0
  152. dataface/core/compile/models/theme.py +362 -0
  153. dataface/core/compile/models/variable/__init__.py +0 -0
  154. dataface/core/compile/models/variable/authored.py +254 -0
  155. dataface/core/compile/models/vega_lite/__init__.py +0 -0
  156. dataface/core/compile/models/vega_lite/config.py +510 -0
  157. dataface/core/compile/models/vega_lite/contracts.py +171 -0
  158. dataface/core/compile/normalize_charts.py +494 -0
  159. dataface/core/compile/normalize_layout.py +1000 -0
  160. dataface/core/compile/normalize_queries.py +297 -0
  161. dataface/core/compile/normalize_variables.py +489 -0
  162. dataface/core/compile/normalizer.py +543 -0
  163. dataface/core/compile/palette.py +1100 -0
  164. dataface/core/compile/parameterized.py +658 -0
  165. dataface/core/compile/parser.py +228 -0
  166. dataface/core/compile/schema.py +20 -0
  167. dataface/core/compile/schema_renderers/__init__.py +0 -0
  168. dataface/core/compile/schema_renderers/json_schema.py +163 -0
  169. dataface/core/compile/schema_renderers/prompt.py +152 -0
  170. dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
  171. dataface/core/compile/sizing.py +2126 -0
  172. dataface/core/compile/sources.py +518 -0
  173. dataface/core/compile/sql_authoring_lint.py +56 -0
  174. dataface/core/compile/style_cascade.py +471 -0
  175. dataface/core/compile/typography.py +299 -0
  176. dataface/core/compile/validator.py +301 -0
  177. dataface/core/compile/variables.py +53 -0
  178. dataface/core/compile/vega_config.py +98 -0
  179. dataface/core/compile/vega_lite/__init__.py +6 -0
  180. dataface/core/compile/vega_lite/validation.py +95 -0
  181. dataface/core/compile/yaml_error_formatter.py +838 -0
  182. dataface/core/connections.py +38 -0
  183. dataface/core/dashboard.py +358 -0
  184. dataface/core/defaults/default_config.yml +101 -0
  185. dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
  186. dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
  187. dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
  188. dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
  189. dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
  190. dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
  191. dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
  192. dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
  193. dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
  194. dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
  195. dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
  196. dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
  197. dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
  198. dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
  199. dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
  200. dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
  201. dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
  202. dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
  203. dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
  204. dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
  205. dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
  206. dataface/core/defaults/palettes/sequential/amber.yml +22 -0
  207. dataface/core/defaults/palettes/sequential/blue.yml +22 -0
  208. dataface/core/defaults/palettes/sequential/brown.yml +22 -0
  209. dataface/core/defaults/palettes/sequential/gray.yml +22 -0
  210. dataface/core/defaults/palettes/sequential/green.yml +22 -0
  211. dataface/core/defaults/palettes/sequential/purple.yml +22 -0
  212. dataface/core/defaults/palettes/sequential/rust.yml +22 -0
  213. dataface/core/defaults/palettes/sequential/teal.yml +22 -0
  214. dataface/core/defaults/palettes/tone/negative.yml +32 -0
  215. dataface/core/defaults/palettes/tone/positive.yml +22 -0
  216. dataface/core/defaults/palettes/tone/warning.yml +22 -0
  217. dataface/core/defaults/themes/_base.yaml +786 -0
  218. dataface/core/defaults/themes/bi.yaml +16 -0
  219. dataface/core/defaults/themes/carbong100.yaml +41 -0
  220. dataface/core/defaults/themes/cream.yaml +122 -0
  221. dataface/core/defaults/themes/dark.yaml +40 -0
  222. dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
  223. dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
  224. dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
  225. dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
  226. dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
  227. dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
  228. dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
  229. dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
  230. dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
  231. dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
  232. dataface/core/defaults/themes/editorial.yaml +147 -0
  233. dataface/core/defaults/themes/light.yaml +30 -0
  234. dataface/core/defaults/themes/looker.yaml +17 -0
  235. dataface/core/defaults/themes/stark.yaml +134 -0
  236. dataface/core/errors/__init__.py +67 -0
  237. dataface/core/errors/codes_compile.py +56 -0
  238. dataface/core/errors/codes_execute.py +177 -0
  239. dataface/core/errors/codes_render.py +106 -0
  240. dataface/core/errors/codes_unknown.py +15 -0
  241. dataface/core/errors/hints.py +74 -0
  242. dataface/core/errors/registry.py +42 -0
  243. dataface/core/errors/structured.py +92 -0
  244. dataface/core/execute/__init__.py +91 -0
  245. dataface/core/execute/adapters/__init__.py +49 -0
  246. dataface/core/execute/adapters/adapter_registry.py +400 -0
  247. dataface/core/execute/adapters/base.py +245 -0
  248. dataface/core/execute/adapters/csv_adapter.py +239 -0
  249. dataface/core/execute/adapters/dbt_adapter.py +283 -0
  250. dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
  251. dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
  252. dataface/core/execute/adapters/dbt_utils.py +150 -0
  253. dataface/core/execute/adapters/http_adapter.py +224 -0
  254. dataface/core/execute/adapters/metricflow_adapter.py +94 -0
  255. dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
  256. dataface/core/execute/adapters/sql_adapter.py +710 -0
  257. dataface/core/execute/adapters/values_adapter.py +58 -0
  258. dataface/core/execute/batch.py +744 -0
  259. dataface/core/execute/cache_backend.py +135 -0
  260. dataface/core/execute/cache_keys.py +66 -0
  261. dataface/core/execute/dbt_jinja.py +21 -0
  262. dataface/core/execute/dialects/__init__.py +121 -0
  263. dataface/core/execute/dialects/athena.py +75 -0
  264. dataface/core/execute/dialects/base.py +302 -0
  265. dataface/core/execute/dialects/bigquery.py +38 -0
  266. dataface/core/execute/dialects/databricks.py +68 -0
  267. dataface/core/execute/dialects/duckdb.py +35 -0
  268. dataface/core/execute/dialects/mysql.py +68 -0
  269. dataface/core/execute/dialects/postgres.py +39 -0
  270. dataface/core/execute/dialects/redshift.py +12 -0
  271. dataface/core/execute/dialects/snowflake.py +51 -0
  272. dataface/core/execute/dialects/sqlserver.py +92 -0
  273. dataface/core/execute/duckdb_cache.py +712 -0
  274. dataface/core/execute/duckdb_config.py +26 -0
  275. dataface/core/execute/errors.py +213 -0
  276. dataface/core/execute/executor.py +1249 -0
  277. dataface/core/execute/parallel.py +162 -0
  278. dataface/core/execute/setup_sql.py +58 -0
  279. dataface/core/execute/source_registry.py +72 -0
  280. dataface/core/execute/source_resolver.py +255 -0
  281. dataface/core/execute/sql_guard.py +387 -0
  282. dataface/core/execute/sql_literals.py +199 -0
  283. dataface/core/fonts.py +52 -0
  284. dataface/core/inspect/__init__.py +32 -0
  285. dataface/core/inspect/cache_factory.py +98 -0
  286. dataface/core/inspect/db_types.py +162 -0
  287. dataface/core/inspect/dbt_schema.py +96 -0
  288. dataface/core/inspect/defaults.yml +37 -0
  289. dataface/core/inspect/fanout_risk.py +109 -0
  290. dataface/core/inspect/manifest_utils.py +77 -0
  291. dataface/core/inspect/partials/categorical.yml +40 -0
  292. dataface/core/inspect/partials/date.yml +40 -0
  293. dataface/core/inspect/partials/numeric.yml +55 -0
  294. dataface/core/inspect/partition_types.py +38 -0
  295. dataface/core/inspect/query_validator.py +975 -0
  296. dataface/core/inspect/renderer.py +354 -0
  297. dataface/core/inspect/resolver.py +808 -0
  298. dataface/core/inspect/search.py +461 -0
  299. dataface/core/inspect/sources/__init__.py +32 -0
  300. dataface/core/inspect/sources/dbt.py +738 -0
  301. dataface/core/inspect/sources/duckdb_utils.py +66 -0
  302. dataface/core/inspect/templates/__init__.py +1 -0
  303. dataface/core/inspect/templates/categorical_column.yml +196 -0
  304. dataface/core/inspect/templates/charts.yml +109 -0
  305. dataface/core/inspect/templates/date_column.yml +248 -0
  306. dataface/core/inspect/templates/model.yml +138 -0
  307. dataface/core/inspect/templates/numeric_column.yml +261 -0
  308. dataface/core/inspect/templates/quality.yml +80 -0
  309. dataface/core/inspect/templates/string_column.yml +263 -0
  310. dataface/core/project_roots.py +165 -0
  311. dataface/core/render/__init__.py +87 -0
  312. dataface/core/render/board_links.py +176 -0
  313. dataface/core/render/chart/__init__.py +27 -0
  314. dataface/core/render/chart/arc_attached_table.py +251 -0
  315. dataface/core/render/chart/artifacts.py +16 -0
  316. dataface/core/render/chart/callout.py +225 -0
  317. dataface/core/render/chart/decisions.py +358 -0
  318. dataface/core/render/chart/geo.py +700 -0
  319. dataface/core/render/chart/kpi.py +916 -0
  320. dataface/core/render/chart/labels.py +76 -0
  321. dataface/core/render/chart/pipeline.py +818 -0
  322. dataface/core/render/chart/presentation.py +36 -0
  323. dataface/core/render/chart/profile.py +3438 -0
  324. dataface/core/render/chart/render_single.py +347 -0
  325. dataface/core/render/chart/renderers.py +193 -0
  326. dataface/core/render/chart/rendering.py +565 -0
  327. dataface/core/render/chart/serialization.py +90 -0
  328. dataface/core/render/chart/spark.py +496 -0
  329. dataface/core/render/chart/spark_bar.py +370 -0
  330. dataface/core/render/chart/spec_builders.py +154 -0
  331. dataface/core/render/chart/standard_renderer.py +2645 -0
  332. dataface/core/render/chart/table.py +2957 -0
  333. dataface/core/render/chart/table_support.py +1452 -0
  334. dataface/core/render/chart/tick_values.py +66 -0
  335. dataface/core/render/chart/time_unit_detect.py +809 -0
  336. dataface/core/render/chart/title_overflow.py +157 -0
  337. dataface/core/render/chart/type_inference.py +122 -0
  338. dataface/core/render/chart/validation.py +99 -0
  339. dataface/core/render/chart/vega_lite.py +125 -0
  340. dataface/core/render/chart/vega_lite_types.py +268 -0
  341. dataface/core/render/chart/vl_field_maps.py +346 -0
  342. dataface/core/render/chart_interactivity.py +24 -0
  343. dataface/core/render/control_registry.py +287 -0
  344. dataface/core/render/converters/__init__.py +24 -0
  345. dataface/core/render/converters/chart.py +276 -0
  346. dataface/core/render/converters/html.py +98 -0
  347. dataface/core/render/converters/pdf.py +40 -0
  348. dataface/core/render/converters/png.py +41 -0
  349. dataface/core/render/errors.py +144 -0
  350. dataface/core/render/face_api.py +160 -0
  351. dataface/core/render/faces.py +1194 -0
  352. dataface/core/render/font_measurement.py +48 -0
  353. dataface/core/render/font_support.py +197 -0
  354. dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
  355. dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
  356. dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
  357. dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
  358. dataface/core/render/fonts/InterVariable.ttf +0 -0
  359. dataface/core/render/fonts/InterVariable.woff2 +0 -0
  360. dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
  361. dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
  362. dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
  363. dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
  364. dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
  365. dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
  366. dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
  367. dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
  368. dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
  369. dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
  370. dataface/core/render/fonts/_emoji_font_face.css +43 -0
  371. dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
  372. dataface/core/render/format_utils.py +329 -0
  373. dataface/core/render/geo_defaults.yml +28 -0
  374. dataface/core/render/json_format.py +146 -0
  375. dataface/core/render/layout_sizing.py +865 -0
  376. dataface/core/render/layouts.py +541 -0
  377. dataface/core/render/markdown_defaults.yml +16 -0
  378. dataface/core/render/missing_vars_prompt.py +79 -0
  379. dataface/core/render/placeholder.py +389 -0
  380. dataface/core/render/render_result.py +14 -0
  381. dataface/core/render/renderer.py +467 -0
  382. dataface/core/render/script_embedding.py +16 -0
  383. dataface/core/render/svg_utils.py +212 -0
  384. dataface/core/render/template_loader.py +69 -0
  385. dataface/core/render/templates/controls/_styles.css +606 -0
  386. dataface/core/render/templates/controls/checkbox.html +16 -0
  387. dataface/core/render/templates/controls/date.html +16 -0
  388. dataface/core/render/templates/controls/number.html +19 -0
  389. dataface/core/render/templates/controls/readonly.html +9 -0
  390. dataface/core/render/templates/controls/select.html +21 -0
  391. dataface/core/render/templates/controls/slider.html +22 -0
  392. dataface/core/render/templates/controls/text.html +16 -0
  393. dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
  394. dataface/core/render/templates/scripts/variables.js +976 -0
  395. dataface/core/render/templates/svg/grid_pattern.svg +3 -0
  396. dataface/core/render/templates/svg/styles.css +51 -0
  397. dataface/core/render/terminal.py +311 -0
  398. dataface/core/render/terminal_charts.py +563 -0
  399. dataface/core/render/terminal_defaults.yml +2 -0
  400. dataface/core/render/terminal_layouts.py +299 -0
  401. dataface/core/render/terminal_text.py +31 -0
  402. dataface/core/render/text/__init__.py +1 -0
  403. dataface/core/render/text/case.py +113 -0
  404. dataface/core/render/text_format.py +129 -0
  405. dataface/core/render/utils.py +106 -0
  406. dataface/core/render/variable_controls.py +946 -0
  407. dataface/core/render/variable_input_refinement.py +140 -0
  408. dataface/core/render/warnings/__init__.py +15 -0
  409. dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
  410. dataface/core/render/warnings/base.py +44 -0
  411. dataface/core/render/warnings/fanout_risk.py +15 -0
  412. dataface/core/render/warnings/from_query_diagnostic.py +56 -0
  413. dataface/core/render/warnings/missing_join_predicate.py +13 -0
  414. dataface/core/render/warnings/query_parse_error.py +14 -0
  415. dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
  416. dataface/core/render/warnings/reaggregation.py +14 -0
  417. dataface/core/render/warnings/registry.py +45 -0
  418. dataface/core/render/warnings/suppression.py +46 -0
  419. dataface/core/render/warnings/temporal_single_point.py +63 -0
  420. dataface/core/render/warnings/unreferenced_chart.py +15 -0
  421. dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
  422. dataface/core/render/yaml_format.py +167 -0
  423. dataface/core/resolve_face.py +195 -0
  424. dataface/core/schema/__init__.py +0 -0
  425. dataface/core/schema/guidance.py +151 -0
  426. dataface/core/scoped_paths.py +59 -0
  427. dataface/core/serve/__init__.py +14 -0
  428. dataface/core/serve/bootstrap.py +39 -0
  429. dataface/core/serve/embedded.py +57 -0
  430. dataface/core/serve/port.py +129 -0
  431. dataface/core/serve/server.py +938 -0
  432. dataface/core/serve/templates/__init__.py +0 -0
  433. dataface/core/serve/templates/directory.yml +6 -0
  434. dataface/core/serve/templates/error.html.j2 +217 -0
  435. dataface/core/utils.py +121 -0
  436. dataface/core/validate.py +64 -0
  437. dataface/integrations/__init__.py +0 -0
  438. dataface/integrations/highlighting.py +351 -0
  439. dataface/integrations/markdown.py +537 -0
  440. dataface/py.typed +0 -0
  441. dataface-0.1.2.dist-info/METADATA +375 -0
  442. dataface-0.1.2.dist-info/RECORD +455 -0
  443. dataface-0.1.2.dist-info/WHEEL +4 -0
  444. dataface-0.1.2.dist-info/entry_points.txt +2 -0
  445. dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
  446. mdsvg/__init__.py +168 -0
  447. mdsvg/fonts.py +656 -0
  448. mdsvg/images.py +299 -0
  449. mdsvg/parser.py +629 -0
  450. mdsvg/playground.py +284 -0
  451. mdsvg/py.typed +2 -0
  452. mdsvg/renderer.py +1623 -0
  453. mdsvg/style.py +355 -0
  454. mdsvg/types.py +200 -0
  455. mdsvg/utils.py +86 -0
dataface/ai/llm.py ADDED
@@ -0,0 +1,403 @@
1
+ """Thin LLM client adapters for the terminal agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from collections.abc import Iterator
8
+ from typing import Any, Protocol
9
+
10
+ import httpx
11
+
12
+ from dataface._install_hint import install_hint
13
+ from dataface.ai.events import ContentDelta, StreamEvent, ThinkingStatus, ToolCallEvent
14
+ from dataface.ai.tool_schemas import ALL_TOOLS
15
+
16
+ DEFAULT_OPENAI_MODEL = "gpt-4.1-mini"
17
+ DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-5"
18
+
19
+
20
+ def infer_provider(model: str | None) -> str:
21
+ if model and model.startswith("anthropic:"):
22
+ return "anthropic"
23
+ if model and model.startswith("openai:"):
24
+ return "openai"
25
+ if model and model.lower().startswith("claude"):
26
+ return "anthropic"
27
+ if model and model.lower().startswith(("gpt", "o1", "o3", "o4")):
28
+ return "openai"
29
+ if not model and not os.getenv("OPENAI_API_KEY") and os.getenv("ANTHROPIC_API_KEY"):
30
+ return "anthropic"
31
+ return "openai"
32
+
33
+
34
+ def _strip_provider_prefix(model: str | None) -> str | None:
35
+ if model and ":" in model:
36
+ provider, _, raw_model = model.partition(":")
37
+ if provider in {"openai", "anthropic"} and raw_model:
38
+ return raw_model
39
+ return model
40
+
41
+
42
+ def _resolve_model(
43
+ explicit_model: str | None,
44
+ env_var: str,
45
+ default_model: str,
46
+ ) -> str:
47
+ return _strip_provider_prefix(explicit_model) or os.getenv(env_var) or default_model
48
+
49
+
50
+ def _normalize_openai_tools(
51
+ tools: list[dict[str, Any]] | None,
52
+ ) -> list[dict[str, Any]]:
53
+ source_tools = tools or ALL_TOOLS
54
+ if not source_tools:
55
+ return []
56
+ if "input_schema" in source_tools[0]:
57
+ return [
58
+ {
59
+ "type": "function",
60
+ "name": tool["name"],
61
+ "description": tool["description"],
62
+ "parameters": tool["input_schema"],
63
+ }
64
+ for tool in source_tools
65
+ ]
66
+ return source_tools
67
+
68
+
69
+ def _normalize_anthropic_tools(
70
+ tools: list[dict[str, Any]] | None,
71
+ ) -> list[dict[str, Any]]:
72
+ source_tools = tools or ALL_TOOLS
73
+ if not source_tools:
74
+ return []
75
+ if "input_schema" in source_tools[0]:
76
+ return [
77
+ {
78
+ "name": tool["name"],
79
+ "description": tool["description"],
80
+ "input_schema": tool["input_schema"],
81
+ }
82
+ for tool in source_tools
83
+ ]
84
+ return source_tools
85
+
86
+
87
+ class LLMClientError(RuntimeError):
88
+ """Raised when a provider request fails."""
89
+
90
+
91
+ def _is_openai_api_error(exc: Exception) -> bool:
92
+ try:
93
+ from openai import APIError
94
+ except ImportError:
95
+ return isinstance(exc, httpx.HTTPError)
96
+ return isinstance(exc, (APIError, httpx.HTTPError))
97
+
98
+
99
+ def _is_anthropic_api_error(exc: Exception) -> bool:
100
+ try:
101
+ from anthropic import APIError
102
+ except ImportError:
103
+ return isinstance(exc, httpx.HTTPError)
104
+ return isinstance(exc, (APIError, httpx.HTTPError))
105
+
106
+
107
+ # -- Provider stream adapter ------------------------------------------------
108
+
109
+
110
+ def _iter_anthropic_stream(stream_ctx: Any) -> Iterator[StreamEvent]:
111
+ """Parse Anthropic Messages API stream context manager into typed events."""
112
+ with stream_ctx as stream:
113
+ for event in stream:
114
+ if event.type == "content_block_delta" and event.delta.type == "text_delta":
115
+ yield ContentDelta(delta=event.delta.text)
116
+
117
+ final_message = stream.get_final_message()
118
+
119
+ for block in final_message.content:
120
+ if block.type == "tool_use":
121
+ yield ToolCallEvent(
122
+ id=block.id, name=block.name, arguments=dict(block.input)
123
+ )
124
+
125
+
126
+ # -- Client protocol and implementations ------------------------------------
127
+
128
+
129
+ class LLMClient(Protocol):
130
+ """Provider-neutral streaming interface."""
131
+
132
+ provider: str
133
+ model: str
134
+
135
+ def stream_with_tools(
136
+ self,
137
+ *,
138
+ messages: list[dict[str, Any]],
139
+ system_prompt: str,
140
+ tools: list[dict[str, Any]] | None = None,
141
+ ) -> Iterator[StreamEvent]: ...
142
+
143
+
144
+ class OpenAIClient:
145
+ """Responses API client with streaming output and tool calls."""
146
+
147
+ provider = "openai"
148
+
149
+ def __init__(self, model: str | None = None, api_key: str | None = None) -> None:
150
+ self.model = _resolve_model(
151
+ model,
152
+ "OPENAI_MODEL",
153
+ DEFAULT_OPENAI_MODEL,
154
+ )
155
+ self.api_key = api_key or os.getenv("OPENAI_API_KEY")
156
+ self._client: Any = None
157
+ self._previous_response_id: str | None = None
158
+ self._sent_message_count = 0
159
+
160
+ @property
161
+ def client(self) -> Any:
162
+ if self._client is None:
163
+ try:
164
+ from openai import OpenAI
165
+ except ImportError as exc:
166
+ raise RuntimeError(
167
+ f"OpenAI client is not installed. Install with: {install_hint('chat')}"
168
+ ) from exc
169
+ self._client = OpenAI(api_key=self.api_key)
170
+ return self._client
171
+
172
+ def create(self, **kwargs: Any) -> Any:
173
+ """Non-streaming Responses API call with unified error handling.
174
+
175
+ Wraps ``client.responses.create()`` with the same exception
176
+ handling used by the streaming path, translating API and network
177
+ errors into :class:`LLMClientError`.
178
+ """
179
+ try:
180
+ return self.client.responses.create(**kwargs)
181
+ except Exception as exc:
182
+ if _is_openai_api_error(exc):
183
+ raise LLMClientError(str(exc)) from exc
184
+ raise
185
+
186
+ def _messages_to_input(
187
+ self, messages: list[dict[str, Any]], incremental: bool
188
+ ) -> list[dict[str, Any]]:
189
+ source_messages = (
190
+ messages[self._sent_message_count :] if incremental else messages
191
+ )
192
+ items: list[dict[str, Any]] = []
193
+ for message in source_messages:
194
+ role = message["role"]
195
+ if not incremental and (
196
+ role == "tool" or (role == "assistant" and message.get("tool_calls"))
197
+ ):
198
+ raise LLMClientError(
199
+ "OpenAI client requires an unbroken previous_response_id chain for tool-use history."
200
+ )
201
+ if role in {"user", "assistant"}:
202
+ content = message.get("content")
203
+ if content:
204
+ items.append({"role": role, "content": content})
205
+ elif role == "tool":
206
+ items.append(
207
+ {
208
+ "type": "function_call_output",
209
+ "call_id": message["tool_call_id"],
210
+ "output": message["content"],
211
+ }
212
+ )
213
+ return items
214
+
215
+ def stream_with_tools(
216
+ self,
217
+ *,
218
+ messages: list[dict[str, Any]],
219
+ system_prompt: str,
220
+ tools: list[dict[str, Any]] | None = None,
221
+ ) -> Iterator[StreamEvent]:
222
+ incremental = self._previous_response_id is not None
223
+ input_items = self._messages_to_input(messages, incremental=incremental)
224
+ if not input_items:
225
+ return
226
+
227
+ previous_response_id = self._previous_response_id
228
+ pending_response_id = previous_response_id
229
+
230
+ kwargs: dict[str, Any] = {
231
+ "model": self.model,
232
+ "instructions": system_prompt,
233
+ "input": input_items,
234
+ "tools": _normalize_openai_tools(tools),
235
+ "stream": True,
236
+ }
237
+ if self._previous_response_id:
238
+ kwargs["previous_response_id"] = self._previous_response_id
239
+
240
+ try:
241
+ stream = self.client.responses.create(**kwargs)
242
+ except Exception as exc:
243
+ if _is_openai_api_error(exc):
244
+ raise LLMClientError(str(exc)) from exc
245
+ raise
246
+ try:
247
+ # Keep OpenAI parsing inline here because previous_response_id tracking
248
+ # and rollback on stream failure are part of the same control flow.
249
+ for event in stream:
250
+ event_type = getattr(event, "type", "")
251
+
252
+ if event_type == "response.created":
253
+ pending_response_id = event.response.id
254
+ continue
255
+
256
+ if event_type == "response.output_text.delta":
257
+ yield ContentDelta(delta=event.delta)
258
+ continue
259
+
260
+ if event_type == "response.reasoning_summary_text.delta":
261
+ yield ThinkingStatus(status=event.delta)
262
+ continue
263
+
264
+ if event_type != "response.output_item.done":
265
+ continue
266
+
267
+ item = event.item
268
+ if item.type != "function_call":
269
+ continue
270
+
271
+ arguments = json.loads(item.arguments) if item.arguments else {}
272
+ yield ToolCallEvent(
273
+ id=item.call_id, name=item.name, arguments=arguments
274
+ )
275
+ except Exception as exc:
276
+ self._previous_response_id = previous_response_id
277
+ if _is_openai_api_error(exc):
278
+ raise LLMClientError(str(exc)) from exc
279
+ raise
280
+
281
+ self._previous_response_id = pending_response_id
282
+ self._sent_message_count = len(messages)
283
+
284
+
285
+ class AnthropicClient:
286
+ """Messages API client with tool-use support."""
287
+
288
+ provider = "anthropic"
289
+
290
+ def __init__(self, model: str | None = None, api_key: str | None = None) -> None:
291
+ self.model = _resolve_model(
292
+ model,
293
+ "ANTHROPIC_MODEL",
294
+ DEFAULT_ANTHROPIC_MODEL,
295
+ )
296
+ self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
297
+ self._client: Any = None
298
+
299
+ @property
300
+ def client(self) -> Any:
301
+ if self._client is None:
302
+ try:
303
+ from anthropic import Anthropic
304
+ except ImportError as exc:
305
+ raise RuntimeError(
306
+ f"Anthropic client is not installed. Install with: {install_hint('chat')}"
307
+ ) from exc
308
+ self._client = Anthropic(api_key=self.api_key)
309
+ return self._client
310
+
311
+ def _messages_to_input(
312
+ self, messages: list[dict[str, Any]]
313
+ ) -> list[dict[str, Any]]:
314
+ converted: list[dict[str, Any]] = []
315
+ pending_tool_results: list[dict[str, Any]] = []
316
+
317
+ def flush_tool_results() -> None:
318
+ if pending_tool_results:
319
+ converted.append(
320
+ {"role": "user", "content": list(pending_tool_results)}
321
+ )
322
+ pending_tool_results.clear()
323
+
324
+ for message in messages:
325
+ role = message["role"]
326
+ if role == "tool":
327
+ pending_tool_results.append(
328
+ {
329
+ "type": "tool_result",
330
+ "tool_use_id": message["tool_call_id"],
331
+ "content": message["content"],
332
+ }
333
+ )
334
+ continue
335
+
336
+ flush_tool_results()
337
+
338
+ if role == "assistant" and message.get("tool_calls"):
339
+ blocks: list[dict[str, Any]] = []
340
+ if message.get("content"):
341
+ blocks.append({"type": "text", "text": message["content"]})
342
+ for tool_call in message["tool_calls"]:
343
+ blocks.append(
344
+ {
345
+ "type": "tool_use",
346
+ "id": tool_call["id"],
347
+ "name": tool_call["name"],
348
+ "input": tool_call["arguments"],
349
+ }
350
+ )
351
+ converted.append({"role": "assistant", "content": blocks})
352
+ continue
353
+
354
+ content = message.get("content")
355
+ if content:
356
+ converted.append(
357
+ {"role": role, "content": [{"type": "text", "text": content}]}
358
+ )
359
+
360
+ flush_tool_results()
361
+ return converted
362
+
363
+ def stream_with_tools(
364
+ self,
365
+ *,
366
+ messages: list[dict[str, Any]],
367
+ system_prompt: str,
368
+ tools: list[dict[str, Any]] | None = None,
369
+ ) -> Iterator[StreamEvent]:
370
+ try:
371
+ yield from _iter_anthropic_stream(
372
+ self.client.messages.stream(
373
+ model=self.model,
374
+ system=system_prompt,
375
+ messages=self._messages_to_input(messages),
376
+ tools=_normalize_anthropic_tools(tools),
377
+ max_tokens=4096,
378
+ )
379
+ )
380
+ except Exception as exc:
381
+ if _is_anthropic_api_error(exc):
382
+ raise LLMClientError(str(exc)) from exc
383
+ raise
384
+
385
+
386
+ def create_client(provider: str | None = None, model: str | None = None) -> LLMClient:
387
+ """Create an LLM client from explicit provider or model naming."""
388
+ if (
389
+ not provider
390
+ and not model
391
+ and not os.getenv("OPENAI_API_KEY")
392
+ and not os.getenv("ANTHROPIC_API_KEY")
393
+ ):
394
+ raise RuntimeError(
395
+ "No LLM API key found. Set OPENAI_API_KEY or ANTHROPIC_API_KEY "
396
+ "in the environment, or pass --model to select a provider explicitly."
397
+ )
398
+ resolved_provider = provider or infer_provider(model)
399
+ if resolved_provider == "anthropic":
400
+ return AnthropicClient(model=model)
401
+ if resolved_provider == "openai":
402
+ return OpenAIClient(model=model)
403
+ raise ValueError(f"Unsupported provider: {resolved_provider}")
@@ -0,0 +1,51 @@
1
+ """Dataface MCP (Model Context Protocol) Server.
2
+
3
+ This module provides an MCP server that enables AI assistants (Claude, Cursor,
4
+ ChatGPT, etc.) to interact with Dataface dashboards through a standardized
5
+ protocol.
6
+
7
+ Architecture:
8
+ Resources (read-only context):
9
+ - dataface://dashboards - List of dashboards in project
10
+ - dataface://dashboard/{path} - Dashboard content and structure
11
+ - dataface://docs/all - Whole DATAFACE_SYNTAX.md (full YAML reference)
12
+ - dataface://docs/{topic} - One H2 section of the YAML reference
13
+ - dataface://guide/dashboard-design - Dashboard design principles
14
+ - dataface://guide/report-design - Report design principles
15
+ - dataface://guide/dashboard-build - Build-test-iterate workflow
16
+ - dataface://guide/dashboard-review - Dashboard review (structural + visual)
17
+
18
+ Tools (actions):
19
+ - render_dashboard - Validate + render; pass as_link=true for URL-only (folds view_dashboard)
20
+ - execute_query - Run SQL against data sources
21
+ - query_face - Run one named query from a face YAML and return columns + rows
22
+ - schema - Drill the data hierarchy: source → schema → table → column
23
+ - search_dashboards - Search dashboards by keyword/structure
24
+ - docs - Browse Dataface YAML reference docs offline
25
+
26
+ Usage:
27
+ # Configure MCP for your AI client
28
+ dft init mcp cursor # or: vscode, claude, claude-code, codex, copilot, print
29
+ dft init mcp --all
30
+
31
+ # Start the MCP server (stdio mode)
32
+ dft mcp serve
33
+
34
+ # Or programmatically
35
+ from dataface.ai.mcp import create_server
36
+ server = create_server()
37
+ await server.run()
38
+
39
+ For more information about MCP, see:
40
+ https://modelcontextprotocol.io/
41
+ """
42
+
43
+ from dataface.ai.context import DatafaceAIContext
44
+ from dataface.ai.mcp.server import create_server, run_server
45
+
46
+ __all__ = [
47
+ # Server
48
+ "create_server",
49
+ "run_server",
50
+ "DatafaceAIContext",
51
+ ]