-
Notifications
You must be signed in to change notification settings - Fork 13
Add BigQuery support to evaluation config generation #194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| ## BigQuery | ||
|
|
||
| **Required properties from the `kind: source` block in `tools.yaml`:** | ||
| - Source Type (`type: bigquery`) | ||
| - Google Cloud Project ID (`project`) | ||
| - Dataset ID (`dataset`) | ||
|
|
||
| **EvalBench Database Config Spec (`db_config.yaml`):** | ||
|
|
||
| ```yaml | ||
| db_type: bigquery | ||
| dialect: googlesql | ||
| database_name: <dataset_id> | ||
| database_path: projects/<project_id>/datasets/<dataset_id> | ||
| gcp_project_id: <project_id> | ||
| max_executions_per_minute: 100 | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| ## BigQuery | ||
|
|
||
| **Required Information:** | ||
| - Data Source Name (e.g., `my-bigquery-db`) | ||
| - Google Cloud Project ID | ||
| - Dataset ID | ||
|
|
||
| **Template:** | ||
|
|
||
| ```yaml | ||
| kind: source | ||
| name: <data_source_name> | ||
| type: bigquery | ||
| project: <project_id> | ||
| dataset: <dataset_id> | ||
| --- | ||
| kind: tool | ||
| name: <data_source_name>-list-schemas | ||
| type: bigquery-sql | ||
| source: <data_source_name> | ||
| description: | | ||
| Use this tool to list tables and their schemas in the <data_source_name> dataset. | ||
|
|
||
| Progressive Schema Discovery (Recommended): | ||
| 1) Fetch structure first, | ||
| 2) Go deep on specific parts if interested, | ||
| 3) Use batching if info is too large. | ||
| statement: | | ||
| SELECT table_name, column_name, data_type, description FROM `<project_id>`.`<dataset_id>`.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS ORDER BY table_name, column_name | ||
| --- | ||
| kind: tool | ||
| name: <data_source_name>-execute-sql | ||
| type: bigquery-execute-sql | ||
| source: <data_source_name> | ||
| description: Use this tool to execute SQL statements against the <data_source_name> BigQuery dataset. | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # BigQuery (GoogleSQL) Facet Generation Reference | ||
|
|
||
| This reference provides best practices and ideal output definitions for generating Facets in BigQuery (GoogleSQL). | ||
|
|
||
| ## Concepts | ||
|
|
||
| Facets are reusable, modular SQL fragments (like a `WHERE` clause or specialized join). They are dynamically injected filters linked to specific vocabulary or terminology. | ||
|
|
||
| ## Fully-Qualified Column References | ||
|
|
||
| Every column reference in a facet's SQL snippet **must** be qualified with its table name as `table.column` (e.g., `products.rating`). Facets are injected into larger queries that may join multiple tables, so unqualified columns risk ambiguity errors or silently binding to the wrong column. Never use table aliases — the surrounding query controls aliasing. | ||
|
|
||
| Use `table.column` (NOT `project.dataset.table.column`) for column | ||
| references inside snippets — the surrounding query's `FROM` clause carries | ||
| the fully-qualified `` `project`.`dataset`.`table` `` reference. However, if | ||
| a facet snippet itself contains a `FROM` clause (e.g., an `EXISTS` | ||
| subquery), every table inside it must be fully qualified as | ||
| `` `project`.`dataset`.`table` `` because BigQuery has no default dataset | ||
| at query time. | ||
|
|
||
| ## Parameterization | ||
|
|
||
| Values in the SQL snippet and the intent must be replaced with positional parameters represented by `?`, according to the [Phrase Extraction and Parameterization Guidelines](../phrase_extraction/guidelines.md). | ||
|
|
||
| ### Example | ||
|
|
||
| **Input**: | ||
| * **SQL Snippet**: `products.rating > 4.5` | ||
| * **Intent**: "highly rated products (above 4.5)" | ||
|
|
||
| **Generated Output** (Conceptual): | ||
| ```json | ||
| { | ||
| "sql_snippet": "products.rating > 4.5", | ||
| "intent": "highly rated products (above 4.5)", | ||
| "manifest": "highly rated products (above a given number)", | ||
| "parameterized": { | ||
| "parameterized_sql_snippet": "products.rating > ?", | ||
| "parameterized_intent": "highly rated products (above ?)" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Best Practices | ||
|
|
||
| * Provide clear and reusable SQL snippets. | ||
| * **Always qualify columns as `table.column`** in both the literal and parameterized SQL snippets. Never use bare column names or table aliases. | ||
| * **Fully qualify any table inside subquery `FROM` clauses** as `` `project`.`dataset`.`table` ``. | ||
| * Ensure the SQL snippet follows BigQuery (GoogleSQL) syntax. | ||
| * The intent should clearly describe the condition or filter. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # BigQuery (GoogleSQL) Template Generation Reference | ||
|
|
||
| This reference provides best practices and ideal output definitions for generating Templates in BigQuery (GoogleSQL). | ||
|
|
||
| ## Concepts | ||
|
|
||
| Templates map full natural language questions to full SQL queries. They are used to teach the system overarching operational logic. | ||
|
|
||
| ## Fully-Qualified Table References (CRITICAL) | ||
|
|
||
| Unlike Postgres or Spanner connections, a BigQuery connection is scoped to a | ||
| project only — there is **no default dataset at query time**. Every table | ||
| reference in the SQL **must** be fully qualified as | ||
| `` `<project>`.`<dataset>`.`<table>` `` (e.g., | ||
| `` `my-project`.`sales_data`.`orders` ``). SQL that references a bare table | ||
| name (`FROM orders`) will fail with "Table not found" when executed. | ||
|
|
||
| Take the project and dataset IDs from the `kind: source` block in | ||
| `tools.yaml`. Apply this rule to the `sql` field AND the | ||
| `parameterized_sql` field — never parameterize the project, dataset, or | ||
| table identifiers themselves; only parameterize filter values. | ||
|
|
||
| ## Parameterization | ||
|
|
||
| Values in the SQL query and the intent must be replaced with positional parameters represented by `?`, according to the [Phrase Extraction and Parameterization Guidelines](../phrase_extraction/guidelines.md). | ||
|
|
||
| ### Example | ||
|
|
||
| **Input**: | ||
| * **Question**: "How many accounts are in London?" | ||
| * **SQL**: ``SELECT count(*) FROM `my-project`.`finance`.`account` WHERE city = 'London'`` | ||
| * **Intent**: "How many accounts are in London?" | ||
|
|
||
| **Generated Output** (Conceptual): | ||
| ```json | ||
| { | ||
| "nl_query": "How many accounts are in London?", | ||
| "sql": "SELECT count(*) FROM `my-project`.`finance`.`account` WHERE city = 'London'", | ||
| "intent": "How many accounts are in London?", | ||
| "manifest": "How many accounts are in a given city?", | ||
| "parameterized": { | ||
| "parameterized_sql": "SELECT count(*) FROM `my-project`.`finance`.`account` WHERE city = ?", | ||
| "parameterized_intent": "How many accounts are in ?" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Best Practices | ||
|
|
||
| * Provide complete, executable SQL queries. | ||
| * Ensure the SQL follows BigQuery (GoogleSQL) syntax. | ||
| * **Always fully qualify every table as `` `project`.`dataset`.`table` ``** in both `sql` and `parameterized_sql`. | ||
| * The intent should accurately describe what the query does. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # BigQuery (GoogleSQL) Value Search Templates | ||
|
|
||
| This reference provides the SQL templates and examples for Value Search in BigQuery (GoogleSQL). | ||
|
|
||
| ## Requirements | ||
|
|
||
| * Every table reference **must** be fully qualified as `` `{project}`.`{dataset}`.`{table}` `` — BigQuery has no default dataset at query time. | ||
|
|
||
| ## Supported Match Functions | ||
|
|
||
| ### 1. EXACT_MATCH_STRINGS | ||
|
|
||
| **Description**: Exact match for strings in BigQuery. | ||
| **Example**: Use for exact IDs or state codes. | ||
|
|
||
| **Template**: | ||
| ```sql | ||
| SELECT CAST($value AS STRING) AS value, '{column}' AS `columns`, | ||
| '{concept_type}' AS concept_type, 0 AS distance, | ||
| JSON '{}' AS context | ||
| FROM `{project}`.`{dataset}`.`{table}` AS T | ||
| WHERE CAST(T.`{column}` AS STRING) = CAST($value AS STRING) | ||
| ``` | ||
|
|
||
| ### 2. EDIT_DISTANCE_MATCH | ||
|
|
||
| **Description**: String similarity using BigQuery's built-in `EDIT_DISTANCE` function (Levenshtein distance). No index prerequisites. | ||
| **Example**: Use for typos/misspellings (e.g., "Lndn" → "London"). | ||
|
|
||
| **Template**: | ||
| ```sql | ||
| SELECT CAST(T.`{column}` AS STRING) AS value, '{column}' AS `columns`, | ||
| '{concept_type}' AS concept_type, | ||
| EDIT_DISTANCE(LOWER(CAST(T.`{column}` AS STRING)), LOWER(CAST($value AS STRING))) AS distance, | ||
| JSON '{}' AS context | ||
| FROM `{project}`.`{dataset}`.`{table}` AS T | ||
| WHERE EDIT_DISTANCE(LOWER(CAST(T.`{column}` AS STRING)), LOWER(CAST($value AS STRING))) <= 3 | ||
| ``` | ||
|
|
||
| **Performance Recommendations**: | ||
| * Value-search scans are full-table scans in BigQuery. Prefer running them against low-cardinality dimension tables, or pre-materialize a `SELECT DISTINCT {column}` lookup table to bound bytes scanned. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,80 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import google.cloud.geminidataanalytics_v1beta as gda | ||||||||||||||||||||||||||||||||||||||||||||||||
| import yaml | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| from .base import BaseDBConfigGenerator | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| class BigQueryConfigGenerator(BaseDBConfigGenerator): | ||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Dedicated generator mapping properties to explicit BigQuery configuration | ||||||||||||||||||||||||||||||||||||||||||||||||
| topologies utilized by both EvalBench binaries and GDA Context objects. | ||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| SOURCE_TYPE = "bigquery" | ||||||||||||||||||||||||||||||||||||||||||||||||
| DIALECT = "googlesql" | ||||||||||||||||||||||||||||||||||||||||||||||||
| REQUIRED_FIELDS = BaseDBConfigGenerator.REQUIRED_FIELDS | { | ||||||||||||||||||||||||||||||||||||||||||||||||
| "project", | ||||||||||||||||||||||||||||||||||||||||||||||||
| "dataset", | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| def __init__(self, params: dict[str, Any]): | ||||||||||||||||||||||||||||||||||||||||||||||||
| super().__init__(params) | ||||||||||||||||||||||||||||||||||||||||||||||||
| self.project = params.get("project") | ||||||||||||||||||||||||||||||||||||||||||||||||
| self.dataset = params.get("dataset") | ||||||||||||||||||||||||||||||||||||||||||||||||
| self.location = params.get("location") | ||||||||||||||||||||||||||||||||||||||||||||||||
| # Optional explicit table scoping; the public GDA proto references | ||||||||||||||||||||||||||||||||||||||||||||||||
| # BigQuery at table granularity rather than dataset granularity. | ||||||||||||||||||||||||||||||||||||||||||||||||
| self.tables = params.get("tables") or [] | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| def generate_db_config(self) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||
| db_type = "bigquery" | ||||||||||||||||||||||||||||||||||||||||||||||||
| db_path = f"projects/{self.project}/datasets/{self.dataset}" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| db_config = { | ||||||||||||||||||||||||||||||||||||||||||||||||
| "db_type": db_type, | ||||||||||||||||||||||||||||||||||||||||||||||||
| "dialect": self.DIALECT, | ||||||||||||||||||||||||||||||||||||||||||||||||
| "database_name": self.dataset, | ||||||||||||||||||||||||||||||||||||||||||||||||
| "database_path": db_path, | ||||||||||||||||||||||||||||||||||||||||||||||||
| "gcp_project_id": self.project, | ||||||||||||||||||||||||||||||||||||||||||||||||
| "max_executions_per_minute": 100, | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| if self.location: | ||||||||||||||||||||||||||||||||||||||||||||||||
| db_config["location"] = self.location | ||||||||||||||||||||||||||||||||||||||||||||||||
| return yaml.safe_dump( | ||||||||||||||||||||||||||||||||||||||||||||||||
| db_config, sort_keys=False, default_flow_style=False | ||||||||||||||||||||||||||||||||||||||||||||||||
| ).strip() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| def build_datasource_reference( | ||||||||||||||||||||||||||||||||||||||||||||||||
| self, context_set_id: str | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> gda.DatasourceReferences: | ||||||||||||||||||||||||||||||||||||||||||||||||
| datasource_ref = gda.DatasourceReferences() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| table_references = [ | ||||||||||||||||||||||||||||||||||||||||||||||||
| gda.BigQueryTableReference( | ||||||||||||||||||||||||||||||||||||||||||||||||
| project_id=self.project, | ||||||||||||||||||||||||||||||||||||||||||||||||
| dataset_id=self.dataset, | ||||||||||||||||||||||||||||||||||||||||||||||||
| table_id=table_id, | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||
| for table_id in self.tables | ||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| bq_references = gda.BigQueryTableReferences( | ||||||||||||||||||||||||||||||||||||||||||||||||
| table_references=table_references | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| # The agent_context_reference field on BigQueryTableReferences is not | ||||||||||||||||||||||||||||||||||||||||||||||||
| # yet available in the public google-cloud-geminidataanalytics SDK | ||||||||||||||||||||||||||||||||||||||||||||||||
| # (restricted-visibility rollout; see the Wave2 FR). Attach it | ||||||||||||||||||||||||||||||||||||||||||||||||
| # dynamically so this generator works with internal SDK builds and | ||||||||||||||||||||||||||||||||||||||||||||||||
| # degrades gracefully on public ones. | ||||||||||||||||||||||||||||||||||||||||||||||||
| if "agent_context_reference" in { | ||||||||||||||||||||||||||||||||||||||||||||||||
| f.name for f in type(bq_references).pb(bq_references).DESCRIPTOR.fields | ||||||||||||||||||||||||||||||||||||||||||||||||
| }: | ||||||||||||||||||||||||||||||||||||||||||||||||
| bq_references.agent_context_reference = gda.AgentContextReference( | ||||||||||||||||||||||||||||||||||||||||||||||||
| context_set_id=context_set_id | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+67
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If To make this fully robust, we should dynamically retrieve the class using
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| datasource_ref.bq = bq_references | ||||||||||||||||||||||||||||||||||||||||||||||||
| return datasource_ref | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We try to isolate dialect/product specific things to dedicated dialect files.
Do you need this here or can it go into references/template/bigquery.md used by context-generation-guide?