Skip to content

Conversation

@cpsievert
Copy link
Contributor

@cpsievert cpsievert commented Jan 21, 2026

Adds support for Snowflake Semantic Views by helping the LLM generate correct queries via the SEMANTIC_VIEW() table function with certified business metrics.

Features

  • Automatic detection: When a Snowflake connection is provided, querychat discovers available Semantic Views via SHOW SEMANTIC VIEWS
  • DDL introspection: Retrieves full DDL definitions via GET_DDL('SEMANTIC_VIEW', ...) to understand available metrics, dimensions, and filters
  • Prompt enhancement: Includes semantic view definitions and SEMANTIC_VIEW() syntax reference in the system prompt
  • Graceful handling: If no semantic views exist, continues with normal table/view querying

Why Semantic Views matter

Raw table queries can return incorrect results when business logic isn't properly applied. For example, querying raw tables for "external customer revenue" returned $184B while the semantic model's certified metric returned $84.5B (the correct answer). The raw query was 2x+ too high because it ignored discounts and included invalid transaction codes.

Implementation

Python

  • SQLAlchemySource and IbisSource gain get_semantic_views_description() method
  • New _snowflake.py module with discover_semantic_views() and format_semantic_views()
  • Prompt templates in prompts/semantic-views/ directory

R

  • DBISource gains get_semantic_views_description() method
  • Snowflake detection via is_snowflake_connection()
  • Prompt templates in inst/prompts/semantic-views/ directory

References

Based on the Snowflake skill from https://github.com/posit-dev/databot/pull/278

cpsievert and others added 5 commits January 26, 2026 11:43
Add automatic detection and context injection for Snowflake Semantic Views
when a Snowflake connection is provided. This helps LLMs generate correct
queries using the SEMANTIC_VIEW() function with certified business metrics.

Changes:
- Add SnowflakeSource class (Python and R) that extends SQLAlchemySource/DBISource
- Discover semantic views via SHOW SEMANTIC VIEWS at initialization
- Retrieve DDL definitions via GET_DDL('SEMANTIC_VIEW', ...)
- Include semantic view context in schema output
- Add SEMANTIC_VIEW() syntax reference to system prompt
- Add Snowflake-specific SQL tips (QUALIFY, LATERAL FLATTEN, time travel)
- Graceful fallback when no semantic views exist or discovery fails

Based on the Snowflake skill from posit-dev/databot#278.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix SQL injection risk by escaping single quotes in view names
- Add discover_semantic_views parameter for lazy initialization
- Remove error swallowing, let errors propagate for debugging
- Add debug logging when no semantic views are found
- Move logger placement to top of file (Python)
- Add defensive dialect check in normalize_data_source (Python)
- Add comprehensive unit tests for both Python and R

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces a Protocol-based adapter pattern for Snowflake semantic view
discovery that works with both SQLAlchemy and Ibis backends:

- Add _snowflake.py with RawSQLExecutor Protocol, executor implementations
  (SQLAlchemyExecutor, IbisExecutor), and standalone discovery functions
- Add _snowflake_sources.py with SnowflakeSource and new SnowflakeIbisSource
- Update normalize_data_source() to route Ibis Snowflake backends
- Maintain backwards-compatible imports from _datasource.py

This enables semantic view support for Ibis connections to Snowflake,
not just SQLAlchemy connections.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of separate SnowflakeSource and SnowflakeIbisSource classes,
SQLAlchemySource and IbisSource now auto-detect Snowflake backends
and discover semantic views during initialization.

Changes:
- SQLAlchemySource/IbisSource check dialect/backend name for "snowflake"
- Discovery can be disabled via QUERYCHAT_DISABLE_SEMANTIC_VIEWS env var
- Removed _snowflake_sources.py (no longer needed)
- Simplified normalize_data_source() - no Snowflake-specific routing
- Updated tests to verify new architecture

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract inline syntax documentation to `prompts/semantic-view-syntax.md`
in both R and Python packages, making it language-agnostic and easier
to maintain.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace Protocol/class-based OOP with functional approach using
  backend_type: Literal["sqlalchemy", "ibis"] discriminator
- Move env var check (QUERYCHAT_DISABLE_SEMANTIC_VIEWS) into
  discover_semantic_views() for early exit
- Move semantic view discovery from __init__ to get_schema() for
  lazy initialization
- Remove SemanticViewMixin in favor of direct function calls
- Update tests to verify new lazy discovery behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Snowflake SQL Tips section isn't necessary for semantic views support.
Removed the {{#is_snowflake}} block from prompt templates and the
is_snowflake variable from system prompt code in both R and Python.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace backend_type: Literal["sqlalchemy", "ibis"] parameter with
isinstance(backend, sqlalchemy.Engine) checks for cleaner API.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The `backend.sql()` method in Ibis parses queries with sqlglot, which
doesn't support Snowflake commands like `SHOW SEMANTIC VIEWS`. Switch
to using `backend.raw_sql()` which executes queries without parsing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove comment about raw_sql() per PR feedback
- Add type ignore for raw_sql() to fix pyright error (method exists
  on concrete backends but not typed on SQLBackend base class)
- Remove unused PROMPTS_DIR constant per PR feedback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename get_semantic_views_section() to get_semantic_views_description()
  on DataSource classes (clearer intent)
- Rename get_semantic_views_section() to format_semantic_views() in
  _snowflake.py / DBISource.R (matches other format_* functions)
- Update tests to use new method names

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cpsievert and others added 2 commits January 27, 2026 19:19
Minimize diff by reverting auto-formatter changes that were not
part of the semantic views feature.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update get_semantic_views_description() docstrings to clarify purpose
- Revert list comprehension formatting to match original style

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Minimize diff by reverting air format changes that were not
part of the semantic views feature.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

This comment was marked as resolved.

@cpsievert cpsievert marked this pull request as ready for review January 27, 2026 21:36
@cpsievert cpsievert merged commit 574c6de into main Jan 27, 2026
24 checks passed
@cpsievert cpsievert deleted the feat/snowflake-semantic-views branch January 27, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants