diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 9da5a57508..c178dd9faa 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -526,6 +526,17 @@ export function SlackMonoIcon(props: SVGProps) { ) } +export function GammaIcon(props: SVGProps) { + return ( + + + + ) +} + export function GithubIcon(props: SVGProps) { return ( diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index a1af3619d0..37447dedf1 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -40,6 +40,7 @@ import { EyeIcon, FirecrawlIcon, FirefliesIcon, + GammaIcon, GithubIcon, GitLabIcon, GmailIcon, @@ -190,6 +191,7 @@ export const blockTypeToIconMap: Record = { file_v3: DocumentIcon, firecrawl: FirecrawlIcon, fireflies_v2: FirefliesIcon, + gamma: GammaIcon, github_v2: GithubIcon, gitlab: GitLabIcon, gmail_v2: GmailIcon, diff --git a/apps/docs/content/docs/en/tools/gamma.mdx b/apps/docs/content/docs/en/tools/gamma.mdx new file mode 100644 index 0000000000..f62b2232a5 --- /dev/null +++ b/apps/docs/content/docs/en/tools/gamma.mdx @@ -0,0 +1,165 @@ +--- +title: Gamma +description: Generate presentations, documents, and webpages with AI +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Gamma](https://gamma.app/) is an AI-powered platform for creating presentations, documents, webpages, and social posts. Gamma's API lets you programmatically generate polished, visually rich content from text prompts, adapt existing templates, and manage workspace assets like themes and folders. + +With Gamma, you can: + +- **Generate presentations and documents:** Create slide decks, documents, webpages, and social posts from text input with full control over format, tone, and image sourcing. +- **Create from templates:** Adapt existing Gamma templates with custom prompts to quickly produce tailored content. +- **Check generation status:** Poll for completion of async generation jobs and retrieve the final Gamma URL. +- **Browse themes and folders:** List available workspace themes and folders to organize and style your generated content. + +In Sim, the Gamma integration enables your agents to automatically generate presentations and documents, create content from templates, and manage workspace assets directly within your workflows. This allows you to automate content creation pipelines, batch-produce slide decks, and integrate AI-generated presentations into broader business automation scenarios. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate Gamma into the workflow. Can generate presentations, documents, webpages, and social posts from text, create from templates, check generation status, and browse themes and folders. + + + +## Tools + +### `gamma_generate` + +Generate a new Gamma presentation, document, webpage, or social post from text input. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Gamma API key | +| `inputText` | string | Yes | Text and image URLs used to generate your gamma \(1-100,000 tokens\) | +| `textMode` | string | Yes | How to handle input text: generate \(AI expands\), condense \(AI summarizes\), or preserve \(keep as-is\) | +| `format` | string | No | Output format: presentation, document, webpage, or social \(default: presentation\) | +| `themeId` | string | No | Custom Gamma workspace theme ID \(use List Themes to find available themes\) | +| `numCards` | number | No | Number of cards/slides to generate \(1-60 for Pro, 1-75 for Ultra; default: 10\) | +| `cardSplit` | string | No | How to split content into cards: auto or inputTextBreaks \(default: auto\) | +| `cardDimensions` | string | No | Card aspect ratio. Presentation: fluid, 16x9, 4x3. Document: fluid, pageless, letter, a4. Social: 1x1, 4x5, 9x16 | +| `additionalInstructions` | string | No | Additional instructions for the AI generation \(max 2000 chars\) | +| `exportAs` | string | No | Automatically export the generated gamma as pdf or pptx | +| `folderIds` | string | No | Comma-separated folder IDs to store the generated gamma in | +| `textAmount` | string | No | Amount of text per card: brief, medium, detailed, or extensive | +| `textTone` | string | No | Tone of the generated text, e.g. "professional", "casual" \(max 500 chars\) | +| `textAudience` | string | No | Target audience for the generated text, e.g. "executives", "students" \(max 500 chars\) | +| `textLanguage` | string | No | Language code for the generated text \(default: en\) | +| `imageSource` | string | No | Where to source images: aiGenerated, pictographic, unsplash, webAllImages, webFreeToUse, webFreeToUseCommercially, giphy, placeholder, or noImages | +| `imageModel` | string | No | AI image generation model to use when imageSource is aiGenerated | +| `imageStyle` | string | No | Style directive for AI-generated images, e.g. "watercolor", "photorealistic" \(max 500 chars\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `generationId` | string | The ID of the generation job. Use with Check Status to poll for completion. | + +### `gamma_generate_from_template` + +Generate a new Gamma by adapting an existing template with a prompt. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Gamma API key | +| `gammaId` | string | Yes | The ID of the template gamma to adapt | +| `prompt` | string | Yes | Instructions for how to adapt the template \(1-100,000 tokens\) | +| `themeId` | string | No | Custom Gamma workspace theme ID to apply | +| `exportAs` | string | No | Automatically export the generated gamma as pdf or pptx | +| `folderIds` | string | No | Comma-separated folder IDs to store the generated gamma in | +| `imageModel` | string | No | AI image generation model to use when imageSource is aiGenerated | +| `imageStyle` | string | No | Style directive for AI-generated images, e.g. "watercolor", "photorealistic" \(max 500 chars\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `generationId` | string | The ID of the generation job. Use with Check Status to poll for completion. | + +### `gamma_check_status` + +Check the status of a Gamma generation job. Returns the gamma URL when completed, or error details if failed. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Gamma API key | +| `generationId` | string | Yes | The generation ID returned by the Generate or Generate from Template tool | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `generationId` | string | The generation ID that was checked | +| `status` | string | Generation status: pending, completed, or failed | +| `gammaUrl` | string | URL of the generated gamma \(only present when status is completed\) | +| `credits` | object | Credit usage information \(only present when status is completed\) | +| ↳ `deducted` | number | Number of credits deducted for this generation | +| ↳ `remaining` | number | Remaining credits in the account | +| `error` | object | Error details \(only present when status is failed\) | +| ↳ `message` | string | Human-readable error message | +| ↳ `statusCode` | number | HTTP status code of the error | + +### `gamma_list_themes` + +List available themes in your Gamma workspace. Returns theme IDs, names, and keywords for styling. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Gamma API key | +| `query` | string | No | Search query to filter themes by name \(case-insensitive\) | +| `limit` | number | No | Maximum number of themes to return per page \(max 50\) | +| `after` | string | No | Pagination cursor from a previous response \(nextCursor\) to fetch the next page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `themes` | array | List of available themes | +| ↳ `id` | string | Theme ID \(use with themeId parameter\) | +| ↳ `name` | string | Theme display name | +| ↳ `type` | string | Theme type: standard or custom | +| ↳ `colorKeywords` | array | Color descriptors for this theme | +| ↳ `toneKeywords` | array | Tone descriptors for this theme | +| `hasMore` | boolean | Whether more results are available on the next page | +| `nextCursor` | string | Pagination cursor to pass as the after parameter for the next page | + +### `gamma_list_folders` + +List available folders in your Gamma workspace. Returns folder IDs and names for organizing generated content. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Gamma API key | +| `query` | string | No | Search query to filter folders by name \(case-sensitive\) | +| `limit` | number | No | Maximum number of folders to return per page \(max 50\) | +| `after` | string | No | Pagination cursor from a previous response \(nextCursor\) to fetch the next page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `folders` | array | List of available folders | +| ↳ `id` | string | Folder ID \(use with folderIds parameter\) | +| ↳ `name` | string | Folder display name | +| `hasMore` | boolean | Whether more results are available on the next page | +| `nextCursor` | string | Pagination cursor to pass as the after parameter for the next page | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index ec0851ee59..fcb1dd02ce 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -35,6 +35,7 @@ "file", "firecrawl", "fireflies", + "gamma", "github", "gitlab", "gmail", diff --git a/apps/sim/blocks/blocks/gamma.ts b/apps/sim/blocks/blocks/gamma.ts new file mode 100644 index 0000000000..27aad52221 --- /dev/null +++ b/apps/sim/blocks/blocks/gamma.ts @@ -0,0 +1,340 @@ +import { GammaIcon } from '@/components/icons' +import { AuthMode, type BlockConfig } from '@/blocks/types' +import type { GammaResponse } from '@/tools/gamma/types' + +export const GammaBlock: BlockConfig = { + type: 'gamma', + name: 'Gamma', + description: 'Generate presentations, documents, and webpages with AI', + longDescription: + 'Integrate Gamma into the workflow. Can generate presentations, documents, webpages, and social posts from text, create from templates, check generation status, and browse themes and folders.', + docsLink: 'https://docs.sim.ai/tools/gamma', + category: 'tools', + bgColor: '#002253', + icon: GammaIcon, + authMode: AuthMode.ApiKey, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Generate', id: 'generate' }, + { label: 'Generate from Template', id: 'generate_from_template' }, + { label: 'Check Status', id: 'check_status' }, + { label: 'List Themes', id: 'list_themes' }, + { label: 'List Folders', id: 'list_folders' }, + ], + value: () => 'generate', + }, + // Generate operation inputs + { + id: 'inputText', + title: 'Input Text', + type: 'long-input', + required: { field: 'operation', value: 'generate' }, + placeholder: 'Enter text content to generate from...', + condition: { field: 'operation', value: 'generate' }, + }, + { + id: 'textMode', + title: 'Text Mode', + type: 'dropdown', + options: [ + { label: 'Generate', id: 'generate' }, + { label: 'Condense', id: 'condense' }, + { label: 'Preserve', id: 'preserve' }, + ], + value: () => 'generate', + condition: { field: 'operation', value: 'generate' }, + }, + { + id: 'format', + title: 'Format', + type: 'dropdown', + options: [ + { label: 'Presentation', id: 'presentation' }, + { label: 'Document', id: 'document' }, + { label: 'Webpage', id: 'webpage' }, + { label: 'Social', id: 'social' }, + ], + value: () => 'presentation', + condition: { field: 'operation', value: 'generate' }, + }, + { + id: 'numCards', + title: 'Number of Cards', + type: 'short-input', + placeholder: '10', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'additionalInstructions', + title: 'Additional Instructions', + type: 'long-input', + placeholder: 'Any additional instructions for the generation...', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'textAmount', + title: 'Text Amount', + type: 'dropdown', + options: [ + { label: 'Brief', id: 'brief' }, + { label: 'Medium', id: 'medium' }, + { label: 'Detailed', id: 'detailed' }, + { label: 'Extensive', id: 'extensive' }, + ], + value: () => 'medium', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'textTone', + title: 'Tone', + type: 'short-input', + placeholder: 'e.g., professional, casual, academic', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'textAudience', + title: 'Audience', + type: 'short-input', + placeholder: 'e.g., executives, students, developers', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'textLanguage', + title: 'Language', + type: 'short-input', + placeholder: 'en', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'cardSplit', + title: 'Card Split', + type: 'dropdown', + options: [ + { label: 'Auto', id: 'auto' }, + { label: 'Input Text Breaks', id: 'inputTextBreaks' }, + ], + value: () => 'auto', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'cardDimensions', + title: 'Card Dimensions', + type: 'short-input', + placeholder: 'e.g., 16x9, fluid, letter, a4', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: `Generate the correct card dimensions value for a Gamma generation. +Valid values depend on the format: +- Presentation: "fluid", "16x9", "4x3" +- Document: "fluid", "pageless", "letter", "a4" +- Social: "1x1", "4x5", "9x16" +Return ONLY the dimension value string, nothing else.`, + placeholder: 'Describe the desired dimensions (e.g., "widescreen slides")...', + }, + }, + { + id: 'imageSource', + title: 'Image Source', + type: 'dropdown', + options: [ + { label: 'AI Generated', id: 'aiGenerated' }, + { label: 'Pictographic', id: 'pictographic' }, + { label: 'Unsplash', id: 'unsplash' }, + { label: 'Web (All Images)', id: 'webAllImages' }, + { label: 'Web (Free to Use)', id: 'webFreeToUse' }, + { label: 'Web (Free Commercial)', id: 'webFreeToUseCommercially' }, + { label: 'Giphy', id: 'giphy' }, + { label: 'Placeholder', id: 'placeholder' }, + { label: 'No Images', id: 'noImages' }, + ], + value: () => 'aiGenerated', + condition: { field: 'operation', value: 'generate' }, + mode: 'advanced', + }, + { + id: 'imageModel', + title: 'Image Model', + type: 'short-input', + placeholder: 'AI image model (when using AI Generated source)', + condition: { field: 'operation', value: ['generate', 'generate_from_template'] }, + mode: 'advanced', + }, + { + id: 'imageStyle', + title: 'Image Style', + type: 'short-input', + placeholder: 'e.g., watercolor, photorealistic, minimalist', + condition: { field: 'operation', value: ['generate', 'generate_from_template'] }, + mode: 'advanced', + }, + { + id: 'exportAs', + title: 'Export As', + type: 'dropdown', + options: [ + { label: 'None', id: '' }, + { label: 'PDF', id: 'pdf' }, + { label: 'PPTX', id: 'pptx' }, + ], + value: () => '', + condition: { field: 'operation', value: ['generate', 'generate_from_template'] }, + mode: 'advanced', + }, + { + id: 'themeId', + title: 'Theme ID', + type: 'short-input', + placeholder: 'Enter theme ID (use List Themes to find)', + condition: { field: 'operation', value: ['generate', 'generate_from_template'] }, + mode: 'advanced', + }, + { + id: 'folderIds', + title: 'Folder IDs', + type: 'short-input', + placeholder: 'Comma-separated folder IDs', + condition: { field: 'operation', value: ['generate', 'generate_from_template'] }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: `Generate a comma-separated list of Gamma folder IDs. +The user will describe which folders to store the generated gamma in. +Each folder ID is a string identifier from the Gamma workspace. +Use the List Folders operation to find available folder IDs first. +Return ONLY the comma-separated IDs, nothing else. +Example: "folder_abc123, folder_def456"`, + placeholder: 'Describe which folders to store the gamma in...', + }, + }, + // Generate from Template inputs + { + id: 'gammaId', + title: 'Template Gamma ID', + type: 'short-input', + required: { field: 'operation', value: 'generate_from_template' }, + placeholder: 'Enter the template gamma ID', + condition: { field: 'operation', value: 'generate_from_template' }, + }, + { + id: 'prompt', + title: 'Prompt', + type: 'long-input', + required: { field: 'operation', value: 'generate_from_template' }, + placeholder: 'Instructions for adapting the template...', + condition: { field: 'operation', value: 'generate_from_template' }, + }, + // Check Status inputs + { + id: 'generationId', + title: 'Generation ID', + type: 'short-input', + required: { field: 'operation', value: 'check_status' }, + placeholder: 'Enter the generation ID to check', + condition: { field: 'operation', value: 'check_status' }, + }, + // List Themes / List Folders inputs + { + id: 'query', + title: 'Search Query', + type: 'short-input', + placeholder: 'Filter by name...', + condition: { field: 'operation', value: ['list_themes', 'list_folders'] }, + mode: 'advanced', + }, + { + id: 'limit', + title: 'Limit', + type: 'short-input', + placeholder: '50', + condition: { field: 'operation', value: ['list_themes', 'list_folders'] }, + mode: 'advanced', + }, + { + id: 'after', + title: 'Pagination Cursor', + type: 'short-input', + placeholder: 'nextCursor from previous response', + condition: { field: 'operation', value: ['list_themes', 'list_folders'] }, + mode: 'advanced', + }, + // API Key (common) + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + required: true, + placeholder: 'Enter your Gamma API key', + password: true, + }, + ], + tools: { + access: [ + 'gamma_generate', + 'gamma_generate_from_template', + 'gamma_check_status', + 'gamma_list_themes', + 'gamma_list_folders', + ], + config: { + tool: (params) => `gamma_${params.operation}`, + params: (params) => { + const result: Record = {} + if (params.numCards) result.numCards = Number(params.numCards) + if (params.limit) result.limit = Number(params.limit) + if (params.exportAs === '') result.exportAs = undefined + return result + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Gamma API key' }, + inputText: { type: 'string', description: 'Text content for generation' }, + textMode: { type: 'string', description: 'Text handling mode' }, + format: { type: 'string', description: 'Output format' }, + numCards: { type: 'number', description: 'Number of cards to generate' }, + additionalInstructions: { type: 'string', description: 'Additional generation instructions' }, + textAmount: { type: 'string', description: 'Amount of text to generate' }, + textTone: { type: 'string', description: 'Tone of generated text' }, + textAudience: { type: 'string', description: 'Target audience' }, + textLanguage: { type: 'string', description: 'Language code' }, + cardSplit: { type: 'string', description: 'Card splitting strategy' }, + cardDimensions: { type: 'string', description: 'Card aspect ratio' }, + imageSource: { type: 'string', description: 'Image source for generation' }, + imageModel: { type: 'string', description: 'AI image model' }, + imageStyle: { type: 'string', description: 'Image style directive' }, + exportAs: { type: 'string', description: 'Export format' }, + themeId: { type: 'string', description: 'Theme ID' }, + folderIds: { type: 'string', description: 'Comma-separated folder IDs' }, + gammaId: { type: 'string', description: 'Template gamma ID' }, + prompt: { type: 'string', description: 'Template adaptation prompt' }, + generationId: { type: 'string', description: 'Generation ID to check' }, + query: { type: 'string', description: 'Search query' }, + limit: { type: 'number', description: 'Result limit' }, + after: { type: 'string', description: 'Pagination cursor' }, + }, + outputs: { + generationId: { type: 'string', description: 'Generation job ID' }, + status: { type: 'string', description: 'Generation status' }, + gammaUrl: { type: 'string', description: 'URL of the generated gamma' }, + credits: { type: 'json', description: 'Credit usage (deducted, remaining)' }, + error: { type: 'json', description: 'Error details if generation failed' }, + themes: { type: 'json', description: 'List of themes' }, + folders: { type: 'json', description: 'List of folders' }, + hasMore: { type: 'boolean', description: 'Whether more results are available' }, + nextCursor: { type: 'string', description: 'Pagination cursor for next page' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index c203af4f66..88d6fc59a7 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -39,6 +39,7 @@ import { FileBlock, FileV2Block, FileV3Block } from '@/blocks/blocks/file' import { FirecrawlBlock } from '@/blocks/blocks/firecrawl' import { FirefliesBlock, FirefliesV2Block } from '@/blocks/blocks/fireflies' import { FunctionBlock } from '@/blocks/blocks/function' +import { GammaBlock } from '@/blocks/blocks/gamma' import { GenericWebhookBlock } from '@/blocks/blocks/generic_webhook' import { GitHubBlock, GitHubV2Block } from '@/blocks/blocks/github' import { GitLabBlock } from '@/blocks/blocks/gitlab' @@ -227,6 +228,7 @@ export const registry: Record = { fireflies: FirefliesBlock, fireflies_v2: FirefliesV2Block, function: FunctionBlock, + gamma: GammaBlock, generic_webhook: GenericWebhookBlock, github: GitHubBlock, github_v2: GitHubV2Block, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 9da5a57508..c178dd9faa 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -526,6 +526,17 @@ export function SlackMonoIcon(props: SVGProps) { ) } +export function GammaIcon(props: SVGProps) { + return ( + + + + ) +} + export function GithubIcon(props: SVGProps) { return ( diff --git a/apps/sim/tools/gamma/check_status.ts b/apps/sim/tools/gamma/check_status.ts new file mode 100644 index 0000000000..f360501b8a --- /dev/null +++ b/apps/sim/tools/gamma/check_status.ts @@ -0,0 +1,109 @@ +import type { GammaCheckStatusParams, GammaCheckStatusResponse } from '@/tools/gamma/types' +import type { ToolConfig } from '@/tools/types' + +export const checkStatusTool: ToolConfig = { + id: 'gamma_check_status', + name: 'Gamma Check Status', + description: + 'Check the status of a Gamma generation job. Returns the gamma URL when completed, or error details if failed.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Gamma API key', + }, + generationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The generation ID returned by the Generate or Generate from Template tool', + }, + }, + + request: { + url: (params) => `https://public-api.gamma.app/v1.0/generations/${params.generationId}`, + method: 'GET', + headers: (params) => ({ + 'X-API-KEY': params.apiKey, + }), + }, + + transformResponse: async (response: Response): Promise => { + const data = await response.json() + + const output: GammaCheckStatusResponse['output'] = { + generationId: data.generationId ?? '', + status: data.status ?? 'pending', + gammaUrl: data.gammaUrl ?? null, + } + + if (data.credits) { + output.credits = { + deducted: data.credits.deducted ?? null, + remaining: data.credits.remaining ?? null, + } + } + + if (data.error) { + output.error = { + message: data.error.message ?? null, + statusCode: data.error.statusCode ?? null, + } + } + + return { success: true, output } + }, + + outputs: { + generationId: { + type: 'string', + description: 'The generation ID that was checked', + }, + status: { + type: 'string', + description: 'Generation status: pending, completed, or failed', + }, + gammaUrl: { + type: 'string', + description: 'URL of the generated gamma (only present when status is completed)', + optional: true, + }, + credits: { + type: 'object', + description: 'Credit usage information (only present when status is completed)', + optional: true, + properties: { + deducted: { + type: 'number', + description: 'Number of credits deducted for this generation', + optional: true, + }, + remaining: { + type: 'number', + description: 'Remaining credits in the account', + optional: true, + }, + }, + }, + error: { + type: 'object', + description: 'Error details (only present when status is failed)', + optional: true, + properties: { + message: { + type: 'string', + description: 'Human-readable error message', + optional: true, + }, + statusCode: { + type: 'number', + description: 'HTTP status code of the error', + optional: true, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/gamma/generate.ts b/apps/sim/tools/gamma/generate.ts new file mode 100644 index 0000000000..5f30f0b684 --- /dev/null +++ b/apps/sim/tools/gamma/generate.ts @@ -0,0 +1,189 @@ +import type { GammaGenerateParams, GammaGenerateResponse } from '@/tools/gamma/types' +import type { ToolConfig } from '@/tools/types' + +export const generateTool: ToolConfig = { + id: 'gamma_generate', + name: 'Gamma Generate', + description: + 'Generate a new Gamma presentation, document, webpage, or social post from text input.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Gamma API key', + }, + inputText: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Text and image URLs used to generate your gamma (1-100,000 tokens)', + }, + textMode: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'How to handle input text: generate (AI expands), condense (AI summarizes), or preserve (keep as-is)', + }, + format: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Output format: presentation, document, webpage, or social (default: presentation)', + }, + themeId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Custom Gamma workspace theme ID (use List Themes to find available themes)', + }, + numCards: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of cards/slides to generate (1-60 for Pro, 1-75 for Ultra; default: 10)', + }, + cardSplit: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'How to split content into cards: auto or inputTextBreaks (default: auto)', + }, + cardDimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Card aspect ratio. Presentation: fluid, 16x9, 4x3. Document: fluid, pageless, letter, a4. Social: 1x1, 4x5, 9x16', + }, + additionalInstructions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Additional instructions for the AI generation (max 2000 chars)', + }, + exportAs: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Automatically export the generated gamma as pdf or pptx', + }, + folderIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated folder IDs to store the generated gamma in', + }, + textAmount: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Amount of text per card: brief, medium, detailed, or extensive', + }, + textTone: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Tone of the generated text, e.g. "professional", "casual" (max 500 chars)', + }, + textAudience: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Target audience for the generated text, e.g. "executives", "students" (max 500 chars)', + }, + textLanguage: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Language code for the generated text (default: en)', + }, + imageSource: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Where to source images: aiGenerated, pictographic, unsplash, webAllImages, webFreeToUse, webFreeToUseCommercially, giphy, placeholder, or noImages', + }, + imageModel: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'AI image generation model to use when imageSource is aiGenerated', + }, + imageStyle: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Style directive for AI-generated images, e.g. "watercolor", "photorealistic" (max 500 chars)', + }, + }, + + request: { + url: 'https://public-api.gamma.app/v1.0/generations', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'X-API-KEY': params.apiKey, + }), + body: (params) => { + const body: Record = { + inputText: params.inputText, + textMode: params.textMode, + } + + if (params.format) body.format = params.format + if (params.themeId) body.themeId = params.themeId + if (params.numCards) body.numCards = params.numCards + if (params.cardSplit) body.cardSplit = params.cardSplit + if (params.additionalInstructions) body.additionalInstructions = params.additionalInstructions + if (params.exportAs) body.exportAs = params.exportAs + if (params.folderIds) { + body.folderIds = params.folderIds.split(',').map((id: string) => id.trim()) + } + + const textOptions: Record = {} + if (params.textAmount) textOptions.amount = params.textAmount + if (params.textTone) textOptions.tone = params.textTone + if (params.textAudience) textOptions.audience = params.textAudience + if (params.textLanguage) textOptions.language = params.textLanguage + if (Object.keys(textOptions).length > 0) body.textOptions = textOptions + + const imageOptions: Record = {} + if (params.imageSource) imageOptions.source = params.imageSource + if (params.imageModel) imageOptions.model = params.imageModel + if (params.imageStyle) imageOptions.style = params.imageStyle + if (Object.keys(imageOptions).length > 0) body.imageOptions = imageOptions + + const cardOptions: Record = {} + if (params.cardDimensions) cardOptions.dimensions = params.cardDimensions + if (Object.keys(cardOptions).length > 0) body.cardOptions = cardOptions + + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + return { + success: true, + output: { + generationId: data.generationId ?? '', + }, + } + }, + + outputs: { + generationId: { + type: 'string', + description: 'The ID of the generation job. Use with Check Status to poll for completion.', + }, + }, +} diff --git a/apps/sim/tools/gamma/generate_from_template.ts b/apps/sim/tools/gamma/generate_from_template.ts new file mode 100644 index 0000000000..97b682c71f --- /dev/null +++ b/apps/sim/tools/gamma/generate_from_template.ts @@ -0,0 +1,113 @@ +import type { + GammaGenerateFromTemplateParams, + GammaGenerateFromTemplateResponse, +} from '@/tools/gamma/types' +import type { ToolConfig } from '@/tools/types' + +export const generateFromTemplateTool: ToolConfig< + GammaGenerateFromTemplateParams, + GammaGenerateFromTemplateResponse +> = { + id: 'gamma_generate_from_template', + name: 'Gamma Generate from Template', + description: 'Generate a new Gamma by adapting an existing template with a prompt.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Gamma API key', + }, + gammaId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the template gamma to adapt', + }, + prompt: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Instructions for how to adapt the template (1-100,000 tokens)', + }, + themeId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Custom Gamma workspace theme ID to apply', + }, + exportAs: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Automatically export the generated gamma as pdf or pptx', + }, + folderIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated folder IDs to store the generated gamma in', + }, + imageModel: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'AI image generation model to use when imageSource is aiGenerated', + }, + imageStyle: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Style directive for AI-generated images, e.g. "watercolor", "photorealistic" (max 500 chars)', + }, + }, + + request: { + url: 'https://public-api.gamma.app/v1.0/generations/from-template', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'X-API-KEY': params.apiKey, + }), + body: (params) => { + const body: Record = { + gammaId: params.gammaId, + prompt: params.prompt, + } + + if (params.themeId) body.themeId = params.themeId + if (params.exportAs) body.exportAs = params.exportAs + if (params.folderIds) { + body.folderIds = params.folderIds.split(',').map((id: string) => id.trim()) + } + + const imageOptions: Record = {} + if (params.imageModel) imageOptions.model = params.imageModel + if (params.imageStyle) imageOptions.style = params.imageStyle + if (Object.keys(imageOptions).length > 0) body.imageOptions = imageOptions + + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + return { + success: true, + output: { + generationId: data.generationId ?? '', + }, + } + }, + + outputs: { + generationId: { + type: 'string', + description: 'The ID of the generation job. Use with Check Status to poll for completion.', + }, + }, +} diff --git a/apps/sim/tools/gamma/index.ts b/apps/sim/tools/gamma/index.ts new file mode 100644 index 0000000000..792a5260da --- /dev/null +++ b/apps/sim/tools/gamma/index.ts @@ -0,0 +1,11 @@ +import { checkStatusTool } from '@/tools/gamma/check_status' +import { generateTool } from '@/tools/gamma/generate' +import { generateFromTemplateTool } from '@/tools/gamma/generate_from_template' +import { listFoldersTool } from '@/tools/gamma/list_folders' +import { listThemesTool } from '@/tools/gamma/list_themes' + +export const gammaGenerateTool = generateTool +export const gammaGenerateFromTemplateTool = generateFromTemplateTool +export const gammaCheckStatusTool = checkStatusTool +export const gammaListThemesTool = listThemesTool +export const gammaListFoldersTool = listFoldersTool diff --git a/apps/sim/tools/gamma/list_folders.ts b/apps/sim/tools/gamma/list_folders.ts new file mode 100644 index 0000000000..1a5be9afad --- /dev/null +++ b/apps/sim/tools/gamma/list_folders.ts @@ -0,0 +1,91 @@ +import type { GammaListFoldersParams, GammaListFoldersResponse } from '@/tools/gamma/types' +import type { ToolConfig } from '@/tools/types' + +export const listFoldersTool: ToolConfig = { + id: 'gamma_list_folders', + name: 'Gamma List Folders', + description: + 'List available folders in your Gamma workspace. Returns folder IDs and names for organizing generated content.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Gamma API key', + }, + query: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search query to filter folders by name (case-sensitive)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of folders to return per page (max 50)', + }, + after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from a previous response (nextCursor) to fetch the next page', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://public-api.gamma.app/v1.0/folders') + if (params.query) url.searchParams.append('query', params.query) + if (params.limit) url.searchParams.append('limit', String(params.limit)) + if (params.after) url.searchParams.append('after', params.after) + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + 'X-API-KEY': params.apiKey, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + const items = Array.isArray(data.data) ? data.data : Array.isArray(data) ? data : [] + + return { + success: true, + output: { + folders: items.map((folder: { id?: string; name?: string }) => ({ + id: folder.id ?? '', + name: folder.name ?? '', + })), + hasMore: data.hasMore ?? false, + nextCursor: data.nextCursor ?? null, + }, + } + }, + + outputs: { + folders: { + type: 'array', + description: 'List of available folders', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Folder ID (use with folderIds parameter)' }, + name: { type: 'string', description: 'Folder display name' }, + }, + }, + }, + hasMore: { + type: 'boolean', + description: 'Whether more results are available on the next page', + }, + nextCursor: { + type: 'string', + description: 'Pagination cursor to pass as the after parameter for the next page', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/gamma/list_themes.ts b/apps/sim/tools/gamma/list_themes.ts new file mode 100644 index 0000000000..1ffc17f26f --- /dev/null +++ b/apps/sim/tools/gamma/list_themes.ts @@ -0,0 +1,113 @@ +import type { GammaListThemesParams, GammaListThemesResponse } from '@/tools/gamma/types' +import type { ToolConfig } from '@/tools/types' + +export const listThemesTool: ToolConfig = { + id: 'gamma_list_themes', + name: 'Gamma List Themes', + description: + 'List available themes in your Gamma workspace. Returns theme IDs, names, and keywords for styling.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Gamma API key', + }, + query: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search query to filter themes by name (case-insensitive)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of themes to return per page (max 50)', + }, + after: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from a previous response (nextCursor) to fetch the next page', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://public-api.gamma.app/v1.0/themes') + if (params.query) url.searchParams.append('query', params.query) + if (params.limit) url.searchParams.append('limit', String(params.limit)) + if (params.after) url.searchParams.append('after', params.after) + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + 'X-API-KEY': params.apiKey, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + const items = Array.isArray(data.data) ? data.data : Array.isArray(data) ? data : [] + + return { + success: true, + output: { + themes: items.map( + (theme: { + id?: string + name?: string + type?: string + colorKeywords?: string[] + toneKeywords?: string[] + }) => ({ + id: theme.id ?? '', + name: theme.name ?? '', + type: theme.type ?? '', + colorKeywords: theme.colorKeywords ?? [], + toneKeywords: theme.toneKeywords ?? [], + }) + ), + hasMore: data.hasMore ?? false, + nextCursor: data.nextCursor ?? null, + }, + } + }, + + outputs: { + themes: { + type: 'array', + description: 'List of available themes', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Theme ID (use with themeId parameter)' }, + name: { type: 'string', description: 'Theme display name' }, + type: { type: 'string', description: 'Theme type: standard or custom' }, + colorKeywords: { + type: 'array', + description: 'Color descriptors for this theme', + items: { type: 'string', description: 'Color keyword' }, + }, + toneKeywords: { + type: 'array', + description: 'Tone descriptors for this theme', + items: { type: 'string', description: 'Tone keyword' }, + }, + }, + }, + }, + hasMore: { + type: 'boolean', + description: 'Whether more results are available on the next page', + }, + nextCursor: { + type: 'string', + description: 'Pagination cursor to pass as the after parameter for the next page', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/gamma/types.ts b/apps/sim/tools/gamma/types.ts new file mode 100644 index 0000000000..56fcab7a79 --- /dev/null +++ b/apps/sim/tools/gamma/types.ts @@ -0,0 +1,163 @@ +import type { ToolResponse } from '@/tools/types' + +/** + * Base parameters shared across all Gamma API tools. + */ +export interface GammaBaseParams { + apiKey: string +} + +/** + * Parameters for the Generate a Gamma tool. + */ +export interface GammaGenerateParams extends GammaBaseParams { + inputText: string + textMode: 'generate' | 'condense' | 'preserve' + format?: 'presentation' | 'document' | 'webpage' | 'social' + themeId?: string + numCards?: number + cardSplit?: 'auto' | 'inputTextBreaks' + cardDimensions?: string + additionalInstructions?: string + exportAs?: 'pdf' | 'pptx' + folderIds?: string + textAmount?: 'brief' | 'medium' | 'detailed' | 'extensive' + textTone?: string + textAudience?: string + textLanguage?: string + imageSource?: + | 'aiGenerated' + | 'pictographic' + | 'unsplash' + | 'webAllImages' + | 'webFreeToUse' + | 'webFreeToUseCommercially' + | 'giphy' + | 'placeholder' + | 'noImages' + imageModel?: string + imageStyle?: string +} + +/** + * Parameters for the Generate from Template tool. + */ +export interface GammaGenerateFromTemplateParams extends GammaBaseParams { + gammaId: string + prompt: string + themeId?: string + exportAs?: 'pdf' | 'pptx' + folderIds?: string + imageModel?: string + imageStyle?: string +} + +/** + * Parameters for the Check Generation Status tool. + */ +export interface GammaCheckStatusParams extends GammaBaseParams { + generationId: string +} + +/** + * Parameters for the List Themes tool. + */ +export interface GammaListThemesParams extends GammaBaseParams { + query?: string + limit?: number + after?: string +} + +/** + * Parameters for the List Folders tool. + */ +export interface GammaListFoldersParams extends GammaBaseParams { + query?: string + limit?: number + after?: string +} + +/** + * Response for the Generate tool. + */ +export interface GammaGenerateResponse extends ToolResponse { + output: { + generationId: string + } +} + +/** + * Response for the Generate from Template tool. + */ +export interface GammaGenerateFromTemplateResponse extends ToolResponse { + output: { + generationId: string + } +} + +/** + * Response for the Check Status tool. + */ +export interface GammaCheckStatusResponse extends ToolResponse { + output: { + generationId: string + status: 'pending' | 'completed' | 'failed' + gammaUrl: string | null + credits?: { + deducted: number | null + remaining: number | null + } + error?: { + message: string | null + statusCode: number | null + } + } +} + +/** + * Theme object from the Gamma API. + */ +export interface GammaTheme { + id: string + name: string + type: string + colorKeywords: string[] + toneKeywords: string[] +} + +/** + * Response for the List Themes tool. + */ +export interface GammaListThemesResponse extends ToolResponse { + output: { + themes: GammaTheme[] + hasMore: boolean + nextCursor: string | null + } +} + +/** + * Folder object from the Gamma API. + */ +export interface GammaFolder { + id: string + name: string +} + +/** + * Response for the List Folders tool. + */ +export interface GammaListFoldersResponse extends ToolResponse { + output: { + folders: GammaFolder[] + hasMore: boolean + nextCursor: string | null + } +} + +export type GammaResponse = + | GammaGenerateResponse + | GammaGenerateFromTemplateResponse + | GammaCheckStatusResponse + | GammaListThemesResponse + | GammaListFoldersResponse diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 4bcbc9a13a..aa57db5bb5 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -414,6 +414,13 @@ import { firefliesUploadAudioTool, } from '@/tools/fireflies' import { functionExecuteTool } from '@/tools/function' +import { + gammaCheckStatusTool, + gammaGenerateFromTemplateTool, + gammaGenerateTool, + gammaListFoldersTool, + gammaListThemesTool, +} from '@/tools/gamma' import { githubAddAssigneesTool, githubAddAssigneesV2Tool, @@ -2159,6 +2166,11 @@ export const tools: Record = { huggingface_chat: huggingfaceChatTool, llm_chat: llmChatTool, function_execute: functionExecuteTool, + gamma_generate: gammaGenerateTool, + gamma_generate_from_template: gammaGenerateFromTemplateTool, + gamma_check_status: gammaCheckStatusTool, + gamma_list_themes: gammaListThemesTool, + gamma_list_folders: gammaListFoldersTool, vision_tool: visionTool, vision_tool_v2: visionToolV2, file_parser: fileParseTool,