Datasource Connectors
Guide the user through loading data from any common source into a working dataset.
This module is a decision framework, not a library. It tells the LLM what to ask,
what to watch for, and how to handle each format's quirks.
When to Use
- The brief names a data source that is not yet loaded
- The user says "the data is in X" where X is a file, database, or API
- The orchestrator's plan includes an Ingest step
When to Skip
- Data is already loaded (inline table, prior step output, user pasted it)
- The brief says "data is pre-loaded" or "use this dataframe"
Connector Selection
Ask the user what format the data is in, or infer from the path/URL:
| Signal |
Connector |
.csv, .tsv, .txt (tabular) |
CSV |
.json, .jsonl, .ndjson |
JSON |
http://, https:// + returns JSON/XML |
REST API |
Connection string, .sql, database name |
SQL |
.xlsx, .xls |
Excel |
.parquet, .arrow |
Parquet |
If ambiguous, ask. Do not guess the format.
CSV / TSV
What to ask
- Does the file have a header row? (default: yes)
- What is the delimiter? (auto-detect: comma, tab, semicolon, pipe)
- What is the encoding? (default: UTF-8; watch for: Latin-1, Windows-1252)
Quirks to handle
| Problem |
Detection |
Fix |
| Wrong delimiter |
First row has one column |
Try tab, semicolon, pipe |
| Encoding garbled |
Non-ASCII chars show as ? or é |
Re-read as Latin-1 or Windows-1252 |
| Trailing commas |
Row has one extra empty column |
Strip trailing delimiter |
| Quoted fields with commas |
Standard CSV; most parsers handle it |
Use RFC 4180 compliant parser |
| Mixed line endings |
\r\n and \n in same file |
Normalize to \n |
| BOM marker |
\xEF\xBB\xBF at file start |
Strip or let parser handle |
Output
A table with typed columns. Report: row count, column count, detected delimiter,
detected encoding.
JSON / JSONL
What to ask
- Is it a single JSON object, an array, or newline-delimited (JSONL)?
- What path holds the data? (e.g.,
data.results[], root array)
Quirks to handle
| Problem |
Detection |
Fix |
| Nested objects |
Column values are objects, not scalars |
Flatten with dot notation (address.city) |
| Mixed types in array |
Some items have extra/missing keys |
Union all keys; fill missing with null |
| Large file (> 100MB) |
Slow to parse |
Stream with JSONL or chunk |
| Date strings |
ISO 8601 or Unix timestamps |
Parse to date; state the format detected |
Output
A flat table. Nested objects flattened to dot-notation columns. Report: record
count, nesting depth, keys found.
REST API
What to ask
- What is the endpoint URL?
- What authentication is needed? (none, API key, OAuth, bearer token)
- Does it paginate? (offset, cursor, link header, or single page)
- What response path holds the data? (e.g.,
results[])
Authentication patterns
| Method |
How to use |
Security |
| API key in header |
Authorization: Api-Key <key> or custom header |
Never log the key |
| Bearer token |
Authorization: Bearer <token> |
Store in env var or secret |
| OAuth 2.0 |
Client credentials or authorization code flow |
Use refresh tokens |
| No auth |
Public API |
Still rate-limit respectfully |
Never hardcode credentials. Use environment variables or secret storage.
Pagination patterns
| Pattern |
Detection |
Handling |
| Offset |
?offset=0&limit=100 |
Increment offset until empty page |
| Cursor |
Response has next_cursor field |
Pass cursor to next request |
| Link header |
Link: <url>; rel="next" |
Follow rel="next" until absent |
| Page number |
?page=1&per_page=50 |
Increment page until empty |
Set a maximum page limit (default: 100 pages) to prevent runaway loops.
Retry and timeout
| Rule |
Value |
| Timeout per request |
30 seconds |
| Retries on 429/5xx |
3 with exponential backoff (1s, 2s, 4s) |
| Retries on network error |
2 |
| Never retry |
400, 401, 403, 404 (client errors are not transient) |
Output
A flat table from all pages combined. Report: total records, pages fetched,
any errors skipped.
SQL
What to ask
- What database engine? (PostgreSQL, SQL Server, MySQL, SQLite)
- Connection string or host/port/database?
- What table or query?
Security rules
| Rule |
Reason |
| Always use parameterized queries |
Prevents SQL injection |
| Never interpolate user input into SQL |
Even for table names; use allowlists |
| Read-only connection |
The pipeline only reads; never write |
| Credentials in env vars |
Never in code, never in logs |
Query patterns
-- Simple table read
SELECT * FROM schema.table_name LIMIT 1000;
-- Filtered read (parameterized)
SELECT * FROM sales WHERE region = @region AND date >= @start_date;
-- Aggregated read (push aggregation to the database when possible)
SELECT region, month, SUM(revenue) AS revenue
FROM sales
GROUP BY region, month;
Push aggregation and filtering to the database when possible. Pulling raw data
and aggregating in the LLM wastes tokens and is slower.
Output
A table. Report: row count, column count, query execution time, database engine.
Excel
What to ask
- Which sheet? (default: first sheet)
- Does the data start at A1 or is there a header region to skip?
- Are there named ranges?
Quirks to handle
| Problem |
Detection |
Fix |
| Multiple sheets |
Workbook has > 1 sheet |
Ask which sheet; default to first |
| Header not in row 1 |
First rows are title/metadata |
Ask for header row number |
| Merged cells |
Values span multiple rows/columns |
Unmerge and fill down |
| Formulas |
Cells contain formulas, not values |
Read values, not formulas |
| Mixed types in column |
Numbers and text in same column |
Coerce to string; flag for data-preparation |
| Date serial numbers |
Dates stored as integers (e.g., 45678) |
Convert using Excel epoch (1899-12-30) |
Output
A table from the specified sheet and range. Report: sheet name, row count,
column count, any merged cells detected.
Parquet / Arrow
What to ask
- Path to the file or directory (partitioned datasets use directories)
Advantages
Parquet is self-describing: schema, types, and encoding are embedded. No
delimiter guessing, no encoding issues, no header detection.
Quirks to handle
| Problem |
Detection |
Fix |
| Partitioned directory |
Path is a directory with part-*.parquet files |
Read all partitions |
| Nested columns (struct/list) |
Schema shows complex types |
Flatten or ask user which fields |
| Large file |
> 1M rows |
Sample or filter before loading all |
Output
A typed table. Report: row count, column count, schema summary (types from
the Parquet metadata).
Error Handling (all connectors)
| Situation |
Action |
| File not found |
Report the exact path tried. Ask user to verify. |
| Permission denied |
Report the error. Do not retry. Ask user to fix permissions. |
| Empty dataset (0 rows) |
Report it. Push back: "file exists but has no data rows." |
| Partial data (truncated) |
Report row count. Ask if this is expected. |
| Unsupported format |
State what was detected. Ask user for the correct format. |
Never fail silently. Every error must produce a specific, actionable message.
Output Contract
Pass to the next module (data-preparation):
- The loaded dataset (or a reference to it)
- A one-line summary: format, row count, column count, source path
- Any warnings (encoding fallback, partial data, skipped errors)
1---2name: datasource-connectors3description: Ingestion patterns for CSV, JSON, REST API, SQL, Excel, and Parquet -- guides an LLM through loading data from any common source4---5
6# Datasource Connectors
7
8Guide the user through loading data from any common source into a working dataset.
9This module is a decision framework, not a library. It tells the LLM what to ask,
10what to watch for, and how to handle each format's quirks.
11
12## When to Use
13
14- The brief names a data source that is not yet loaded
15- The user says "the data is in X" where X is a file, database, or API
16- The orchestrator's plan includes an Ingest step
17
18## When to Skip
19
20- Data is already loaded (inline table, prior step output, user pasted it)
21- The brief says "data is pre-loaded" or "use this dataframe"
22
23## Connector Selection
24
25Ask the user what format the data is in, or infer from the path/URL:
26
27| Signal | Connector |
28| --- | --- |
29| `.csv`, `.tsv`, `.txt` (tabular) | CSV |
30| `.json`, `.jsonl`, `.ndjson` | JSON |
31| `http://`, `https://` + returns JSON/XML | REST API |
32| Connection string, `.sql`, database name | SQL |
33| `.xlsx`, `.xls` | Excel |
34| `.parquet`, `.arrow` | Parquet |
35
36If ambiguous, ask. Do not guess the format.
37
38## CSV / TSV
39
40### What to ask
41
42- Does the file have a header row? (default: yes)
43- What is the delimiter? (auto-detect: comma, tab, semicolon, pipe)
44- What is the encoding? (default: UTF-8; watch for: Latin-1, Windows-1252)
45
46### Quirks to handle
47
48| Problem | Detection | Fix |
49| --- | --- | --- |
50| Wrong delimiter | First row has one column | Try tab, semicolon, pipe |
51| Encoding garbled | Non-ASCII chars show as `?` or `é` | Re-read as Latin-1 or Windows-1252 |
52| Trailing commas | Row has one extra empty column | Strip trailing delimiter |
53| Quoted fields with commas | Standard CSV; most parsers handle it | Use RFC 4180 compliant parser |
54| Mixed line endings | `\r\n` and `\n` in same file | Normalize to `\n` |
55| BOM marker | `\xEF\xBB\xBF` at file start | Strip or let parser handle |
56
57### Output
58
59A table with typed columns. Report: row count, column count, detected delimiter,
60detected encoding.
61
62## JSON / JSONL
63
64### What to ask
65
66- Is it a single JSON object, an array, or newline-delimited (JSONL)?
67- What path holds the data? (e.g., `data.results[]`, root array)
68
69### Quirks to handle
70
71| Problem | Detection | Fix |
72| --- | --- | --- |
73| Nested objects | Column values are objects, not scalars | Flatten with dot notation (`address.city`) |
74| Mixed types in array | Some items have extra/missing keys | Union all keys; fill missing with null |
75| Large file (> 100MB) | Slow to parse | Stream with JSONL or chunk |
76| Date strings | ISO 8601 or Unix timestamps | Parse to date; state the format detected |
77
78### Output
79
80A flat table. Nested objects flattened to dot-notation columns. Report: record
81count, nesting depth, keys found.
82
83## REST API
84
85### What to ask
86
871. What is the endpoint URL?
882. What authentication is needed? (none, API key, OAuth, bearer token)
893. Does it paginate? (offset, cursor, link header, or single page)
904. What response path holds the data? (e.g., `results[]`)
91
92### Authentication patterns
93
94| Method | How to use | Security |
95| --- | --- | --- |
96| **API key in header** | `Authorization: Api-Key <key>` or custom header | Never log the key |
97| **Bearer token** | `Authorization: Bearer <token>` | Store in env var or secret |
98| **OAuth 2.0** | Client credentials or authorization code flow | Use refresh tokens |
99| **No auth** | Public API | Still rate-limit respectfully |
100
101Never hardcode credentials. Use environment variables or secret storage.
102
103### Pagination patterns
104
105| Pattern | Detection | Handling |
106| --- | --- | --- |
107| **Offset** | `?offset=0&limit=100` | Increment offset until empty page |
108| **Cursor** | Response has `next_cursor` field | Pass cursor to next request |
109| **Link header** | `Link: <url>; rel="next"` | Follow `rel="next"` until absent |
110| **Page number** | `?page=1&per_page=50` | Increment page until empty |
111
112Set a maximum page limit (default: 100 pages) to prevent runaway loops.
113
114### Retry and timeout
115
116| Rule | Value |
117| --- | --- |
118| Timeout per request | 30 seconds |
119| Retries on 429/5xx | 3 with exponential backoff (1s, 2s, 4s) |
120| Retries on network error | 2 |
121| Never retry | 400, 401, 403, 404 (client errors are not transient) |
122
123### Output
124
125A flat table from all pages combined. Report: total records, pages fetched,
126any errors skipped.
127
128## SQL
129
130### What to ask
131
1321. What database engine? (PostgreSQL, SQL Server, MySQL, SQLite)
1332. Connection string or host/port/database?
1343. What table or query?
135
136### Security rules
137
138| Rule | Reason |
139| --- | --- |
140| **Always use parameterized queries** | Prevents SQL injection |
141| **Never interpolate user input into SQL** | Even for table names; use allowlists |
142| **Read-only connection** | The pipeline only reads; never write |
143| **Credentials in env vars** | Never in code, never in logs |
144
145### Query patterns
146
147```sql
148-- Simple table read
149SELECT * FROM schema.table_name LIMIT 1000;
150
151-- Filtered read (parameterized)
152SELECT * FROM sales WHERE region = @region AND date >= @start_date;
153
154-- Aggregated read (push aggregation to the database when possible)
155SELECT region, month, SUM(revenue) AS revenue
156FROM sales
157GROUP BY region, month;
158```
159
160Push aggregation and filtering to the database when possible. Pulling raw data
161and aggregating in the LLM wastes tokens and is slower.
162
163### Output
164
165A table. Report: row count, column count, query execution time, database engine.
166
167## Excel
168
169### What to ask
170
1711. Which sheet? (default: first sheet)
1722. Does the data start at A1 or is there a header region to skip?
1733. Are there named ranges?
174
175### Quirks to handle
176
177| Problem | Detection | Fix |
178| --- | --- | --- |
179| Multiple sheets | Workbook has > 1 sheet | Ask which sheet; default to first |
180| Header not in row 1 | First rows are title/metadata | Ask for header row number |
181| Merged cells | Values span multiple rows/columns | Unmerge and fill down |
182| Formulas | Cells contain formulas, not values | Read values, not formulas |
183| Mixed types in column | Numbers and text in same column | Coerce to string; flag for data-preparation |
184| Date serial numbers | Dates stored as integers (e.g., 45678) | Convert using Excel epoch (1899-12-30) |
185
186### Output
187
188A table from the specified sheet and range. Report: sheet name, row count,
189column count, any merged cells detected.
190
191## Parquet / Arrow
192
193### What to ask
194
195- Path to the file or directory (partitioned datasets use directories)
196
197### Advantages
198
199Parquet is self-describing: schema, types, and encoding are embedded. No
200delimiter guessing, no encoding issues, no header detection.
201
202### Quirks to handle
203
204| Problem | Detection | Fix |
205| --- | --- | --- |
206| Partitioned directory | Path is a directory with `part-*.parquet` files | Read all partitions |
207| Nested columns (struct/list) | Schema shows complex types | Flatten or ask user which fields |
208| Large file | > 1M rows | Sample or filter before loading all |
209
210### Output
211
212A typed table. Report: row count, column count, schema summary (types from
213the Parquet metadata).
214
215## Error Handling (all connectors)
216
217| Situation | Action |
218| --- | --- |
219| File not found | Report the exact path tried. Ask user to verify. |
220| Permission denied | Report the error. Do not retry. Ask user to fix permissions. |
221| Empty dataset (0 rows) | Report it. Push back: "file exists but has no data rows." |
222| Partial data (truncated) | Report row count. Ask if this is expected. |
223| Unsupported format | State what was detected. Ask user for the correct format. |
224
225Never fail silently. Every error must produce a specific, actionable message.
226
227## Output Contract
228
229Pass to the next module (`data-preparation`):
230
231- The loaded dataset (or a reference to it)
232- A one-line summary: format, row count, column count, source path
233- Any warnings (encoding fallback, partial data, skipped errors)