mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-13 07:55:09 -05:00
feat(google books): Add google books integration
This commit is contained in:
@@ -1157,6 +1157,21 @@ export function AirweaveIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleBooksIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 478.633 540.068'>
|
||||
<path
|
||||
fill='#1C51A4'
|
||||
d='M449.059,218.231L245.519,99.538l-0.061,193.23c0.031,1.504-0.368,2.977-1.166,4.204c-0.798,1.258-1.565,1.995-2.915,2.547c-1.35,0.552-2.792,0.706-4.204,0.399c-1.412-0.307-2.7-1.043-3.713-2.117l-69.166-70.609l-69.381,70.179c-1.013,0.982-2.301,1.657-3.652,1.903c-1.381,0.246-2.792,0.092-4.081-0.491c-1.289-0.583-1.626-0.522-2.394-1.749c-0.767-1.197-1.197-2.608-1.197-4.081L85.031,6.007l-2.915-1.289C43.973-11.638,0,16.409,0,59.891v420.306c0,46.029,49.312,74.782,88.775,51.767l360.285-210.138C488.491,298.782,488.491,241.246,449.059,218.231z'
|
||||
/>
|
||||
<path
|
||||
fill='#80D7FB'
|
||||
d='M88.805,8.124c-2.179-1.289-4.419-2.363-6.659-3.345l0.123,288.663c0,1.442,0.43,2.854,1.197,4.081c0.767,1.197,1.872,2.148,3.161,2.731c1.289,0.583,2.7,0.736,4.081,0.491c1.381-0.246,2.639-0.921,3.652-1.903l69.749-69.688l69.811,69.749c1.013,1.074,2.301,1.81,3.713,2.117c1.412,0.307,2.884,0.153,4.204-0.399c1.319-0.552,2.455-1.565,3.253-2.792c0.798-1.258,1.197-2.731,1.166-4.204V99.998L88.805,8.124z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleDocsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
GithubIcon,
|
||||
GitLabIcon,
|
||||
GmailIcon,
|
||||
GoogleBooksIcon,
|
||||
GoogleCalendarIcon,
|
||||
GoogleDocsIcon,
|
||||
GoogleDriveIcon,
|
||||
@@ -172,6 +173,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
github_v2: GithubIcon,
|
||||
gitlab: GitLabIcon,
|
||||
gmail_v2: GmailIcon,
|
||||
google_books: GoogleBooksIcon,
|
||||
google_calendar_v2: GoogleCalendarIcon,
|
||||
google_docs: GoogleDocsIcon,
|
||||
google_drive: GoogleDriveIcon,
|
||||
|
||||
96
apps/docs/content/docs/en/tools/google_books.mdx
Normal file
96
apps/docs/content/docs/en/tools/google_books.mdx
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Google Books
|
||||
description: Search and retrieve book information
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="google_books"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
## 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 |
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"gmail",
|
||||
"google_books",
|
||||
"google_calendar",
|
||||
"google_docs",
|
||||
"google_drive",
|
||||
|
||||
198
apps/sim/blocks/blocks/google_books.ts
Normal file
198
apps/sim/blocks/blocks/google_books.ts
Normal file
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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<string, BlockConfig> = {
|
||||
gmail_v2: GmailV2Block,
|
||||
google_calendar: GoogleCalendarBlock,
|
||||
google_calendar_v2: GoogleCalendarV2Block,
|
||||
google_books: GoogleBooksBlock,
|
||||
google_docs: GoogleDocsBlock,
|
||||
google_drive: GoogleDriveBlock,
|
||||
google_forms: GoogleFormsBlock,
|
||||
|
||||
@@ -1157,6 +1157,21 @@ export function AirweaveIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleBooksIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 478.633 540.068'>
|
||||
<path
|
||||
fill='#1C51A4'
|
||||
d='M449.059,218.231L245.519,99.538l-0.061,193.23c0.031,1.504-0.368,2.977-1.166,4.204c-0.798,1.258-1.565,1.995-2.915,2.547c-1.35,0.552-2.792,0.706-4.204,0.399c-1.412-0.307-2.7-1.043-3.713-2.117l-69.166-70.609l-69.381,70.179c-1.013,0.982-2.301,1.657-3.652,1.903c-1.381,0.246-2.792,0.092-4.081-0.491c-1.289-0.583-1.626-0.522-2.394-1.749c-0.767-1.197-1.197-2.608-1.197-4.081L85.031,6.007l-2.915-1.289C43.973-11.638,0,16.409,0,59.891v420.306c0,46.029,49.312,74.782,88.775,51.767l360.285-210.138C488.491,298.782,488.491,241.246,449.059,218.231z'
|
||||
/>
|
||||
<path
|
||||
fill='#80D7FB'
|
||||
d='M88.805,8.124c-2.179-1.289-4.419-2.363-6.659-3.345l0.123,288.663c0,1.442,0.43,2.854,1.197,4.081c0.767,1.197,1.872,2.148,3.161,2.731c1.289,0.583,2.7,0.736,4.081,0.491c1.381-0.246,2.639-0.921,3.652-1.903l69.749-69.688l69.811,69.749c1.013,1.074,2.301,1.81,3.713,2.117c1.412,0.307,2.884,0.153,4.204-0.399c1.319-0.552,2.455-1.565,3.253-2.792c0.798-1.258,1.197-2.731,1.166-4.204V99.998L88.805,8.124z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleDocsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
3
apps/sim/tools/google_books/index.ts
Normal file
3
apps/sim/tools/google_books/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './types'
|
||||
export { googleBooksVolumeDetailsTool } from './volume_details'
|
||||
export { googleBooksVolumeSearchTool } from './volume_search'
|
||||
64
apps/sim/tools/google_books/types.ts
Normal file
64
apps/sim/tools/google_books/types.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Volume information structure shared between search and details responses
|
||||
*/
|
||||
export interface VolumeInfo {
|
||||
id: string
|
||||
title: string
|
||||
subtitle: string | null
|
||||
authors: string[]
|
||||
publisher: string | null
|
||||
publishedDate: string | null
|
||||
description: string | null
|
||||
pageCount: number | null
|
||||
categories: string[]
|
||||
averageRating: number | null
|
||||
ratingsCount: number | null
|
||||
language: string | null
|
||||
previewLink: string | null
|
||||
infoLink: string | null
|
||||
thumbnailUrl: string | null
|
||||
isbn10: string | null
|
||||
isbn13: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for searching volumes
|
||||
*/
|
||||
export interface GoogleBooksVolumeSearchParams {
|
||||
apiKey: string
|
||||
query: string
|
||||
filter?: 'partial' | 'full' | 'free-ebooks' | 'paid-ebooks' | 'ebooks'
|
||||
printType?: 'all' | 'books' | 'magazines'
|
||||
orderBy?: 'relevance' | 'newest'
|
||||
startIndex?: number
|
||||
maxResults?: number
|
||||
langRestrict?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from volume search
|
||||
*/
|
||||
export interface GoogleBooksVolumeSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
totalItems: number
|
||||
volumes: VolumeInfo[]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting volume details
|
||||
*/
|
||||
export interface GoogleBooksVolumeDetailsParams {
|
||||
apiKey: string
|
||||
volumeId: string
|
||||
projection?: 'full' | 'lite'
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from volume details
|
||||
*/
|
||||
export interface GoogleBooksVolumeDetailsResponse extends ToolResponse {
|
||||
output: VolumeInfo
|
||||
}
|
||||
198
apps/sim/tools/google_books/volume_details.ts
Normal file
198
apps/sim/tools/google_books/volume_details.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import type {
|
||||
GoogleBooksVolumeDetailsParams,
|
||||
GoogleBooksVolumeDetailsResponse,
|
||||
} 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
|
||||
> = {
|
||||
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,
|
||||
},
|
||||
},
|
||||
}
|
||||
202
apps/sim/tools/google_books/volume_search.ts
Normal file
202
apps/sim/tools/google_books/volume_search.ts
Normal file
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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<string, ToolConfig> = {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user