mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
fix(tools): fixed zendesk tools, kb upload failure for md files, stronger typing (#2297)
* fix(tools): fixed zendesk tools, kb upload failure for md files, stronger typing * ack PR comments
This commit is contained in:
@@ -4151,7 +4151,7 @@ export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
|
||||
<circle r='108' fill='#d53' />
|
||||
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
|
||||
<circle r='96' fill='none' stroke='#ffffff' strokeWidth={7} />
|
||||
<path
|
||||
d='M-32-55C-62-48-51-6-51-6l19 93 7 3M-39-73h-8l11 4s-11 0-11 7c24-1 35 5 35 5'
|
||||
fill='#ddd'
|
||||
|
||||
@@ -40,7 +40,7 @@ Processes a provided thought/instruction, making it available for subsequent ste
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `thought` | string | Yes | The thought process or instruction provided by the user in the Thinking Step block. |
|
||||
| `thought` | string | Yes | Your internal reasoning, analysis, or thought process. Use this to think through the problem step by step before responding. |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -34,38 +34,30 @@ Integrate Translate into the workflow. Can translate text to any language.
|
||||
|
||||
## Tools
|
||||
|
||||
### `openai_chat`
|
||||
### `llm_chat`
|
||||
|
||||
Send a chat completion request to any supported LLM provider
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Yes | The model to use \(e.g., gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | No | System prompt to set the behavior of the assistant |
|
||||
| `context` | string | Yes | The user message or context to send to the model |
|
||||
| `apiKey` | string | No | API key for the provider \(uses platform key if not provided for hosted models\) |
|
||||
| `temperature` | number | No | Temperature for response generation \(0-2\) |
|
||||
| `maxTokens` | number | No | Maximum tokens in the response |
|
||||
| `azureEndpoint` | string | No | Azure OpenAI endpoint URL |
|
||||
| `azureApiVersion` | string | No | Azure OpenAI API version |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Translated text |
|
||||
| `model` | string | Model used |
|
||||
| `tokens` | json | Token usage |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
|
||||
### `google_chat`
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Translated text |
|
||||
| `model` | string | Model used |
|
||||
| `tokens` | json | Token usage |
|
||||
| `content` | string | The generated response content |
|
||||
| `model` | string | The model used for generation |
|
||||
| `tokens` | object | Token usage information |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -254,8 +254,8 @@ Upload a media file (image, video, document) to WordPress.com
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `file` | string | Yes | Base64 encoded file data or URL to fetch file from |
|
||||
| `filename` | string | Yes | Filename with extension \(e.g., image.jpg\) |
|
||||
| `file` | file | No | File to upload \(UserFile object\) |
|
||||
| `filename` | string | No | Optional filename override \(e.g., image.jpg\) |
|
||||
| `title` | string | No | Media title |
|
||||
| `caption` | string | No | Media caption |
|
||||
| `altText` | string | No | Alternative text for accessibility |
|
||||
|
||||
@@ -173,25 +173,6 @@ Get videos from a YouTube playlist.
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of videos in the playlist |
|
||||
|
||||
### `youtube_related_videos`
|
||||
|
||||
Find videos related to a specific YouTube video.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `videoId` | string | Yes | YouTube video ID to find related videos for |
|
||||
| `maxResults` | number | No | Maximum number of related videos to return \(1-50\) |
|
||||
| `pageToken` | string | No | Page token for pagination |
|
||||
| `apiKey` | string | Yes | YouTube API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of related videos |
|
||||
|
||||
### `youtube_comments`
|
||||
|
||||
Get comments from a YouTube video.
|
||||
|
||||
@@ -76,8 +76,9 @@ Retrieve a list of tickets from Zendesk with optional filtering
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Tickets data and metadata |
|
||||
| `tickets` | array | Array of ticket objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_ticket`
|
||||
|
||||
@@ -96,8 +97,8 @@ Get a single ticket by ID from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Ticket data |
|
||||
| `ticket` | object | Ticket object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_ticket`
|
||||
|
||||
@@ -125,8 +126,8 @@ Create a new ticket in Zendesk with support for custom fields
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created ticket data |
|
||||
| `ticket` | object | Created ticket object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_tickets_bulk`
|
||||
|
||||
@@ -145,8 +146,8 @@ Create multiple tickets in Zendesk at once (max 100)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk create job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_ticket`
|
||||
|
||||
@@ -174,8 +175,8 @@ Update an existing ticket in Zendesk with support for custom fields
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated ticket data |
|
||||
| `ticket` | object | Updated ticket object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_tickets_bulk`
|
||||
|
||||
@@ -199,8 +200,8 @@ Update multiple tickets in Zendesk at once (max 100)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk update job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_delete_ticket`
|
||||
|
||||
@@ -219,8 +220,8 @@ Delete a ticket from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Delete confirmation |
|
||||
| `deleted` | boolean | Deletion success |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_merge_tickets`
|
||||
|
||||
@@ -241,8 +242,8 @@ Merge multiple tickets into a target ticket
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Merge job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_users`
|
||||
|
||||
@@ -264,8 +265,9 @@ Retrieve a list of users from Zendesk with optional filtering
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Users data and metadata |
|
||||
| `users` | array | Array of user objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_user`
|
||||
|
||||
@@ -284,8 +286,8 @@ Get a single user by ID from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | User data |
|
||||
| `user` | object | User object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_current_user`
|
||||
|
||||
@@ -303,8 +305,8 @@ Get the currently authenticated user from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Current user data |
|
||||
| `user` | object | Current user object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_search_users`
|
||||
|
||||
@@ -326,8 +328,9 @@ Search for users in Zendesk using a query string
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Users search results |
|
||||
| `users` | array | Array of user objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_user`
|
||||
|
||||
@@ -353,8 +356,8 @@ Create a new user in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created user data |
|
||||
| `user` | object | Created user object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_users_bulk`
|
||||
|
||||
@@ -373,8 +376,8 @@ Create multiple users in Zendesk using bulk import
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk creation job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_user`
|
||||
|
||||
@@ -401,8 +404,8 @@ Update an existing user in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated user data |
|
||||
| `user` | object | Updated user object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_users_bulk`
|
||||
|
||||
@@ -421,8 +424,8 @@ Update multiple users in Zendesk using bulk update
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk update job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_delete_user`
|
||||
|
||||
@@ -441,8 +444,8 @@ Delete a user from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deleted user data |
|
||||
| `deleted` | boolean | Deletion success |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_organizations`
|
||||
|
||||
@@ -462,8 +465,9 @@ Retrieve a list of organizations from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Organizations data and metadata |
|
||||
| `organizations` | array | Array of organization objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_get_organization`
|
||||
|
||||
@@ -482,8 +486,8 @@ Get a single organization by ID from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Organization data |
|
||||
| `organization` | object | Organization object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_autocomplete_organizations`
|
||||
|
||||
@@ -504,8 +508,9 @@ Autocomplete organizations in Zendesk by name prefix (for name matching/autocomp
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Organizations search results |
|
||||
| `organizations` | array | Array of organization objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_organization`
|
||||
|
||||
@@ -529,8 +534,8 @@ Create a new organization in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created organization data |
|
||||
| `organization` | object | Created organization object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_create_organizations_bulk`
|
||||
|
||||
@@ -549,8 +554,8 @@ Create multiple organizations in Zendesk using bulk import
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Bulk creation job status |
|
||||
| `jobStatus` | object | Job status object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_update_organization`
|
||||
|
||||
@@ -575,8 +580,8 @@ Update an existing organization in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated organization data |
|
||||
| `organization` | object | Updated organization object |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_delete_organization`
|
||||
|
||||
@@ -595,8 +600,8 @@ Delete an organization from Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deleted organization data |
|
||||
| `deleted` | boolean | Deletion success |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_search`
|
||||
|
||||
@@ -619,8 +624,9 @@ Unified search across tickets, users, and organizations in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search results |
|
||||
| `results` | array | Array of result objects |
|
||||
| `paging` | object | Pagination information |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
### `zendesk_search_count`
|
||||
|
||||
@@ -639,8 +645,8 @@ Count the number of search results matching a query in Zendesk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search count result |
|
||||
| `count` | number | Number of matching results |
|
||||
| `metadata` | object | Operation metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
224
apps/sim/app/api/tools/wordpress/upload/route.ts
Normal file
224
apps/sim/app/api/tools/wordpress/upload/route.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
getFileExtension,
|
||||
getMimeTypeFromExtension,
|
||||
processSingleFileToUserFile,
|
||||
} from '@/lib/uploads/utils/file-utils'
|
||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('WordPressUploadAPI')
|
||||
|
||||
const WORDPRESS_COM_API_BASE = 'https://public-api.wordpress.com/wp/v2/sites'
|
||||
|
||||
const WordPressUploadSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
siteId: z.string().min(1, 'Site ID is required'),
|
||||
file: z.any().optional().nullable(),
|
||||
filename: z.string().optional().nullable(),
|
||||
title: z.string().optional().nullable(),
|
||||
caption: z.string().optional().nullable(),
|
||||
altText: z.string().optional().nullable(),
|
||||
description: z.string().optional().nullable(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized WordPress upload attempt: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: authResult.error || 'Authentication required',
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Authenticated WordPress upload request via ${authResult.authType}`,
|
||||
{
|
||||
userId: authResult.userId,
|
||||
}
|
||||
)
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = WordPressUploadSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Uploading file to WordPress`, {
|
||||
siteId: validatedData.siteId,
|
||||
filename: validatedData.filename,
|
||||
hasFile: !!validatedData.file,
|
||||
})
|
||||
|
||||
if (!validatedData.file) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'No file provided. Please upload a file.',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Process file - convert to UserFile format if needed
|
||||
const fileData = validatedData.file
|
||||
|
||||
let userFile
|
||||
try {
|
||||
userFile = processSingleFileToUserFile(fileData, requestId, logger)
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to process file',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Downloading file from storage`, {
|
||||
fileName: userFile.name,
|
||||
key: userFile.key,
|
||||
size: userFile.size,
|
||||
})
|
||||
|
||||
let fileBuffer: Buffer
|
||||
|
||||
try {
|
||||
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Failed to download file:`, error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
// Use provided filename or fall back to the original file name
|
||||
const filename = validatedData.filename || userFile.name
|
||||
const mimeType = userFile.type || getMimeTypeFromExtension(getFileExtension(filename))
|
||||
|
||||
logger.info(`[${requestId}] Uploading to WordPress`, {
|
||||
siteId: validatedData.siteId,
|
||||
filename,
|
||||
mimeType,
|
||||
size: fileBuffer.length,
|
||||
})
|
||||
|
||||
// Upload to WordPress using multipart form data
|
||||
const formData = new FormData()
|
||||
// Convert Buffer to Uint8Array for Blob compatibility
|
||||
const uint8Array = new Uint8Array(fileBuffer)
|
||||
const blob = new Blob([uint8Array], { type: mimeType })
|
||||
formData.append('file', blob, filename)
|
||||
|
||||
// Add optional metadata
|
||||
if (validatedData.title) {
|
||||
formData.append('title', validatedData.title)
|
||||
}
|
||||
if (validatedData.caption) {
|
||||
formData.append('caption', validatedData.caption)
|
||||
}
|
||||
if (validatedData.altText) {
|
||||
formData.append('alt_text', validatedData.altText)
|
||||
}
|
||||
if (validatedData.description) {
|
||||
formData.append('description', validatedData.description)
|
||||
}
|
||||
|
||||
const uploadResponse = await fetch(`${WORDPRESS_COM_API_BASE}/${validatedData.siteId}/media`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
const errorText = await uploadResponse.text()
|
||||
let errorMessage = `WordPress API error: ${uploadResponse.statusText}`
|
||||
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
errorMessage = errorJson.message || errorJson.error || errorMessage
|
||||
} catch {
|
||||
// Use default error message
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] WordPress API error:`, {
|
||||
status: uploadResponse.status,
|
||||
statusText: uploadResponse.statusText,
|
||||
error: errorText,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
},
|
||||
{ status: uploadResponse.status }
|
||||
)
|
||||
}
|
||||
|
||||
const uploadData = await uploadResponse.json()
|
||||
|
||||
logger.info(`[${requestId}] File uploaded successfully`, {
|
||||
mediaId: uploadData.id,
|
||||
sourceUrl: uploadData.source_url,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
media: {
|
||||
id: uploadData.id,
|
||||
date: uploadData.date,
|
||||
slug: uploadData.slug,
|
||||
type: uploadData.type,
|
||||
link: uploadData.link,
|
||||
title: uploadData.title,
|
||||
caption: uploadData.caption,
|
||||
alt_text: uploadData.alt_text,
|
||||
media_type: uploadData.media_type,
|
||||
mime_type: uploadData.mime_type,
|
||||
source_url: uploadData.source_url,
|
||||
media_details: uploadData.media_details,
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Invalid request data',
|
||||
details: error.errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] Error uploading file to WordPress:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Internal server error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from '@/components/emcn'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils'
|
||||
@@ -53,7 +52,7 @@ export function AddDocumentsModal({
|
||||
const [dragCounter, setDragCounter] = useState(0)
|
||||
const [retryingIndexes, setRetryingIndexes] = useState<Set<number>>(new Set())
|
||||
|
||||
const { isUploading, uploadProgress, uploadFiles, clearError } = useKnowledgeUpload({
|
||||
const { isUploading, uploadProgress, uploadFiles, uploadError, clearError } = useKnowledgeUpload({
|
||||
workspaceId,
|
||||
onUploadComplete: () => {
|
||||
logger.info(`Successfully uploaded ${files.length} files`)
|
||||
@@ -234,11 +233,7 @@ export function AddDocumentsModal({
|
||||
<div className='min-h-0 flex-1 overflow-y-auto'>
|
||||
<div className='space-y-[12px]'>
|
||||
{fileError && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{fileError}</AlertDescription>
|
||||
</Alert>
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>{fileError}</p>
|
||||
)}
|
||||
|
||||
<div className='flex flex-col gap-[8px]'>
|
||||
@@ -341,24 +336,31 @@ export function AddDocumentsModal({
|
||||
</div>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant='default' onClick={handleClose} type='button' disabled={isUploading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
type='button'
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 || isUploading}
|
||||
>
|
||||
{isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Uploading...'
|
||||
: 'Upload'}
|
||||
</Button>
|
||||
<ModalFooter className='flex-col items-stretch gap-[12px]'>
|
||||
{uploadError && (
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>
|
||||
{uploadError.message}
|
||||
</p>
|
||||
)}
|
||||
<div className='flex justify-end gap-[8px]'>
|
||||
<Button variant='default' onClick={handleClose} type='button' disabled={isUploading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
type='button'
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 || isUploading}
|
||||
>
|
||||
{isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Uploading...'
|
||||
: 'Upload'}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
ModalHeader,
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils'
|
||||
@@ -89,7 +88,7 @@ export function CreateBaseModal({
|
||||
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { uploadFiles, isUploading, uploadProgress, clearError } = useKnowledgeUpload({
|
||||
const { uploadFiles, isUploading, uploadProgress, uploadError, clearError } = useKnowledgeUpload({
|
||||
workspaceId,
|
||||
onUploadComplete: (uploadedFiles) => {
|
||||
logger.info(`Successfully uploaded ${uploadedFiles.length} files`)
|
||||
@@ -280,21 +279,35 @@ export function CreateBaseModal({
|
||||
const newKnowledgeBase = result.data
|
||||
|
||||
if (files.length > 0) {
|
||||
newKnowledgeBase.docCount = files.length
|
||||
try {
|
||||
const uploadedFiles = await uploadFiles(files, newKnowledgeBase.id, {
|
||||
chunkSize: data.maxChunkSize,
|
||||
minCharactersPerChunk: data.minChunkSize,
|
||||
chunkOverlap: data.overlapSize,
|
||||
recipe: 'default',
|
||||
})
|
||||
|
||||
if (onKnowledgeBaseCreated) {
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
logger.info(`Successfully uploaded ${uploadedFiles.length} files`)
|
||||
logger.info(`Started processing ${uploadedFiles.length} documents in the background`)
|
||||
|
||||
newKnowledgeBase.docCount = uploadedFiles.length
|
||||
|
||||
if (onKnowledgeBaseCreated) {
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
}
|
||||
} catch (uploadError) {
|
||||
// If file upload fails completely, delete the knowledge base to avoid orphaned empty KB
|
||||
logger.error('File upload failed, deleting knowledge base:', uploadError)
|
||||
try {
|
||||
await fetch(`/api/knowledge/${newKnowledgeBase.id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
logger.info(`Deleted orphaned knowledge base: ${newKnowledgeBase.id}`)
|
||||
} catch (deleteError) {
|
||||
logger.error('Failed to delete orphaned knowledge base:', deleteError)
|
||||
}
|
||||
throw uploadError
|
||||
}
|
||||
|
||||
const uploadedFiles = await uploadFiles(files, newKnowledgeBase.id, {
|
||||
chunkSize: data.maxChunkSize,
|
||||
minCharactersPerChunk: data.minChunkSize,
|
||||
chunkOverlap: data.overlapSize,
|
||||
recipe: 'default',
|
||||
})
|
||||
|
||||
logger.info(`Successfully uploaded ${uploadedFiles.length} files`)
|
||||
logger.info(`Started processing ${uploadedFiles.length} documents in the background`)
|
||||
} else {
|
||||
if (onKnowledgeBaseCreated) {
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
@@ -325,14 +338,6 @@ export function CreateBaseModal({
|
||||
<ModalBody className='!pb-[16px]'>
|
||||
<div ref={scrollContainerRef} className='min-h-0 flex-1 overflow-y-auto'>
|
||||
<div className='space-y-[12px]'>
|
||||
{submitStatus && submitStatus.type === 'error' && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{submitStatus.message}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className='flex flex-col gap-[8px]'>
|
||||
<Label htmlFor='name'>Name</Label>
|
||||
<Input
|
||||
@@ -498,36 +503,39 @@ export function CreateBaseModal({
|
||||
)}
|
||||
|
||||
{fileError && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{fileError}</AlertDescription>
|
||||
</Alert>
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>{fileError}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={() => handleClose(false)}
|
||||
type='button'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='primary' type='submit' disabled={isSubmitting || !nameValue?.trim()}>
|
||||
{isSubmitting
|
||||
? isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Creating...'
|
||||
: 'Creating...'
|
||||
: 'Create'}
|
||||
</Button>
|
||||
<ModalFooter className='flex-col items-stretch gap-[12px]'>
|
||||
{(submitStatus?.type === 'error' || uploadError) && (
|
||||
<p className='text-[11px] text-[var(--text-error)] leading-tight'>
|
||||
{uploadError?.message || submitStatus?.message}
|
||||
</p>
|
||||
)}
|
||||
<div className='flex justify-end gap-[8px]'>
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={() => handleClose(false)}
|
||||
type='button'
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='primary' type='submit' disabled={isSubmitting || !nameValue?.trim()}>
|
||||
{isSubmitting
|
||||
? isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted}/${uploadProgress.totalFiles}...`
|
||||
: uploadProgress.stage === 'processing'
|
||||
? 'Processing...'
|
||||
: 'Creating...'
|
||||
: 'Creating...'
|
||||
: 'Create'}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalContent>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getFileExtension, getMimeTypeFromExtension } from '@/lib/uploads/utils/file-utils'
|
||||
|
||||
const logger = createLogger('KnowledgeUpload')
|
||||
|
||||
@@ -143,6 +144,17 @@ const calculateThroughputMbps = (bytes: number, durationMs: number) => {
|
||||
*/
|
||||
const formatDurationSeconds = (durationMs: number) => Number((durationMs / 1000).toFixed(2))
|
||||
|
||||
/**
|
||||
* Gets the content type for a file, falling back to extension-based lookup if browser doesn't provide one
|
||||
*/
|
||||
const getFileContentType = (file: File): string => {
|
||||
if (file.type?.trim()) {
|
||||
return file.type
|
||||
}
|
||||
const extension = getFileExtension(file.name)
|
||||
return getMimeTypeFromExtension(extension)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs async operations with concurrency limit
|
||||
*/
|
||||
@@ -280,7 +292,7 @@ const getPresignedData = async (
|
||||
},
|
||||
body: JSON.stringify({
|
||||
fileName: file.name,
|
||||
contentType: file.type,
|
||||
contentType: getFileContentType(file),
|
||||
fileSize: file.size,
|
||||
}),
|
||||
signal: localController.signal,
|
||||
@@ -529,7 +541,9 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
throughputMbps: calculateThroughputMbps(file.size, durationMs),
|
||||
status: xhr.status,
|
||||
})
|
||||
resolve(createUploadedFile(file.name, fullFileUrl, file.size, file.type, file))
|
||||
resolve(
|
||||
createUploadedFile(file.name, fullFileUrl, file.size, getFileContentType(file), file)
|
||||
)
|
||||
} else {
|
||||
logger.error('S3 PUT request failed', {
|
||||
status: xhr.status,
|
||||
@@ -597,7 +611,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
fileName: file.name,
|
||||
contentType: file.type,
|
||||
contentType: getFileContentType(file),
|
||||
fileSize: file.size,
|
||||
}),
|
||||
})
|
||||
@@ -736,7 +750,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
|
||||
const fullFileUrl = path.startsWith('http') ? path : `${window.location.origin}${path}`
|
||||
|
||||
return createUploadedFile(file.name, fullFileUrl, file.size, file.type, file)
|
||||
return createUploadedFile(file.name, fullFileUrl, file.size, getFileContentType(file), file)
|
||||
} catch (error) {
|
||||
logger.error(`Multipart upload failed for ${file.name}:`, error)
|
||||
const durationMs = getHighResTime() - startTime
|
||||
@@ -800,7 +814,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
file.name,
|
||||
filePath.startsWith('http') ? filePath : `${window.location.origin}${filePath}`,
|
||||
file.size,
|
||||
file.type,
|
||||
getFileContentType(file),
|
||||
file
|
||||
)
|
||||
} finally {
|
||||
@@ -855,7 +869,7 @@ export function useKnowledgeUpload(options: UseKnowledgeUploadOptions = {}) {
|
||||
const batchRequest = {
|
||||
files: batchFiles.map((file) => ({
|
||||
fileName: file.name,
|
||||
contentType: file.type,
|
||||
contentType: getFileContentType(file),
|
||||
fileSize: file.size,
|
||||
})),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { RepeatIcon, SplitIcon } from 'lucide-react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import {
|
||||
Popover,
|
||||
@@ -349,14 +351,24 @@ const getCaretViewportPosition = (
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a tag icon with background color
|
||||
* Renders a tag icon with background color - can use either a React icon component or a letter
|
||||
*/
|
||||
const TagIcon: React.FC<{ icon: string; color: string }> = ({ icon, color }) => (
|
||||
const TagIcon: React.FC<{
|
||||
icon: string | React.ComponentType<{ className?: string }>
|
||||
color: string
|
||||
}> = ({ icon, color }) => (
|
||||
<div
|
||||
className='flex h-[14px] w-[14px] flex-shrink-0 items-center justify-center rounded'
|
||||
style={{ backgroundColor: color }}
|
||||
style={{ background: color }}
|
||||
>
|
||||
<span className='font-bold text-[10px] text-white'>{icon}</span>
|
||||
{typeof icon === 'string' ? (
|
||||
<span className='font-bold text-[10px] text-white'>{icon}</span>
|
||||
) : (
|
||||
(() => {
|
||||
const IconComponent = icon
|
||||
return <IconComponent className='h-[9px] w-[9px] text-white' />
|
||||
})()
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1385,7 +1397,17 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
blockColor = BLOCK_COLORS.PARALLEL
|
||||
}
|
||||
|
||||
const tagIcon = group.blockName.charAt(0).toUpperCase()
|
||||
// Use actual block icon if available, otherwise fall back to special icons for loop/parallel or first letter
|
||||
let tagIcon: string | React.ComponentType<{ className?: string }> = group.blockName
|
||||
.charAt(0)
|
||||
.toUpperCase()
|
||||
if (blockConfig?.icon) {
|
||||
tagIcon = blockConfig.icon
|
||||
} else if (group.blockType === 'loop') {
|
||||
tagIcon = RepeatIcon
|
||||
} else if (group.blockType === 'parallel') {
|
||||
tagIcon = SplitIcon
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={group.blockId}>
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
import { TranslateIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
import {
|
||||
getAllModelProviders,
|
||||
getHostedModels,
|
||||
getProviderIcon,
|
||||
providers,
|
||||
} from '@/providers/utils'
|
||||
import { getHostedModels, getProviderIcon, providers } from '@/providers/utils'
|
||||
import { useProvidersStore } from '@/stores/providers/store'
|
||||
|
||||
const getCurrentOllamaModels = () => {
|
||||
return useProvidersStore.getState().providers.ollama.models
|
||||
}
|
||||
|
||||
const getTranslationPrompt = (
|
||||
targetLanguage: string
|
||||
) => `You are a highly skilled translator. Your task is to translate the given text into ${targetLanguage || 'English'} while:
|
||||
1. Preserving the original meaning and nuance
|
||||
2. Maintaining appropriate formality levels
|
||||
3. Adapting idioms and cultural references appropriately
|
||||
4. Preserving formatting and special characters
|
||||
5. Handling technical terms accurately
|
||||
|
||||
Only return the translated text without any explanations or notes. The translation should be natural and fluent in ${targetLanguage || 'English'}.`
|
||||
const getTranslationPrompt = (targetLanguage: string) =>
|
||||
`Translate the following text into ${targetLanguage || 'English'}. Output ONLY the translated text with no additional commentary, explanations, or notes.`
|
||||
|
||||
export const TranslateBlock: BlockConfig = {
|
||||
type: 'translate',
|
||||
@@ -123,19 +110,17 @@ export const TranslateBlock: BlockConfig = {
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['openai_chat', 'anthropic_chat', 'google_chat'],
|
||||
access: ['llm_chat'],
|
||||
config: {
|
||||
tool: (params: Record<string, any>) => {
|
||||
const model = params.model || 'gpt-4o'
|
||||
if (!model) {
|
||||
throw new Error('No model selected')
|
||||
}
|
||||
const tool = getAllModelProviders()[model]
|
||||
if (!tool) {
|
||||
throw new Error(`Invalid model selected: ${model}`)
|
||||
}
|
||||
return tool
|
||||
},
|
||||
tool: () => 'llm_chat',
|
||||
params: (params: Record<string, any>) => ({
|
||||
model: params.model,
|
||||
systemPrompt: getTranslationPrompt(params.targetLanguage || 'English'),
|
||||
context: params.context,
|
||||
apiKey: params.apiKey,
|
||||
azureEndpoint: params.azureEndpoint,
|
||||
azureApiVersion: params.azureApiVersion,
|
||||
}),
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
|
||||
@@ -272,22 +272,35 @@ export const WordPressBlock: BlockConfig<WordPressResponse> = {
|
||||
},
|
||||
},
|
||||
|
||||
// Media Operations
|
||||
// Media Operations - File upload (basic mode)
|
||||
{
|
||||
id: 'fileUpload',
|
||||
title: 'Upload File',
|
||||
type: 'file-upload',
|
||||
canonicalParamId: 'file',
|
||||
placeholder: 'Upload a media file to WordPress',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
mode: 'basic',
|
||||
multiple: false,
|
||||
required: false,
|
||||
},
|
||||
// Variable reference (advanced mode) - for referencing files from previous blocks
|
||||
{
|
||||
id: 'file',
|
||||
title: 'File',
|
||||
title: 'File Reference',
|
||||
type: 'short-input',
|
||||
placeholder: 'Base64 encoded file data or file URL',
|
||||
canonicalParamId: 'file',
|
||||
placeholder: 'Reference file from previous block (e.g., {{block_name.file}})',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
required: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'filename',
|
||||
title: 'Filename',
|
||||
title: 'Filename Override',
|
||||
type: 'short-input',
|
||||
placeholder: 'image.jpg',
|
||||
placeholder: 'Optional: Override filename (e.g., image.jpg)',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
required: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
},
|
||||
{
|
||||
id: 'mediaTitle',
|
||||
@@ -756,7 +769,7 @@ export const WordPressBlock: BlockConfig<WordPressResponse> = {
|
||||
case 'wordpress_upload_media':
|
||||
return {
|
||||
...baseParams,
|
||||
file: params.file,
|
||||
file: params.fileUpload || params.file,
|
||||
filename: params.filename,
|
||||
title: params.mediaTitle,
|
||||
caption: params.caption,
|
||||
@@ -891,8 +904,9 @@ export const WordPressBlock: BlockConfig<WordPressResponse> = {
|
||||
parent: { type: 'number', description: 'Parent page ID' },
|
||||
menuOrder: { type: 'number', description: 'Menu order' },
|
||||
// Media inputs
|
||||
file: { type: 'string', description: 'File data (base64) or URL' },
|
||||
filename: { type: 'string', description: 'Filename with extension' },
|
||||
fileUpload: { type: 'json', description: 'File to upload (UserFile object)' },
|
||||
file: { type: 'json', description: 'File reference from previous block' },
|
||||
filename: { type: 'string', description: 'Optional filename override' },
|
||||
mediaTitle: { type: 'string', description: 'Media title' },
|
||||
caption: { type: 'string', description: 'Media caption' },
|
||||
altText: { type: 'string', description: 'Alt text' },
|
||||
|
||||
@@ -26,7 +26,6 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
|
||||
{ label: 'Get Channel Videos', id: 'youtube_channel_videos' },
|
||||
{ label: 'Get Channel Playlists', id: 'youtube_channel_playlists' },
|
||||
{ label: 'Get Playlist Items', id: 'youtube_playlist_items' },
|
||||
{ label: 'Get Related Videos', id: 'youtube_related_videos' },
|
||||
{ label: 'Get Video Comments', id: 'youtube_comments' },
|
||||
],
|
||||
value: () => 'youtube_search',
|
||||
@@ -250,25 +249,6 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
|
||||
integer: true,
|
||||
condition: { field: 'operation', value: 'youtube_playlist_items' },
|
||||
},
|
||||
// Get Related Videos operation inputs
|
||||
{
|
||||
id: 'videoId',
|
||||
title: 'Video ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter YouTube video ID to find related videos',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'youtube_related_videos' },
|
||||
},
|
||||
{
|
||||
id: 'maxResults',
|
||||
title: 'Max Results',
|
||||
type: 'slider',
|
||||
min: 1,
|
||||
max: 50,
|
||||
step: 1,
|
||||
integer: true,
|
||||
condition: { field: 'operation', value: 'youtube_related_videos' },
|
||||
},
|
||||
// Get Video Comments operation inputs
|
||||
{
|
||||
id: 'videoId',
|
||||
@@ -317,7 +297,6 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
|
||||
'youtube_channel_videos',
|
||||
'youtube_channel_playlists',
|
||||
'youtube_playlist_items',
|
||||
'youtube_related_videos',
|
||||
'youtube_comments',
|
||||
],
|
||||
config: {
|
||||
@@ -340,8 +319,6 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
|
||||
return 'youtube_channel_playlists'
|
||||
case 'youtube_playlist_items':
|
||||
return 'youtube_playlist_items'
|
||||
case 'youtube_related_videos':
|
||||
return 'youtube_related_videos'
|
||||
case 'youtube_comments':
|
||||
return 'youtube_comments'
|
||||
default:
|
||||
|
||||
@@ -504,7 +504,48 @@ export const ZendeskBlock: BlockConfig = {
|
||||
subdomain: { type: 'string', description: 'Zendesk subdomain' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: { type: 'json', description: 'Operation result data' },
|
||||
// Ticket operations - list
|
||||
tickets: { type: 'json', description: 'Array of ticket objects (get_tickets)' },
|
||||
// Ticket operations - single
|
||||
ticket: {
|
||||
type: 'json',
|
||||
description: 'Single ticket object (get_ticket, create_ticket, update_ticket)',
|
||||
},
|
||||
// User operations - list
|
||||
users: { type: 'json', description: 'Array of user objects (get_users, search_users)' },
|
||||
// User operations - single
|
||||
user: {
|
||||
type: 'json',
|
||||
description: 'Single user object (get_user, get_current_user, create_user, update_user)',
|
||||
},
|
||||
// Organization operations - list
|
||||
organizations: {
|
||||
type: 'json',
|
||||
description: 'Array of organization objects (get_organizations, autocomplete_organizations)',
|
||||
},
|
||||
// Organization operations - single
|
||||
organization: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Single organization object (get_organization, create_organization, update_organization)',
|
||||
},
|
||||
// Search operations
|
||||
results: { type: 'json', description: 'Array of search result objects (search)' },
|
||||
count: { type: 'number', description: 'Number of matching results (search_count)' },
|
||||
// Bulk/async operations
|
||||
jobStatus: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Job status for async operations (create_tickets_bulk, update_tickets_bulk, merge_tickets, create_users_bulk, update_users_bulk, create_organizations_bulk)',
|
||||
},
|
||||
// Delete operations
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Deletion confirmation (delete_ticket, delete_user, delete_organization)',
|
||||
},
|
||||
// Pagination (shared across list operations)
|
||||
paging: { type: 'json', description: 'Pagination information for list operations' },
|
||||
// Metadata (shared across all operations)
|
||||
metadata: { type: 'json', description: 'Operation metadata including operation type' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -133,6 +133,11 @@ export function validateFileType(fileName: string, mimeType: string): FileValida
|
||||
|
||||
const baseMimeType = mimeType.split(';')[0].trim()
|
||||
|
||||
// Allow empty MIME types if the extension is supported (browsers often don't recognize certain file types)
|
||||
if (!baseMimeType) {
|
||||
return null
|
||||
}
|
||||
|
||||
const allowedMimeTypes = SUPPORTED_MIME_TYPES[extension]
|
||||
if (!allowedMimeTypes.includes(baseMimeType)) {
|
||||
return {
|
||||
|
||||
130
apps/sim/tools/llm/chat.ts
Normal file
130
apps/sim/tools/llm/chat.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getProviderFromModel } from '@/providers/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('LLMChatTool')
|
||||
|
||||
interface LLMChatParams {
|
||||
model: string
|
||||
systemPrompt?: string
|
||||
context: string
|
||||
apiKey?: string
|
||||
temperature?: number
|
||||
maxTokens?: number
|
||||
azureEndpoint?: string
|
||||
azureApiVersion?: string
|
||||
}
|
||||
|
||||
interface LLMChatResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
model: string
|
||||
tokens?: {
|
||||
prompt?: number
|
||||
completion?: number
|
||||
total?: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const llmChatTool: ToolConfig<LLMChatParams, LLMChatResponse> = {
|
||||
id: 'llm_chat',
|
||||
name: 'LLM Chat',
|
||||
description: 'Send a chat completion request to any supported LLM provider',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
model: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The model to use (e.g., gpt-4o, claude-sonnet-4-5, gemini-2.0-flash)',
|
||||
},
|
||||
systemPrompt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'System prompt to set the behavior of the assistant',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The user message or context to send to the model',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'API key for the provider (uses platform key if not provided for hosted models)',
|
||||
},
|
||||
temperature: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
description: 'Temperature for response generation (0-2)',
|
||||
},
|
||||
maxTokens: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
description: 'Maximum tokens in the response',
|
||||
},
|
||||
azureEndpoint: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Azure OpenAI endpoint URL',
|
||||
},
|
||||
azureApiVersion: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Azure OpenAI API version',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => '/api/providers',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const provider = getProviderFromModel(params.model)
|
||||
|
||||
return {
|
||||
provider,
|
||||
model: params.model,
|
||||
systemPrompt: params.systemPrompt,
|
||||
context: JSON.stringify([{ role: 'user', content: params.context }]),
|
||||
apiKey: params.apiKey,
|
||||
temperature: params.temperature,
|
||||
maxTokens: params.maxTokens,
|
||||
azureEndpoint: params.azureEndpoint,
|
||||
azureApiVersion: params.azureApiVersion,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage = errorData.error || `LLM API error: ${response.status}`
|
||||
logger.error('LLM chat request failed', { error: errorMessage })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: data.content,
|
||||
model: data.model,
|
||||
tokens: data.tokens,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'The generated response content' },
|
||||
model: { type: 'string', description: 'The model used for generation' },
|
||||
tokens: { type: 'object', description: 'Token usage information' },
|
||||
},
|
||||
}
|
||||
1
apps/sim/tools/llm/index.ts
Normal file
1
apps/sim/tools/llm/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { llmChatTool } from './chat'
|
||||
@@ -579,6 +579,7 @@ import {
|
||||
} from '@/tools/linear'
|
||||
import { linkedInGetProfileTool, linkedInSharePostTool } from '@/tools/linkedin'
|
||||
import { linkupSearchTool } from '@/tools/linkup'
|
||||
import { llmChatTool } from '@/tools/llm'
|
||||
import {
|
||||
mailchimpAddMemberTagsTool,
|
||||
mailchimpAddMemberTool,
|
||||
@@ -1256,7 +1257,6 @@ import {
|
||||
youtubeChannelVideosTool,
|
||||
youtubeCommentsTool,
|
||||
youtubePlaylistItemsTool,
|
||||
youtubeRelatedVideosTool,
|
||||
youtubeSearchTool,
|
||||
youtubeVideoDetailsTool,
|
||||
} from '@/tools/youtube'
|
||||
@@ -1327,6 +1327,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
openai_embeddings: openAIEmbeddingsTool,
|
||||
http_request: httpRequestTool,
|
||||
huggingface_chat: huggingfaceChatTool,
|
||||
llm_chat: llmChatTool,
|
||||
function_execute: functionExecuteTool,
|
||||
vision_tool: visionTool,
|
||||
file_parser: fileParseTool,
|
||||
@@ -1526,7 +1527,6 @@ export const tools: Record<string, ToolConfig> = {
|
||||
youtube_comments: youtubeCommentsTool,
|
||||
youtube_channel_videos: youtubeChannelVideosTool,
|
||||
youtube_channel_playlists: youtubeChannelPlaylistsTool,
|
||||
youtube_related_videos: youtubeRelatedVideosTool,
|
||||
notion_read: notionReadTool,
|
||||
notion_read_database: notionReadDatabaseTool,
|
||||
notion_write: notionWriteTool,
|
||||
|
||||
@@ -12,9 +12,9 @@ export const thinkingTool: ToolConfig<ThinkingToolParams, ThinkingToolResponse>
|
||||
thought: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'llm-only',
|
||||
description:
|
||||
'The thought process or instruction provided by the user in the Thinking Step block.',
|
||||
'Your internal reasoning, analysis, or thought process. Use this to think through the problem step by step before responding.',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -254,8 +254,8 @@ export interface WordPressListPagesResponse extends ToolResponse {
|
||||
|
||||
// Upload Media
|
||||
export interface WordPressUploadMediaParams extends WordPressBaseParams {
|
||||
file: string // Base64 encoded file data or URL
|
||||
filename: string
|
||||
file: any // UserFile object from file upload
|
||||
filename?: string // Optional filename override
|
||||
title?: string
|
||||
caption?: string
|
||||
altText?: string
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import {
|
||||
WORDPRESS_COM_API_BASE,
|
||||
type WordPressUploadMediaParams,
|
||||
type WordPressUploadMediaResponse,
|
||||
} from './types'
|
||||
import type { WordPressUploadMediaParams, WordPressUploadMediaResponse } from './types'
|
||||
|
||||
const logger = createLogger('WordPressUploadMediaTool')
|
||||
|
||||
export const uploadMediaTool: ToolConfig<WordPressUploadMediaParams, WordPressUploadMediaResponse> =
|
||||
{
|
||||
@@ -26,16 +25,16 @@ export const uploadMediaTool: ToolConfig<WordPressUploadMediaParams, WordPressUp
|
||||
description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)',
|
||||
},
|
||||
file: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Base64 encoded file data or URL to fetch file from',
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'File to upload (UserFile object)',
|
||||
},
|
||||
filename: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filename with extension (e.g., image.jpg)',
|
||||
description: 'Optional filename override (e.g., image.jpg)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
@@ -64,66 +63,37 @@ export const uploadMediaTool: ToolConfig<WordPressUploadMediaParams, WordPressUp
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${WORDPRESS_COM_API_BASE}/${params.siteId}/media`,
|
||||
url: () => '/api/tools/wordpress/upload',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
// Determine content type from filename
|
||||
const ext = params.filename.split('.').pop()?.toLowerCase() || ''
|
||||
const mimeTypes: Record<string, string> = {
|
||||
jpg: 'image/jpeg',
|
||||
jpeg: 'image/jpeg',
|
||||
png: 'image/png',
|
||||
gif: 'image/gif',
|
||||
webp: 'image/webp',
|
||||
svg: 'image/svg+xml',
|
||||
pdf: 'application/pdf',
|
||||
mp4: 'video/mp4',
|
||||
mp3: 'audio/mpeg',
|
||||
wav: 'audio/wav',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
}
|
||||
const contentType = mimeTypes[ext] || 'application/octet-stream'
|
||||
|
||||
return {
|
||||
'Content-Type': contentType,
|
||||
'Content-Disposition': `attachment; filename="${params.filename}"`,
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
// If the file is a base64 string, we need to decode it
|
||||
// The body function returns the data directly for binary uploads
|
||||
// In this case, we return the file data as-is and let the executor handle it
|
||||
return params.file as any
|
||||
},
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
siteId: params.siteId,
|
||||
file: params.file,
|
||||
filename: params.filename,
|
||||
title: params.title,
|
||||
caption: params.caption,
|
||||
altText: params.altText,
|
||||
description: params.description,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({}))
|
||||
throw new Error(error.message || `WordPress API error: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
logger.error('Failed to upload media via custom API route', {
|
||||
error: data.error,
|
||||
})
|
||||
throw new Error(data.error || 'Failed to upload media to WordPress')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
media: {
|
||||
id: data.id,
|
||||
date: data.date,
|
||||
slug: data.slug,
|
||||
type: data.type,
|
||||
link: data.link,
|
||||
title: data.title,
|
||||
caption: data.caption,
|
||||
alt_text: data.alt_text,
|
||||
media_type: data.media_type,
|
||||
mime_type: data.mime_type,
|
||||
source_url: data.source_url,
|
||||
media_details: data.media_details,
|
||||
},
|
||||
media: data.output.media,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,7 +3,6 @@ import { youtubeChannelPlaylistsTool } from '@/tools/youtube/channel_playlists'
|
||||
import { youtubeChannelVideosTool } from '@/tools/youtube/channel_videos'
|
||||
import { youtubeCommentsTool } from '@/tools/youtube/comments'
|
||||
import { youtubePlaylistItemsTool } from '@/tools/youtube/playlist_items'
|
||||
import { youtubeRelatedVideosTool } from '@/tools/youtube/related_videos'
|
||||
import { youtubeSearchTool } from '@/tools/youtube/search'
|
||||
import { youtubeVideoDetailsTool } from '@/tools/youtube/video_details'
|
||||
|
||||
@@ -14,4 +13,3 @@ export { youtubePlaylistItemsTool }
|
||||
export { youtubeCommentsTool }
|
||||
export { youtubeChannelVideosTool }
|
||||
export { youtubeChannelPlaylistsTool }
|
||||
export { youtubeRelatedVideosTool }
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type {
|
||||
YouTubeRelatedVideosParams,
|
||||
YouTubeRelatedVideosResponse,
|
||||
} from '@/tools/youtube/types'
|
||||
|
||||
export const youtubeRelatedVideosTool: ToolConfig<
|
||||
YouTubeRelatedVideosParams,
|
||||
YouTubeRelatedVideosResponse
|
||||
> = {
|
||||
id: 'youtube_related_videos',
|
||||
name: 'YouTube Related Videos',
|
||||
description: 'Find videos related to a specific YouTube video.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
videoId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'YouTube video ID to find related videos for',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
default: 10,
|
||||
description: 'Maximum number of related videos to return (1-50)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Page token for pagination',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'YouTube API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: YouTubeRelatedVideosParams) => {
|
||||
let url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&relatedToVideoId=${encodeURIComponent(
|
||||
params.videoId
|
||||
)}&key=${params.apiKey}`
|
||||
url += `&maxResults=${Number(params.maxResults || 10)}`
|
||||
if (params.pageToken) {
|
||||
url += `&pageToken=${params.pageToken}`
|
||||
}
|
||||
return url
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<YouTubeRelatedVideosResponse> => {
|
||||
const data = await response.json()
|
||||
const items = (data.items || []).map((item: any) => ({
|
||||
videoId: item.id?.videoId,
|
||||
title: item.snippet?.title,
|
||||
description: item.snippet?.description,
|
||||
thumbnail:
|
||||
item.snippet?.thumbnails?.medium?.url ||
|
||||
item.snippet?.thumbnails?.default?.url ||
|
||||
item.snippet?.thumbnails?.high?.url ||
|
||||
'',
|
||||
channelTitle: item.snippet?.channelTitle || '',
|
||||
}))
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items,
|
||||
totalResults: data.pageInfo?.totalResults || 0,
|
||||
nextPageToken: data.nextPageToken,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Array of related videos',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
videoId: { type: 'string', description: 'YouTube video ID' },
|
||||
title: { type: 'string', description: 'Video title' },
|
||||
description: { type: 'string', description: 'Video description' },
|
||||
thumbnail: { type: 'string', description: 'Video thumbnail URL' },
|
||||
channelTitle: { type: 'string', description: 'Channel name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalResults: {
|
||||
type: 'number',
|
||||
description: 'Total number of related videos available',
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for accessing the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -166,27 +166,6 @@ export interface YouTubeChannelPlaylistsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface YouTubeRelatedVideosParams {
|
||||
apiKey: string
|
||||
videoId: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
export interface YouTubeRelatedVideosResponse extends ToolResponse {
|
||||
output: {
|
||||
items: Array<{
|
||||
videoId: string
|
||||
title: string
|
||||
description: string
|
||||
thumbnail: string
|
||||
channelTitle: string
|
||||
}>
|
||||
totalResults: number
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type YouTubeResponse =
|
||||
| YouTubeSearchResponse
|
||||
| YouTubeVideoDetailsResponse
|
||||
@@ -195,4 +174,3 @@ export type YouTubeResponse =
|
||||
| YouTubeCommentsResponse
|
||||
| YouTubeChannelVideosResponse
|
||||
| YouTubeChannelPlaylistsResponse
|
||||
| YouTubeRelatedVideosResponse
|
||||
|
||||
@@ -129,16 +129,8 @@ export const zendeskAutocompleteOrganizationsTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Organizations search results',
|
||||
properties: {
|
||||
organizations: { type: 'array', description: 'Array of organization objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
organizations: { type: 'array', description: 'Array of organization objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -151,15 +151,7 @@ export const zendeskCreateOrganizationTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Created organization data',
|
||||
properties: {
|
||||
organization: { type: 'object', description: 'Created organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
organization: { type: 'object', description: 'Created organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -103,15 +103,7 @@ export const zendeskCreateOrganizationsBulkTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Bulk creation job status',
|
||||
properties: {
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -183,15 +183,7 @@ export const zendeskCreateTicketTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Created ticket data',
|
||||
properties: {
|
||||
ticket: { type: 'object', description: 'Created ticket object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
ticket: { type: 'object', description: 'Created ticket object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -104,15 +104,7 @@ export const zendeskCreateTicketsBulkTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Bulk create job status',
|
||||
properties: {
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -163,15 +163,7 @@ export const zendeskCreateUserTool: ToolConfig<ZendeskCreateUserParams, ZendeskC
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Created user data',
|
||||
properties: {
|
||||
user: { type: 'object', description: 'Created user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
user: { type: 'object', description: 'Created user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -103,15 +103,7 @@ export const zendeskCreateUsersBulkTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Bulk creation job status',
|
||||
properties: {
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface ZendeskDeleteOrganizationParams {
|
||||
export interface ZendeskDeleteOrganizationResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
organization: any
|
||||
deleted: boolean
|
||||
metadata: {
|
||||
operation: 'delete_organization'
|
||||
organizationId: string
|
||||
@@ -82,7 +82,7 @@ export const zendeskDeleteOrganizationTool: ToolConfig<
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
organization: null,
|
||||
deleted: true,
|
||||
metadata: {
|
||||
operation: 'delete_organization' as const,
|
||||
organizationId: params?.organizationId || '',
|
||||
@@ -93,15 +93,7 @@ export const zendeskDeleteOrganizationTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Deleted organization data',
|
||||
properties: {
|
||||
organization: { type: 'object', description: 'Deleted organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
deleted: { type: 'boolean', description: 'Deletion success' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -93,15 +93,7 @@ export const zendeskDeleteTicketTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Delete confirmation',
|
||||
properties: {
|
||||
deleted: { type: 'boolean', description: 'Deletion success' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
deleted: { type: 'boolean', description: 'Deletion success' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface ZendeskDeleteUserParams {
|
||||
export interface ZendeskDeleteUserResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
user: any
|
||||
deleted: boolean
|
||||
metadata: {
|
||||
operation: 'delete_user'
|
||||
userId: string
|
||||
@@ -80,7 +80,7 @@ export const zendeskDeleteUserTool: ToolConfig<ZendeskDeleteUserParams, ZendeskD
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
user: null,
|
||||
deleted: true,
|
||||
metadata: {
|
||||
operation: 'delete_user' as const,
|
||||
userId: params?.userId || '',
|
||||
@@ -91,15 +91,7 @@ export const zendeskDeleteUserTool: ToolConfig<ZendeskDeleteUserParams, ZendeskD
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Deleted user data',
|
||||
properties: {
|
||||
user: { type: 'object', description: 'Deleted user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
deleted: { type: 'boolean', description: 'Deletion success' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -85,15 +85,7 @@ export const zendeskGetCurrentUserTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Current user data',
|
||||
properties: {
|
||||
user: { type: 'object', description: 'Current user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
user: { type: 'object', description: 'Current user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -92,15 +92,7 @@ export const zendeskGetOrganizationTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Organization data',
|
||||
properties: {
|
||||
organization: { type: 'object', description: 'Organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
organization: { type: 'object', description: 'Organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -120,16 +120,8 @@ export const zendeskGetOrganizationsTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Organizations data and metadata',
|
||||
properties: {
|
||||
organizations: { type: 'array', description: 'Array of organization objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
organizations: { type: 'array', description: 'Array of organization objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -90,15 +90,7 @@ export const zendeskGetTicketTool: ToolConfig<ZendeskGetTicketParams, ZendeskGet
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Ticket data',
|
||||
properties: {
|
||||
ticket: { type: 'object', description: 'Ticket object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
ticket: { type: 'object', description: 'Ticket object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -199,16 +199,8 @@ export const zendeskGetTicketsTool: ToolConfig<ZendeskGetTicketsParams, ZendeskG
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Tickets data and metadata',
|
||||
properties: {
|
||||
tickets: { type: 'array', description: 'Array of ticket objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
tickets: { type: 'array', description: 'Array of ticket objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -89,15 +89,7 @@ export const zendeskGetUserTool: ToolConfig<ZendeskGetUserParams, ZendeskGetUser
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'User data',
|
||||
properties: {
|
||||
user: { type: 'object', description: 'User object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
user: { type: 'object', description: 'User object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -133,16 +133,8 @@ export const zendeskGetUsersTool: ToolConfig<ZendeskGetUsersParams, ZendeskGetUs
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Users data and metadata',
|
||||
properties: {
|
||||
users: { type: 'array', description: 'Array of user objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
users: { type: 'array', description: 'Array of user objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -121,15 +121,7 @@ export const zendeskMergeTicketsTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Merge job status',
|
||||
properties: {
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -141,16 +141,8 @@ export const zendeskSearchTool: ToolConfig<ZendeskSearchParams, ZendeskSearchRes
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Search results',
|
||||
properties: {
|
||||
results: { type: 'array', description: 'Array of result objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
results: { type: 'array', description: 'Array of result objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -99,15 +99,7 @@ export const zendeskSearchCountTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Search count result',
|
||||
properties: {
|
||||
count: { type: 'number', description: 'Number of matching results' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of matching results' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -136,16 +136,8 @@ export const zendeskSearchUsersTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Users search results',
|
||||
properties: {
|
||||
users: { type: 'array', description: 'Array of user objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
users: { type: 'array', description: 'Array of user objects' },
|
||||
paging: { type: 'object', description: 'Pagination information' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -157,15 +157,7 @@ export const zendeskUpdateOrganizationTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Updated organization data',
|
||||
properties: {
|
||||
organization: { type: 'object', description: 'Updated organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
organization: { type: 'object', description: 'Updated organization object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -181,15 +181,7 @@ export const zendeskUpdateTicketTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Updated ticket data',
|
||||
properties: {
|
||||
ticket: { type: 'object', description: 'Updated ticket object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
ticket: { type: 'object', description: 'Updated ticket object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -141,15 +141,7 @@ export const zendeskUpdateTicketsBulkTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Bulk update job status',
|
||||
properties: {
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -170,15 +170,7 @@ export const zendeskUpdateUserTool: ToolConfig<ZendeskUpdateUserParams, ZendeskU
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Updated user data',
|
||||
properties: {
|
||||
user: { type: 'object', description: 'Updated user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
user: { type: 'object', description: 'Updated user object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -103,15 +103,7 @@ export const zendeskUpdateUsersBulkTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: {
|
||||
type: 'object',
|
||||
description: 'Bulk update job status',
|
||||
properties: {
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
success: { type: 'boolean', description: 'Operation success' },
|
||||
},
|
||||
},
|
||||
jobStatus: { type: 'object', description: 'Job status object' },
|
||||
metadata: { type: 'object', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user