Compare commits

...

1 Commits

Author SHA1 Message Date
Waleed Latif
c024526d74 feat(google-sheets): add filter support to read operation 2026-02-25 11:35:35 -08:00
6 changed files with 109 additions and 10 deletions

View File

@@ -38,8 +38,8 @@ import {
EyeIcon,
FirecrawlIcon,
FirefliesIcon,
GithubIcon,
GitLabIcon,
GithubIcon,
GmailIcon,
GongIcon,
GoogleBooksIcon,
@@ -72,9 +72,9 @@ import {
LinearIcon,
LinkedInIcon,
LinkupIcon,
MailServerIcon,
MailchimpIcon,
MailgunIcon,
MailServerIcon,
Mem0Icon,
MicrosoftDataverseIcon,
MicrosoftExcelIcon,
@@ -107,6 +107,8 @@ import {
ResendIcon,
RevenueCatIcon,
S3Icon,
SQSIcon,
STTIcon,
SalesforceIcon,
SearchIcon,
SendgridIcon,
@@ -118,19 +120,17 @@ import {
SimilarwebIcon,
SlackIcon,
SmtpIcon,
SQSIcon,
SshIcon,
STTIcon,
StagehandIcon,
StripeIcon,
SupabaseIcon,
TTSIcon,
TavilyIcon,
TelegramIcon,
TextractIcon,
TinybirdIcon,
TranslateIcon,
TrelloIcon,
TTSIcon,
TwilioIcon,
TypeformIcon,
UpstashIcon,
@@ -141,11 +141,11 @@ import {
WhatsAppIcon,
WikipediaIcon,
WordpressIcon,
xIcon,
YouTubeIcon,
ZendeskIcon,
ZepIcon,
ZoomIcon,
xIcon,
} from '@/components/icons'
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>

View File

@@ -146,4 +146,4 @@
"zep",
"zoom"
]
}
}

View File

@@ -440,6 +440,36 @@ Return ONLY the range string - no sheet name, no explanations, no quotes.`,
placeholder: 'Describe the range (e.g., "first 50 rows" or "column A")...',
},
},
// Read Filter Fields (advanced mode only)
{
id: 'filterColumn',
title: 'Filter Column',
type: 'short-input',
placeholder: 'Column header name to filter on (e.g., Email, Status)',
condition: { field: 'operation', value: 'read' },
mode: 'advanced',
},
{
id: 'filterValue',
title: 'Filter Value',
type: 'short-input',
placeholder: 'Value to match against',
condition: { field: 'operation', value: 'read' },
mode: 'advanced',
},
{
id: 'filterMatchType',
title: 'Match Type',
type: 'dropdown',
options: [
{ label: 'Contains', id: 'contains' },
{ label: 'Exact Match', id: 'exact' },
{ label: 'Starts With', id: 'starts_with' },
{ label: 'Ends With', id: 'ends_with' },
],
condition: { field: 'operation', value: 'read' },
mode: 'advanced',
},
// Write-specific Fields
{
id: 'values',
@@ -748,6 +778,9 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
batchData,
sheetId,
destinationSpreadsheetId,
filterColumn,
filterValue,
filterMatchType,
...rest
} = params
@@ -836,6 +869,11 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
cellRange: cellRange ? (cellRange as string).trim() : undefined,
values: parsedValues,
oauthCredential,
...(filterColumn ? { filterColumn: (filterColumn as string).trim() } : {}),
...(filterValue !== undefined && filterValue !== ''
? { filterValue: filterValue as string }
: {}),
...(filterMatchType ? { filterMatchType: filterMatchType as string } : {}),
}
},
},
@@ -858,6 +896,12 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
type: 'string',
description: 'Destination spreadsheet ID for copy',
},
filterColumn: { type: 'string', description: 'Column header name to filter on' },
filterValue: { type: 'string', description: 'Value to match against the filter column' },
filterMatchType: {
type: 'string',
description: 'Match type: contains, exact, starts_with, or ends_with',
},
},
outputs: {
// Read outputs

View File

@@ -154,6 +154,26 @@ export const readV2Tool: ToolConfig<GoogleSheetsV2ToolParams, GoogleSheetsV2Read
description:
'The cell range to read (e.g. "A1:D10"). Defaults to "A1:Z1000" if not specified.',
},
filterColumn: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Column name (from header row) to filter on. If not provided, no filtering is applied.',
},
filterValue: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Value to match against the filter column.',
},
filterMatchType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'How to match the filter value: "contains", "exact", "starts_with", or "ends_with". Defaults to "contains".',
},
},
request: {
@@ -196,12 +216,45 @@ export const readV2Tool: ToolConfig<GoogleSheetsV2ToolParams, GoogleSheetsV2Read
spreadsheetUrl: `https://docs.google.com/spreadsheets/d/${spreadsheetId}`,
}
let values: unknown[][] = data.values ?? []
// Apply client-side filtering only when both filterColumn and filterValue are provided
if (params?.filterColumn && params?.filterValue !== undefined && values.length > 1) {
const headers = values[0] as string[]
const columnIndex = headers.findIndex(
(h) => String(h).toLowerCase() === params.filterColumn!.toLowerCase()
)
if (columnIndex !== -1) {
const matchType = params.filterMatchType ?? 'contains'
const filterVal = params.filterValue.toLowerCase()
const filteredRows = values.slice(1).filter((row) => {
const cellValue = String(row[columnIndex] ?? '').toLowerCase()
switch (matchType) {
case 'exact':
return cellValue === filterVal
case 'starts_with':
return cellValue.startsWith(filterVal)
case 'ends_with':
return cellValue.endsWith(filterVal)
case 'contains':
default:
return cellValue.includes(filterVal)
}
})
// Return header row + matching rows
values = [values[0], ...filteredRows]
}
}
return {
success: true,
output: {
sheetName: params?.sheetName ?? '',
range: data.range ?? '',
values: data.values ?? [],
values,
metadata: {
spreadsheetId: metadata.spreadsheetId,
spreadsheetUrl: metadata.spreadsheetUrl,

View File

@@ -129,6 +129,9 @@ export interface GoogleSheetsV2ToolParams {
includeValuesInResponse?: boolean
responseValueRenderOption?: 'FORMATTED_VALUE' | 'UNFORMATTED_VALUE' | 'FORMULA'
majorDimension?: 'ROWS' | 'COLUMNS'
filterColumn?: string
filterValue?: string
filterMatchType?: 'contains' | 'exact' | 'starts_with' | 'ends_with'
}
export type GoogleSheetsV2Response =

View File

@@ -65,8 +65,7 @@
"rules": {
"recommended": true,
"nursery": {
"useSortedClasses": "warn",
"noNestedComponentDefinitions": "off"
"useSortedClasses": "warn"
},
"a11y": {
"noSvgWithoutTitle": "off",