Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

When tasked with creating new pairs, using the generation plan as the guideline and north star. For *every* generated pair, execute the following internal Chain of Thought:

1. **Draft SQL (Schema-First):** Write syntactically perfect, dialect-compliant SQL using prioritized tables and conditions. Make sure to adhere to the technical schema and generation plan. Avoid inventing columns or tables based on business documents alone.
1. **Draft SQL (Schema-First):** Write syntactically perfect, dialect-compliant SQL using prioritized tables and conditions. Make sure to adhere to the technical schema and generation plan. Avoid inventing columns or tables based on business documents alone. For **BigQuery** sources, every table reference must be fully qualified as `` `project`.`dataset`.`table` `` (taken from the `kind: source` block in `tools.yaml`) — BigQuery has no default dataset at query time, so bare table names fail with "Table not found".

Copy link
Copy Markdown
Collaborator

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?

2. **Literal Translation:** Translate the SQL literally into English to guarantee no logical constraints are missed.
3. **Humanize & Bridge:** Rewrite the literal translation into natural business language, applying the *Semantic Bridge* principle.
4. **Verify and Refine:** Apply `acceptance-criteria.md` to verify the generated pair. If the pair fails the acceptance criteria, backtrack and try again.
17 changes: 17 additions & 0 deletions plugin/skills/context-engineering-evaluate/references/bigquery.md
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
```
1 change: 1 addition & 0 deletions plugin/skills/context-engineering-init/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ When collecting information from the user, inform the user that only Application
- Cloud SQL MySQL
- AlloyDB Postgres
- Spanner
- BigQuery
2. **Collect Information:** Request all **Required Information** based on the templates inside this directory. Do NOT assume missing fields; ask the user for them explicitly.
3. **Generate Configuration:** Replace all placeholders with the user's provided values and generate the complete `tools.yaml` content. Save it to the target location (e.g., `autoctx/tools.yaml` for Autoctx workflows, or `tools.yaml` in the current directory for standalone use).
4. **Validate:** After saving, validate the new connection using the toolbox script, replacing `<config_path>` with the actual path to the file:
Expand Down
36 changes: 36 additions & 0 deletions plugin/skills/context-engineering-init/references/bigquery.md
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.
```
5 changes: 4 additions & 1 deletion plugin/skills/context-generation-guide/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Context generation allows you to create specific, high-value items in three form
When asked to generate context items:
1. **Identify the Type**: Determine if the user wants to create a Template, Facet, or Value Search.
2. **Gather Information**: Ensure you have all the required information for the chosen context type as described in the "Context Type Definitions" section below. If information is missing, try to explore the database to find it or ask the user for clarification.
3. **Select Dialect Reference**: Identify the target database dialect (PostgreSQL, GoogleSQL, or MySQL) and consult the corresponding file in `references/` for specific syntax and patterns.
3. **Select Dialect Reference**: Identify the target database dialect (PostgreSQL, GoogleSQL for Spanner, GoogleSQL for BigQuery, or MySQL) and consult the corresponding file in `references/` for specific syntax and patterns.
4. **Parameterize**: Follow the [Phrase Extraction and Parameterization Guidelines](references/phrase_extraction/guidelines.md) to generalize the values.
5. **Format Output**: Construct the final JSON object according to the examples in the reference files.
6. **Save Context**: Use the appropriate MCP tool (e.g., `mutate_context_set`) to save or update the context set.
Expand Down Expand Up @@ -131,12 +131,15 @@ For specific SQL templates, examples, and performance recommendations, refer to
* **Templates**:
* [PostgreSQL](references/template/postgresql.md)
* [Spanner (GoogleSQL)](references/template/googlesql.md)
* [BigQuery (GoogleSQL)](references/template/bigquery.md)
* [MySQL](references/template/mysql.md)
* **Facets**:
* [PostgreSQL](references/facet/postgresql.md)
* [Spanner (GoogleSQL)](references/facet/googlesql.md)
* [BigQuery (GoogleSQL)](references/facet/bigquery.md)
* [MySQL](references/facet/mysql.md)
* **Value Searches**:
* [PostgreSQL](references/value_search/postgresql.md)
* [Spanner (GoogleSQL)](references/value_search/googlesql.md)
* [BigQuery (GoogleSQL)](references/value_search/bigquery.md)
* [MySQL](references/value_search/mysql.md)
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If agent_context_reference is not yet available in the public SDK, it is highly likely that the AgentContextReference class itself is also missing or restricted in that version. Referencing gda.AgentContextReference directly will raise an AttributeError at runtime, defeating the graceful degradation goal.

To make this fully robust, we should dynamically retrieve the class using getattr and check its existence. Additionally, we can use bq_references._pb to access the underlying protobuf message, which is more consistent with how it is done elsewhere in the codebase (e.g., query_context._pb in BaseDBConfigGenerator).

Suggested change
# 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
)
# 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.
agent_context_ref_cls = getattr(gda, "AgentContextReference", None)
if agent_context_ref_cls and "agent_context_reference" in {
f.name for f in bq_references._pb.DESCRIPTOR.fields
}:
bq_references.agent_context_reference = agent_context_ref_cls(
context_set_id=context_set_id
)


datasource_ref.bq = bq_references
return datasource_ref
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from .db_generators.alloydb import AlloyDBConfigGenerator
from .db_generators.base import BaseDBConfigGenerator
from .db_generators.bigquery import BigQueryConfigGenerator
from .db_generators.mysql import MySQLConfigGenerator
from .db_generators.postgres import PostgresConfigGenerator
from .db_generators.spanner import SpannerConfigGenerator
Expand Down Expand Up @@ -130,6 +131,7 @@ def _get_db_generator(params: dict[str, Any]) -> BaseDBConfigGenerator:

generators = {
AlloyDBConfigGenerator.SOURCE_TYPE: AlloyDBConfigGenerator,
BigQueryConfigGenerator.SOURCE_TYPE: BigQueryConfigGenerator,
PostgresConfigGenerator.SOURCE_TYPE: PostgresConfigGenerator,
MySQLConfigGenerator.SOURCE_TYPE: MySQLConfigGenerator,
SpannerConfigGenerator.SOURCE_TYPE: SpannerConfigGenerator,
Expand Down
2 changes: 1 addition & 1 deletion src/google/cloud/db_context_enrichment/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def generate_evalbench_configs(
dataset_path: The absolute path to the golden dataset file in the simplified user-facing format (JSON list of objects with keys: "id", "database", "nlq", "golden_sql").
context_set_id: Full ContextSet resource name to evaluate against.
toolbox_config_path: The absolute path to the tools.yaml configuration file.
toolbox_source_name: The name of the database source to use inside tools.yaml. The underlying source block must use a supported 'type' (cloud-sql-postgres, cloud-sql-mysql, spanner, alloydb-postgres).
toolbox_source_name: The name of the database source to use inside tools.yaml. The underlying source block must use a supported 'type' (cloud-sql-postgres, cloud-sql-mysql, spanner, alloydb-postgres, bigquery).

Returns:
A message indicating that the configuration files were successfully created.
Expand Down
Loading
Loading