From 6c006cdfec2d20c91c27e00a4df974f5dbefa2cd Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 12 Feb 2026 13:03:54 -0800 Subject: [PATCH 1/4] feat(google books): Add google books integration --- apps/docs/components/icons.tsx | 15 ++ apps/docs/components/ui/icon-mapping.ts | 2 + .../content/docs/en/tools/google_books.mdx | 96 +++++++++ apps/docs/content/docs/en/tools/meta.json | 1 + apps/sim/blocks/blocks/google_books.ts | 198 +++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 15 ++ apps/sim/tools/google_books/index.ts | 3 + apps/sim/tools/google_books/types.ts | 64 ++++++ apps/sim/tools/google_books/volume_details.ts | 198 +++++++++++++++++ apps/sim/tools/google_books/volume_search.ts | 202 ++++++++++++++++++ apps/sim/tools/registry.ts | 3 + 12 files changed, 799 insertions(+) create mode 100644 apps/docs/content/docs/en/tools/google_books.mdx create mode 100644 apps/sim/blocks/blocks/google_books.ts create mode 100644 apps/sim/tools/google_books/index.ts create mode 100644 apps/sim/tools/google_books/types.ts create mode 100644 apps/sim/tools/google_books/volume_details.ts create mode 100644 apps/sim/tools/google_books/volume_search.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index f13fc8aa8..dfb95dab2 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -1157,6 +1157,21 @@ export function AirweaveIcon(props: SVGProps) { ) } +export function GoogleBooksIcon(props: SVGProps) { + return ( + + + + + ) +} + export function GoogleDocsIcon(props: SVGProps) { return ( = { github_v2: GithubIcon, gitlab: GitLabIcon, gmail_v2: GmailIcon, + google_books: GoogleBooksIcon, google_calendar_v2: GoogleCalendarIcon, google_docs: GoogleDocsIcon, google_drive: GoogleDriveIcon, diff --git a/apps/docs/content/docs/en/tools/google_books.mdx b/apps/docs/content/docs/en/tools/google_books.mdx new file mode 100644 index 000000000..9baec6846 --- /dev/null +++ b/apps/docs/content/docs/en/tools/google_books.mdx @@ -0,0 +1,96 @@ +--- +title: Google Books +description: Search and retrieve book information +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Search for books using the Google Books API. Find volumes by title, author, ISBN, or keywords, and retrieve detailed information about specific books including descriptions, ratings, and publication details. + + + +## Tools + +### `google_books_volume_search` + +Search for books using the Google Books API + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Google Books API key | +| `query` | string | Yes | Search query. Supports special keywords: intitle:, inauthor:, inpublisher:, subject:, isbn: | +| `filter` | string | No | Filter results by availability \(partial, full, free-ebooks, paid-ebooks, ebooks\) | +| `printType` | string | No | Restrict to print type \(all, books, magazines\) | +| `orderBy` | string | No | Sort order \(relevance, newest\) | +| `startIndex` | number | No | Index of the first result to return \(for pagination\) | +| `maxResults` | number | No | Maximum number of results to return \(1-40\) | +| `langRestrict` | string | No | Restrict results to a specific language \(ISO 639-1 code\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalItems` | number | Total number of matching results | +| `volumes` | array | List of matching volumes | +| ↳ `id` | string | Volume ID | +| ↳ `title` | string | Book title | +| ↳ `subtitle` | string | Book subtitle | +| ↳ `authors` | array | List of authors | +| ↳ `publisher` | string | Publisher name | +| ↳ `publishedDate` | string | Publication date | +| ↳ `description` | string | Book description | +| ↳ `pageCount` | number | Number of pages | +| ↳ `categories` | array | Book categories | +| ↳ `averageRating` | number | Average rating \(1-5\) | +| ↳ `ratingsCount` | number | Number of ratings | +| ↳ `language` | string | Language code | +| ↳ `previewLink` | string | Link to preview on Google Books | +| ↳ `infoLink` | string | Link to info page | +| ↳ `thumbnailUrl` | string | Book cover thumbnail URL | +| ↳ `isbn10` | string | ISBN-10 identifier | +| ↳ `isbn13` | string | ISBN-13 identifier | + +### `google_books_volume_details` + +Get detailed information about a specific book volume + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Google Books API key | +| `volumeId` | string | Yes | The ID of the volume to retrieve | +| `projection` | string | No | Projection level \(full, lite\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Volume ID | +| `title` | string | Book title | +| `subtitle` | string | Book subtitle | +| `authors` | array | List of authors | +| `publisher` | string | Publisher name | +| `publishedDate` | string | Publication date | +| `description` | string | Book description | +| `pageCount` | number | Number of pages | +| `categories` | array | Book categories | +| `averageRating` | number | Average rating \(1-5\) | +| `ratingsCount` | number | Number of ratings | +| `language` | string | Language code | +| `previewLink` | string | Link to preview on Google Books | +| `infoLink` | string | Link to info page | +| `thumbnailUrl` | string | Book cover thumbnail URL | +| `isbn10` | string | ISBN-10 identifier | +| `isbn13` | string | ISBN-13 identifier | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index f9bd3ca1f..c10640a96 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -33,6 +33,7 @@ "github", "gitlab", "gmail", + "google_books", "google_calendar", "google_docs", "google_drive", diff --git a/apps/sim/blocks/blocks/google_books.ts b/apps/sim/blocks/blocks/google_books.ts new file mode 100644 index 000000000..5b7e31aae --- /dev/null +++ b/apps/sim/blocks/blocks/google_books.ts @@ -0,0 +1,198 @@ +import { GoogleBooksIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' + +export const GoogleBooksBlock: BlockConfig = { + type: 'google_books', + name: 'Google Books', + description: 'Search and retrieve book information', + longDescription: + 'Search for books using the Google Books API. Find volumes by title, author, ISBN, or keywords, and retrieve detailed information about specific books including descriptions, ratings, and publication details.', + docsLink: 'https://docs.sim.ai/tools/google_books', + category: 'tools', + bgColor: '#E0E0E0', + icon: GoogleBooksIcon, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Search Volumes', id: 'volume_search' }, + { label: 'Get Volume Details', id: 'volume_details' }, + ], + value: () => 'volume_search', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + password: true, + placeholder: 'Enter your Google Books API key', + required: true, + }, + { + id: 'query', + title: 'Search Query', + type: 'short-input', + placeholder: 'e.g., intitle:harry potter inauthor:rowling', + condition: { field: 'operation', value: 'volume_search' }, + required: { field: 'operation', value: 'volume_search' }, + }, + { + id: 'filter', + title: 'Filter', + type: 'dropdown', + options: [ + { label: 'None', id: '' }, + { label: 'Partial Preview', id: 'partial' }, + { label: 'Full Preview', id: 'full' }, + { label: 'Free eBooks', id: 'free-ebooks' }, + { label: 'Paid eBooks', id: 'paid-ebooks' }, + { label: 'All eBooks', id: 'ebooks' }, + ], + condition: { field: 'operation', value: 'volume_search' }, + mode: 'advanced', + }, + { + id: 'printType', + title: 'Print Type', + type: 'dropdown', + options: [ + { label: 'All', id: 'all' }, + { label: 'Books', id: 'books' }, + { label: 'Magazines', id: 'magazines' }, + ], + value: () => 'all', + condition: { field: 'operation', value: 'volume_search' }, + mode: 'advanced', + }, + { + id: 'orderBy', + title: 'Order By', + type: 'dropdown', + options: [ + { label: 'Relevance', id: 'relevance' }, + { label: 'Newest', id: 'newest' }, + ], + value: () => 'relevance', + condition: { field: 'operation', value: 'volume_search' }, + mode: 'advanced', + }, + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + placeholder: 'Number of results (1-40)', + condition: { field: 'operation', value: 'volume_search' }, + mode: 'advanced', + }, + { + id: 'startIndex', + title: 'Start Index', + type: 'short-input', + placeholder: 'Starting index for pagination', + condition: { field: 'operation', value: 'volume_search' }, + mode: 'advanced', + }, + { + id: 'langRestrict', + title: 'Language', + type: 'short-input', + placeholder: 'ISO 639-1 code (e.g., en, es, fr)', + condition: { field: 'operation', value: 'volume_search' }, + mode: 'advanced', + }, + { + id: 'volumeId', + title: 'Volume ID', + type: 'short-input', + placeholder: 'Google Books volume ID', + condition: { field: 'operation', value: 'volume_details' }, + required: { field: 'operation', value: 'volume_details' }, + }, + { + id: 'projection', + title: 'Projection', + type: 'dropdown', + options: [ + { label: 'Full', id: 'full' }, + { label: 'Lite', id: 'lite' }, + ], + value: () => 'full', + mode: 'advanced', + }, + ], + + tools: { + access: ['google_books_volume_search', 'google_books_volume_details'], + config: { + tool: (params) => `google_books_${params.operation}`, + params: (params) => { + const { operation, ...rest } = params + + let maxResults: number | undefined + if (params.maxResults) { + maxResults = Number.parseInt(params.maxResults, 10) + if (Number.isNaN(maxResults)) { + maxResults = undefined + } + } + + let startIndex: number | undefined + if (params.startIndex) { + startIndex = Number.parseInt(params.startIndex, 10) + if (Number.isNaN(startIndex)) { + startIndex = undefined + } + } + + return { + ...rest, + maxResults, + startIndex, + filter: params.filter || undefined, + printType: params.printType || undefined, + orderBy: params.orderBy || undefined, + projection: params.projection || undefined, + } + }, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Google Books API key' }, + query: { type: 'string', description: 'Search query' }, + filter: { type: 'string', description: 'Filter by availability' }, + printType: { type: 'string', description: 'Print type filter' }, + orderBy: { type: 'string', description: 'Sort order' }, + maxResults: { type: 'string', description: 'Maximum number of results' }, + startIndex: { type: 'string', description: 'Starting index for pagination' }, + langRestrict: { type: 'string', description: 'Language restriction' }, + volumeId: { type: 'string', description: 'Volume ID for details' }, + projection: { type: 'string', description: 'Projection level' }, + }, + + outputs: { + totalItems: { type: 'number', description: 'Total number of matching results' }, + volumes: { type: 'json', description: 'List of matching volumes' }, + id: { type: 'string', description: 'Volume ID' }, + title: { type: 'string', description: 'Book title' }, + subtitle: { type: 'string', description: 'Book subtitle' }, + authors: { type: 'json', description: 'List of authors' }, + publisher: { type: 'string', description: 'Publisher name' }, + publishedDate: { type: 'string', description: 'Publication date' }, + description: { type: 'string', description: 'Book description' }, + pageCount: { type: 'number', description: 'Number of pages' }, + categories: { type: 'json', description: 'Book categories' }, + averageRating: { type: 'number', description: 'Average rating (1-5)' }, + ratingsCount: { type: 'number', description: 'Number of ratings' }, + language: { type: 'string', description: 'Language code' }, + previewLink: { type: 'string', description: 'Link to preview on Google Books' }, + infoLink: { type: 'string', description: 'Link to info page' }, + thumbnailUrl: { type: 'string', description: 'Book cover thumbnail URL' }, + isbn10: { type: 'string', description: 'ISBN-10 identifier' }, + isbn13: { type: 'string', description: 'ISBN-13 identifier' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 301b7b350..f51019da2 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -39,6 +39,7 @@ import { GitHubBlock, GitHubV2Block } from '@/blocks/blocks/github' import { GitLabBlock } from '@/blocks/blocks/gitlab' import { GmailBlock, GmailV2Block } from '@/blocks/blocks/gmail' import { GoogleSearchBlock } from '@/blocks/blocks/google' +import { GoogleBooksBlock } from '@/blocks/blocks/google_books' import { GoogleCalendarBlock, GoogleCalendarV2Block } from '@/blocks/blocks/google_calendar' import { GoogleDocsBlock } from '@/blocks/blocks/google_docs' import { GoogleDriveBlock } from '@/blocks/blocks/google_drive' @@ -214,6 +215,7 @@ export const registry: Record = { gmail_v2: GmailV2Block, google_calendar: GoogleCalendarBlock, google_calendar_v2: GoogleCalendarV2Block, + google_books: GoogleBooksBlock, google_docs: GoogleDocsBlock, google_drive: GoogleDriveBlock, google_forms: GoogleFormsBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index f13fc8aa8..dfb95dab2 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1157,6 +1157,21 @@ export function AirweaveIcon(props: SVGProps) { ) } +export function GoogleBooksIcon(props: SVGProps) { + return ( + + + + + ) +} + export function GoogleDocsIcon(props: SVGProps) { return ( + } +} + +export const googleBooksVolumeDetailsTool: ToolConfig< + GoogleBooksVolumeDetailsParams, + GoogleBooksVolumeDetailsResponse +> = { + id: 'google_books_volume_details', + name: 'Google Books Volume Details', + description: 'Get detailed information about a specific book volume', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Google Books API key', + }, + volumeId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the volume to retrieve', + }, + projection: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Projection level (full, lite)', + }, + }, + + request: { + url: (params) => { + const url = new URL(`https://www.googleapis.com/books/v1/volumes/${params.volumeId.trim()}`) + url.searchParams.set('key', params.apiKey.trim()) + + if (params.projection) { + url.searchParams.set('projection', params.projection) + } + + return url.toString() + }, + method: 'GET', + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data: GoogleBooksVolumeResponse = await response.json() + + if (!data.volumeInfo) { + throw new Error('Volume not found') + } + + const info = data.volumeInfo + const identifiers = info.industryIdentifiers ?? [] + + return { + success: true, + output: { + id: data.id, + title: info.title ?? '', + subtitle: info.subtitle ?? null, + authors: info.authors ?? [], + publisher: info.publisher ?? null, + publishedDate: info.publishedDate ?? null, + description: info.description ?? null, + pageCount: info.pageCount ?? null, + categories: info.categories ?? [], + averageRating: info.averageRating ?? null, + ratingsCount: info.ratingsCount ?? null, + language: info.language ?? null, + previewLink: info.previewLink ?? null, + infoLink: info.infoLink ?? null, + thumbnailUrl: info.imageLinks?.thumbnail ?? info.imageLinks?.smallThumbnail ?? null, + isbn10: identifiers.find((id) => id.type === 'ISBN_10')?.identifier ?? null, + isbn13: identifiers.find((id) => id.type === 'ISBN_13')?.identifier ?? null, + }, + } + }, + + outputs: { + id: { + type: 'string', + description: 'Volume ID', + }, + title: { + type: 'string', + description: 'Book title', + }, + subtitle: { + type: 'string', + description: 'Book subtitle', + optional: true, + }, + authors: { + type: 'array', + description: 'List of authors', + }, + publisher: { + type: 'string', + description: 'Publisher name', + optional: true, + }, + publishedDate: { + type: 'string', + description: 'Publication date', + optional: true, + }, + description: { + type: 'string', + description: 'Book description', + optional: true, + }, + pageCount: { + type: 'number', + description: 'Number of pages', + optional: true, + }, + categories: { + type: 'array', + description: 'Book categories', + }, + averageRating: { + type: 'number', + description: 'Average rating (1-5)', + optional: true, + }, + ratingsCount: { + type: 'number', + description: 'Number of ratings', + optional: true, + }, + language: { + type: 'string', + description: 'Language code', + optional: true, + }, + previewLink: { + type: 'string', + description: 'Link to preview on Google Books', + optional: true, + }, + infoLink: { + type: 'string', + description: 'Link to info page', + optional: true, + }, + thumbnailUrl: { + type: 'string', + description: 'Book cover thumbnail URL', + optional: true, + }, + isbn10: { + type: 'string', + description: 'ISBN-10 identifier', + optional: true, + }, + isbn13: { + type: 'string', + description: 'ISBN-13 identifier', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/google_books/volume_search.ts b/apps/sim/tools/google_books/volume_search.ts new file mode 100644 index 000000000..1f643c599 --- /dev/null +++ b/apps/sim/tools/google_books/volume_search.ts @@ -0,0 +1,202 @@ +import type { + GoogleBooksVolumeSearchParams, + GoogleBooksVolumeSearchResponse, + VolumeInfo, +} from '@/tools/google_books/types' +import type { ToolConfig } from '@/tools/types' + +interface GoogleBooksVolumeItem { + id: string + volumeInfo: { + title?: string + subtitle?: string + authors?: string[] + publisher?: string + publishedDate?: string + description?: string + pageCount?: number + categories?: string[] + averageRating?: number + ratingsCount?: number + language?: string + previewLink?: string + infoLink?: string + imageLinks?: { + thumbnail?: string + smallThumbnail?: string + } + industryIdentifiers?: Array<{ + type: string + identifier: string + }> + } +} + +function extractVolumeInfo(item: GoogleBooksVolumeItem): VolumeInfo { + const info = item.volumeInfo + const identifiers = info.industryIdentifiers ?? [] + + return { + id: item.id, + title: info.title ?? '', + subtitle: info.subtitle ?? null, + authors: info.authors ?? [], + publisher: info.publisher ?? null, + publishedDate: info.publishedDate ?? null, + description: info.description ?? null, + pageCount: info.pageCount ?? null, + categories: info.categories ?? [], + averageRating: info.averageRating ?? null, + ratingsCount: info.ratingsCount ?? null, + language: info.language ?? null, + previewLink: info.previewLink ?? null, + infoLink: info.infoLink ?? null, + thumbnailUrl: info.imageLinks?.thumbnail ?? info.imageLinks?.smallThumbnail ?? null, + isbn10: identifiers.find((id) => id.type === 'ISBN_10')?.identifier ?? null, + isbn13: identifiers.find((id) => id.type === 'ISBN_13')?.identifier ?? null, + } +} + +export const googleBooksVolumeSearchTool: ToolConfig< + GoogleBooksVolumeSearchParams, + GoogleBooksVolumeSearchResponse +> = { + id: 'google_books_volume_search', + name: 'Google Books Volume Search', + description: 'Search for books using the Google Books API', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Google Books API key', + }, + query: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Search query. Supports special keywords: intitle:, inauthor:, inpublisher:, subject:, isbn:', + }, + filter: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Filter results by availability (partial, full, free-ebooks, paid-ebooks, ebooks)', + }, + printType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Restrict to print type (all, books, magazines)', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort order (relevance, newest)', + }, + startIndex: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Index of the first result to return (for pagination)', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results to return (1-40)', + }, + langRestrict: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Restrict results to a specific language (ISO 639-1 code)', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://www.googleapis.com/books/v1/volumes') + url.searchParams.set('q', params.query.trim()) + url.searchParams.set('key', params.apiKey.trim()) + + if (params.filter) { + url.searchParams.set('filter', params.filter) + } + if (params.printType) { + url.searchParams.set('printType', params.printType) + } + if (params.orderBy) { + url.searchParams.set('orderBy', params.orderBy) + } + if (params.startIndex !== undefined) { + url.searchParams.set('startIndex', String(params.startIndex)) + } + if (params.maxResults !== undefined) { + url.searchParams.set('maxResults', String(params.maxResults)) + } + if (params.langRestrict) { + url.searchParams.set('langRestrict', params.langRestrict) + } + + return url.toString() + }, + method: 'GET', + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + const items: GoogleBooksVolumeItem[] = data.items ?? [] + const volumes = items.map(extractVolumeInfo) + + return { + success: true, + output: { + totalItems: data.totalItems ?? 0, + volumes, + }, + } + }, + + outputs: { + totalItems: { + type: 'number', + description: 'Total number of matching results', + }, + volumes: { + type: 'array', + description: 'List of matching volumes', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Volume ID' }, + title: { type: 'string', description: 'Book title' }, + subtitle: { type: 'string', description: 'Book subtitle' }, + authors: { type: 'array', description: 'List of authors' }, + publisher: { type: 'string', description: 'Publisher name' }, + publishedDate: { type: 'string', description: 'Publication date' }, + description: { type: 'string', description: 'Book description' }, + pageCount: { type: 'number', description: 'Number of pages' }, + categories: { type: 'array', description: 'Book categories' }, + averageRating: { type: 'number', description: 'Average rating (1-5)' }, + ratingsCount: { type: 'number', description: 'Number of ratings' }, + language: { type: 'string', description: 'Language code' }, + previewLink: { type: 'string', description: 'Link to preview on Google Books' }, + infoLink: { type: 'string', description: 'Link to info page' }, + thumbnailUrl: { type: 'string', description: 'Book cover thumbnail URL' }, + isbn10: { type: 'string', description: 'ISBN-10 identifier' }, + isbn13: { type: 'string', description: 'ISBN-13 identifier' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 52506d744..991c7e140 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -526,6 +526,7 @@ import { gmailUnarchiveV2Tool, } from '@/tools/gmail' import { googleSearchTool } from '@/tools/google' +import { googleBooksVolumeDetailsTool, googleBooksVolumeSearchTool } from '@/tools/google_books' import { googleCalendarCreateTool, googleCalendarCreateV2Tool, @@ -2556,6 +2557,8 @@ export const tools: Record = { google_docs_read: googleDocsReadTool, google_docs_write: googleDocsWriteTool, google_docs_create: googleDocsCreateTool, + google_books_volume_search: googleBooksVolumeSearchTool, + google_books_volume_details: googleBooksVolumeDetailsTool, google_maps_air_quality: googleMapsAirQualityTool, google_maps_directions: googleMapsDirectionsTool, google_maps_distance_matrix: googleMapsDistanceMatrixTool, From fc97ce007db4f4c0b5dc2c08cafe4ec427c4729d Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 12 Feb 2026 15:26:13 -0800 Subject: [PATCH 2/4] Correct error handling, specify auth mode as api key --- apps/sim/blocks/blocks/google_books.ts | 3 +++ apps/sim/tools/google_books/volume_details.ts | 7 +++++++ apps/sim/tools/google_books/volume_search.ts | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/apps/sim/blocks/blocks/google_books.ts b/apps/sim/blocks/blocks/google_books.ts index 5b7e31aae..764ee0290 100644 --- a/apps/sim/blocks/blocks/google_books.ts +++ b/apps/sim/blocks/blocks/google_books.ts @@ -1,10 +1,12 @@ import { GoogleBooksIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' export const GoogleBooksBlock: BlockConfig = { type: 'google_books', name: 'Google Books', description: 'Search and retrieve book information', + authMode: AuthMode.ApiKey, longDescription: 'Search for books using the Google Books API. Find volumes by title, author, ISBN, or keywords, and retrieve detailed information about specific books including descriptions, ratings, and publication details.', docsLink: 'https://docs.sim.ai/tools/google_books', @@ -120,6 +122,7 @@ export const GoogleBooksBlock: BlockConfig = { { label: 'Lite', id: 'lite' }, ], value: () => 'full', + condition: { field: 'operation', value: 'volume_details' }, mode: 'advanced', }, ], diff --git a/apps/sim/tools/google_books/volume_details.ts b/apps/sim/tools/google_books/volume_details.ts index f50e23986..114a78361 100644 --- a/apps/sim/tools/google_books/volume_details.ts +++ b/apps/sim/tools/google_books/volume_details.ts @@ -29,6 +29,9 @@ interface GoogleBooksVolumeResponse { identifier: string }> } + error?: { + message?: string + } } export const googleBooksVolumeDetailsTool: ToolConfig< @@ -81,6 +84,10 @@ export const googleBooksVolumeDetailsTool: ToolConfig< transformResponse: async (response: Response) => { const data: GoogleBooksVolumeResponse = await response.json() + if (data.error) { + throw new Error(`Google Books API error: ${data.error.message || 'Unknown error'}`) + } + if (!data.volumeInfo) { throw new Error('Volume not found') } diff --git a/apps/sim/tools/google_books/volume_search.ts b/apps/sim/tools/google_books/volume_search.ts index 1f643c599..72ca1a783 100644 --- a/apps/sim/tools/google_books/volume_search.ts +++ b/apps/sim/tools/google_books/volume_search.ts @@ -155,6 +155,10 @@ export const googleBooksVolumeSearchTool: ToolConfig< transformResponse: async (response: Response) => { const data = await response.json() + if (data.error) { + throw new Error(`Google Books API error: ${data.error.message || 'Unknown error'}`) + } + const items: GoogleBooksVolumeItem[] = data.items ?? [] const volumes = items.map(extractVolumeInfo) From 1130f8ddb216b64121157c17996046c74deca07c Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 12 Feb 2026 15:31:12 -0800 Subject: [PATCH 3/4] Remove redundant error handling, move volume item to types file --- apps/sim/tools/google_books/types.ts | 30 +++++++++++++++++ apps/sim/tools/google_books/volume_details.ts | 7 ---- apps/sim/tools/google_books/volume_search.ts | 32 +------------------ 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/apps/sim/tools/google_books/types.ts b/apps/sim/tools/google_books/types.ts index f56966095..3484d576a 100644 --- a/apps/sim/tools/google_books/types.ts +++ b/apps/sim/tools/google_books/types.ts @@ -1,5 +1,35 @@ import type { ToolResponse } from '@/tools/types' +/** + * Raw volume item from Google Books API response + */ +export interface GoogleBooksVolumeItem { + id: string + volumeInfo: { + title?: string + subtitle?: string + authors?: string[] + publisher?: string + publishedDate?: string + description?: string + pageCount?: number + categories?: string[] + averageRating?: number + ratingsCount?: number + language?: string + previewLink?: string + infoLink?: string + imageLinks?: { + thumbnail?: string + smallThumbnail?: string + } + industryIdentifiers?: Array<{ + type: string + identifier: string + }> + } +} + /** * Volume information structure shared between search and details responses */ diff --git a/apps/sim/tools/google_books/volume_details.ts b/apps/sim/tools/google_books/volume_details.ts index 114a78361..f50e23986 100644 --- a/apps/sim/tools/google_books/volume_details.ts +++ b/apps/sim/tools/google_books/volume_details.ts @@ -29,9 +29,6 @@ interface GoogleBooksVolumeResponse { identifier: string }> } - error?: { - message?: string - } } export const googleBooksVolumeDetailsTool: ToolConfig< @@ -84,10 +81,6 @@ export const googleBooksVolumeDetailsTool: ToolConfig< transformResponse: async (response: Response) => { const data: GoogleBooksVolumeResponse = await response.json() - if (data.error) { - throw new Error(`Google Books API error: ${data.error.message || 'Unknown error'}`) - } - if (!data.volumeInfo) { throw new Error('Volume not found') } diff --git a/apps/sim/tools/google_books/volume_search.ts b/apps/sim/tools/google_books/volume_search.ts index 72ca1a783..55d90cf9a 100644 --- a/apps/sim/tools/google_books/volume_search.ts +++ b/apps/sim/tools/google_books/volume_search.ts @@ -1,37 +1,11 @@ import type { + GoogleBooksVolumeItem, GoogleBooksVolumeSearchParams, GoogleBooksVolumeSearchResponse, VolumeInfo, } from '@/tools/google_books/types' import type { ToolConfig } from '@/tools/types' -interface GoogleBooksVolumeItem { - id: string - volumeInfo: { - title?: string - subtitle?: string - authors?: string[] - publisher?: string - publishedDate?: string - description?: string - pageCount?: number - categories?: string[] - averageRating?: number - ratingsCount?: number - language?: string - previewLink?: string - infoLink?: string - imageLinks?: { - thumbnail?: string - smallThumbnail?: string - } - industryIdentifiers?: Array<{ - type: string - identifier: string - }> - } -} - function extractVolumeInfo(item: GoogleBooksVolumeItem): VolumeInfo { const info = item.volumeInfo const identifiers = info.industryIdentifiers ?? [] @@ -155,10 +129,6 @@ export const googleBooksVolumeSearchTool: ToolConfig< transformResponse: async (response: Response) => { const data = await response.json() - if (data.error) { - throw new Error(`Google Books API error: ${data.error.message || 'Unknown error'}`) - } - const items: GoogleBooksVolumeItem[] = data.items ?? [] const volumes = items.map(extractVolumeInfo) From dce47a101cb20dae4da14a949f5a2d5a5a478f9d Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Thu, 12 Feb 2026 15:45:00 -0800 Subject: [PATCH 4/4] Migrate last response to types --- apps/sim/tools/google_books/types.ts | 32 ++++++++++++++++++- apps/sim/tools/google_books/volume_details.ts | 28 +--------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/apps/sim/tools/google_books/types.ts b/apps/sim/tools/google_books/types.ts index 3484d576a..363038de5 100644 --- a/apps/sim/tools/google_books/types.ts +++ b/apps/sim/tools/google_books/types.ts @@ -1,7 +1,7 @@ import type { ToolResponse } from '@/tools/types' /** - * Raw volume item from Google Books API response + * Raw volume item from Google Books API search response */ export interface GoogleBooksVolumeItem { id: string @@ -30,6 +30,36 @@ export interface GoogleBooksVolumeItem { } } +/** + * Raw volume response from Google Books API details endpoint + */ +export interface GoogleBooksVolumeResponse { + id: string + volumeInfo: { + title?: string + subtitle?: string + authors?: string[] + publisher?: string + publishedDate?: string + description?: string + pageCount?: number + categories?: string[] + averageRating?: number + ratingsCount?: number + language?: string + previewLink?: string + infoLink?: string + imageLinks?: { + thumbnail?: string + smallThumbnail?: string + } + industryIdentifiers?: Array<{ + type: string + identifier: string + }> + } +} + /** * Volume information structure shared between search and details responses */ diff --git a/apps/sim/tools/google_books/volume_details.ts b/apps/sim/tools/google_books/volume_details.ts index f50e23986..23fe7cd4a 100644 --- a/apps/sim/tools/google_books/volume_details.ts +++ b/apps/sim/tools/google_books/volume_details.ts @@ -1,36 +1,10 @@ import type { GoogleBooksVolumeDetailsParams, GoogleBooksVolumeDetailsResponse, + GoogleBooksVolumeResponse, } from '@/tools/google_books/types' import type { ToolConfig } from '@/tools/types' -interface GoogleBooksVolumeResponse { - id: string - volumeInfo: { - title?: string - subtitle?: string - authors?: string[] - publisher?: string - publishedDate?: string - description?: string - pageCount?: number - categories?: string[] - averageRating?: number - ratingsCount?: number - language?: string - previewLink?: string - infoLink?: string - imageLinks?: { - thumbnail?: string - smallThumbnail?: string - } - industryIdentifiers?: Array<{ - type: string - identifier: string - }> - } -} - export const googleBooksVolumeDetailsTool: ToolConfig< GoogleBooksVolumeDetailsParams, GoogleBooksVolumeDetailsResponse