Deep Agents
AgentContextOrchestratorRetrievalText2SQLToolbox

Spreadsheet

Query Excel and CSV files with natural language using Text2SQL

The Spreadsheet adapter loads Excel (.xlsx/.xls) and CSV/TSV files into an in-memory SQLite database, enabling natural language queries against spreadsheet data. Each sheet becomes a queryable table.

Basic Setup

import { InMemoryHistory, Text2Sql } from '@deepagents/text2sql';
import { Spreadsheet, tables, info } from '@deepagents/text2sql/spreadsheet';

const text2sql = new Text2Sql({
  version: 'v1',
  adapter: new Spreadsheet({
    file: './sales-data.xlsx',
    grounding: [tables(), info()],
  }),
  history: new InMemoryHistory(),
});

// Query your spreadsheet with natural language
const result = await text2sql.toSql('What are the top 5 products by revenue?');

// Don't forget to close when done
text2sql.adapter.close();

Configuration Options

import { Spreadsheet, tables, info, rowCount } from '@deepagents/text2sql/spreadsheet';

new Spreadsheet({
  // Required: Path to Excel or CSV/TSV file
  file: './data.xlsx',

  // Required: Grounding functions for schema introspection
  grounding: [
    tables(),
    info(),
    rowCount(),
  ],

  // Optional: Persist SQLite database to file (default: in-memory)
  database: './cache.db',
});

Supported File Formats

FormatExtensionTable Naming
Excel.xlsx, .xlsEach sheet becomes a table (sheet name → table name)
CSV.csvFilename becomes table name
TSV.tsvFilename becomes table name

Table and Column Naming

The adapter automatically sanitizes identifiers for SQL compatibility:

OriginalSanitizedRule
My Sheet!my_sheetSpecial chars → underscore, lowercase
123Data_123dataPrefix underscore if starts with number
Customer IDcustomer_idSpaces → underscores
aaaa... (100 chars)aaaa... (64 chars)Truncated to 64 characters
col, col, colcol, col_2, col_3Duplicates get numeric suffix

Type Inference

The adapter automatically infers SQLite types from your data:

Data PatternSQLite TypeExample Values
All integersINTEGER1, 42, 100
Any decimalsREAL3.14, 99.99
DatesTEXTStored as YYYY-MM-DD
Mixed/textTEXT"hello", "123"
Empty/nullNULLEmpty cells become NULL

Multi-Sheet Excel Files

Each sheet in an Excel file becomes a separate table that you can query and join:

import { Spreadsheet, tables } from '@deepagents/text2sql/spreadsheet';

// sales.xlsx has sheets: "Customers", "Orders", "Products"
const adapter = new Spreadsheet({
  file: './sales.xlsx',
  grounding: [tables()],
});

// Query individual tables
const customers = await adapter.execute('SELECT * FROM customers');

// Join across sheets
const results = await adapter.execute(`
  SELECT c.name, o.amount
  FROM customers c
  JOIN orders o ON c.id = o.customer_id
`);

adapter.close();

Available Grounding Functions

The Spreadsheet adapter reuses SQLite grounding functions. Import from @deepagents/text2sql/spreadsheet:

FunctionDescription
tables()Lists all tables (sheets) with their columns and types
info()Reports SQLite dialect and version info
rowCount()Counts rows per table with size classification
columnStats()Collects min, max, null fraction for columns
columnValues()Samples distinct values from columns

See Grounding for detailed configuration options.

Persisting the Database

By default, the adapter creates an in-memory database. For large files or repeated queries, persist to disk:

const adapter = new Spreadsheet({
  file: './large-dataset.xlsx',
  database: './cache.db', // SQLite file is created here
  grounding: [tables()],
});

// Later sessions can query the persisted database directly

Handling Special Data

Unicode and International Characters

// Full Unicode support including CJK, accents, and emoji
const adapter = new Spreadsheet({
  file: './international-data.xlsx',
  grounding: [tables()],
});

const results = await adapter.execute("SELECT * FROM data WHERE text LIKE '%日本%'");

Dates

Excel dates are automatically converted to YYYY-MM-DD format:

// Query date ranges
const events = await adapter.execute(`
  SELECT * FROM events
  WHERE date >= '2024-01-01' AND date < '2025-01-01'
`);

NULL Values

Empty cells and missing values become SQL NULL:

// Find rows with missing emails
const incomplete = await adapter.execute(`
  SELECT * FROM customers WHERE email IS NULL
`);

Error Handling

ErrorCauseSolution
Failed to read spreadsheetFile not found or corruptedCheck file path and format
No valid sheets foundAll sheets are emptyEnsure spreadsheet has data
Cannot create table with no columnsSheet has no headersAdd header row to sheet

Full Example

import { InMemoryHistory, Text2Sql } from '@deepagents/text2sql';
import {
  Spreadsheet,
  tables,
  info,
  rowCount,
  columnStats,
} from '@deepagents/text2sql/spreadsheet';

// Load an Excel file with customer and order data
const adapter = new Spreadsheet({
  file: './ecommerce.xlsx',
  grounding: [
    tables(),
    info(),
    rowCount(),
    columnStats(),
  ],
});

const text2sql = new Text2Sql({
  version: 'v1',
  adapter,
  history: new InMemoryHistory(),
});

// Introspect schema
const schema = await adapter.introspect();
console.log('Schema:', schema);

// Natural language queries
const topCustomers = await text2sql.toSql(
  'Who are the top 10 customers by total order amount?'
);
console.log('SQL:', topCustomers.sql);

const results = await adapter.execute(topCustomers.sql);
console.log('Results:', results);

// Always close when done
adapter.close();

Size Hints

Tables are classified by row count (inherited from SQLite adapter):

Size HintRow Count
tiny< 100
small100 - 999
medium1,000 - 9,999
large10,000 - 99,999
huge>= 100,000