chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import type {
|
||||
FirefliesAddToLiveMeetingParams,
|
||||
FirefliesAddToLiveMeetingResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesAddToLiveMeetingTool: ToolConfig<
|
||||
FirefliesAddToLiveMeetingParams,
|
||||
FirefliesAddToLiveMeetingResponse
|
||||
> = {
|
||||
id: 'fireflies_add_to_live_meeting',
|
||||
name: 'Fireflies Add to Live Meeting',
|
||||
description: 'Add the Fireflies.ai bot to an ongoing meeting to record and transcribe',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
meetingLink: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Valid meeting URL (Zoom, Google Meet, Microsoft Teams, etc.)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Title for the meeting (max 256 characters)',
|
||||
},
|
||||
meetingPassword: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for the meeting if required (max 32 characters)',
|
||||
},
|
||||
duration: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Meeting duration in minutes (15-120, default: 60)',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for transcription (e.g., "en", "es", "de")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
if (!params.meetingLink || !params.meetingLink.startsWith('http')) {
|
||||
throw new Error('Meeting link must be a valid HTTP/HTTPS URL')
|
||||
}
|
||||
|
||||
const variables: Record<string, unknown> = {
|
||||
meetingLink: params.meetingLink,
|
||||
}
|
||||
|
||||
if (params.title) variables.title = params.title.substring(0, 256)
|
||||
if (params.meetingPassword)
|
||||
variables.meeting_password = params.meetingPassword.substring(0, 32)
|
||||
if (params.duration) {
|
||||
const duration = Math.min(Math.max(Number(params.duration), 15), 120)
|
||||
variables.duration = duration
|
||||
}
|
||||
if (params.language) variables.language = params.language.substring(0, 5)
|
||||
|
||||
return {
|
||||
query: `
|
||||
mutation AddToLiveMeeting(
|
||||
$meetingLink: String!
|
||||
$title: String
|
||||
$meeting_password: String
|
||||
$duration: Int
|
||||
$language: String
|
||||
) {
|
||||
addToLiveMeeting(
|
||||
meeting_link: $meetingLink
|
||||
title: $title
|
||||
meeting_password: $meeting_password
|
||||
duration: $duration
|
||||
language: $language
|
||||
) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
const error = data.errors[0]
|
||||
const errorCode = error?.extensions?.code || ''
|
||||
let errorMessage = error?.message || 'Failed to add bot to meeting'
|
||||
|
||||
if (errorCode === 'too_many_requests') {
|
||||
errorMessage = 'Rate limit exceeded. This endpoint allows 3 requests per 20 minutes.'
|
||||
} else if (errorCode === 'invalid_language_code') {
|
||||
errorMessage = 'Invalid language code provided'
|
||||
} else if (errorCode === 'unsupported_platform') {
|
||||
errorMessage = 'Meeting platform is not supported'
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const result = data.data?.addToLiveMeeting
|
||||
return {
|
||||
success: result?.success ?? false,
|
||||
output: {
|
||||
success: result?.success ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the bot was successfully added to the meeting',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import type {
|
||||
FirefliesCreateBiteParams,
|
||||
FirefliesCreateBiteResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesCreateBiteTool: ToolConfig<
|
||||
FirefliesCreateBiteParams,
|
||||
FirefliesCreateBiteResponse
|
||||
> = {
|
||||
id: 'fireflies_create_bite',
|
||||
name: 'Fireflies Create Bite',
|
||||
description: 'Create a soundbite/highlight from a specific time range in a transcript',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
transcriptId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the transcript to create the bite from (e.g., "abc123def456")',
|
||||
},
|
||||
startTime: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start time of the bite in seconds',
|
||||
},
|
||||
endTime: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End time of the bite in seconds',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the bite (max 256 characters)',
|
||||
},
|
||||
mediaType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Media type: "video" or "audio"',
|
||||
},
|
||||
summary: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Summary for the bite (max 500 characters)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
if (!params.transcriptId) {
|
||||
throw new Error('Transcript ID is required')
|
||||
}
|
||||
if (params.startTime === undefined || params.endTime === undefined) {
|
||||
throw new Error('Start time and end time are required')
|
||||
}
|
||||
if (params.startTime >= params.endTime) {
|
||||
throw new Error('Start time must be less than end time')
|
||||
}
|
||||
|
||||
const variables: Record<string, unknown> = {
|
||||
transcriptId: params.transcriptId,
|
||||
startTime: Number(params.startTime),
|
||||
endTime: Number(params.endTime),
|
||||
}
|
||||
|
||||
if (params.name) variables.name = params.name.substring(0, 256)
|
||||
if (params.mediaType) variables.media_type = params.mediaType
|
||||
if (params.summary) variables.summary = params.summary.substring(0, 500)
|
||||
|
||||
return {
|
||||
query: `
|
||||
mutation CreateBite(
|
||||
$transcriptId: ID!
|
||||
$startTime: Float!
|
||||
$endTime: Float!
|
||||
$name: String
|
||||
$media_type: String
|
||||
$summary: String
|
||||
) {
|
||||
createBite(
|
||||
transcript_id: $transcriptId
|
||||
start_time: $startTime
|
||||
end_time: $endTime
|
||||
name: $name
|
||||
media_type: $media_type
|
||||
summary: $summary
|
||||
) {
|
||||
id
|
||||
name
|
||||
status
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to create bite',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const bite = data.data?.createBite
|
||||
if (!bite) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to create bite',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
bite: {
|
||||
id: bite.id,
|
||||
name: bite.name,
|
||||
status: bite.status,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
bite: {
|
||||
type: 'object',
|
||||
description: 'Created bite details',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Bite ID' },
|
||||
name: { type: 'string', description: 'Bite name' },
|
||||
status: { type: 'string', description: 'Processing status' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import type {
|
||||
FirefliesDeleteTranscriptParams,
|
||||
FirefliesDeleteTranscriptResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesDeleteTranscriptTool: ToolConfig<
|
||||
FirefliesDeleteTranscriptParams,
|
||||
FirefliesDeleteTranscriptResponse
|
||||
> = {
|
||||
id: 'fireflies_delete_transcript',
|
||||
name: 'Fireflies Delete Transcript',
|
||||
description: 'Delete a transcript from Fireflies.ai',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
transcriptId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The transcript ID to delete (e.g., "abc123def456")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
if (!params.transcriptId) {
|
||||
throw new Error('Transcript ID is required')
|
||||
}
|
||||
|
||||
return {
|
||||
query: `
|
||||
mutation DeleteTranscript($id: String!) {
|
||||
deleteTranscript(id: $id) {
|
||||
id
|
||||
title
|
||||
date
|
||||
duration
|
||||
host_email
|
||||
organizer_email
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: params.transcriptId,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to delete transcript',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const deleted = data.data?.deleteTranscript
|
||||
if (!deleted) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to delete transcript',
|
||||
output: { success: false },
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
transcript: {
|
||||
id: deleted.id,
|
||||
title: deleted.title ?? null,
|
||||
date: deleted.date ?? null,
|
||||
duration: deleted.duration ?? null,
|
||||
host_email: deleted.host_email ?? null,
|
||||
organizer_email: deleted.organizer_email ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the transcript was successfully deleted',
|
||||
},
|
||||
transcript: {
|
||||
type: 'object',
|
||||
description: 'The deleted transcript',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Transcript ID' },
|
||||
title: { type: 'string', description: 'Meeting title' },
|
||||
date: { type: 'number', description: 'Meeting timestamp' },
|
||||
duration: { type: 'number', description: 'Meeting duration' },
|
||||
host_email: { type: 'string', description: 'Host email address' },
|
||||
organizer_email: { type: 'string', description: 'Organizer email address' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
import type {
|
||||
FirefliesGetTranscriptParams,
|
||||
FirefliesGetTranscriptResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesGetTranscriptTool: ToolConfig<
|
||||
FirefliesGetTranscriptParams,
|
||||
FirefliesGetTranscriptResponse
|
||||
> = {
|
||||
id: 'fireflies_get_transcript',
|
||||
name: 'Fireflies Get Transcript',
|
||||
description:
|
||||
'Get a single transcript with full details including summary, action items, and analytics',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
transcriptId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The transcript ID to retrieve (e.g., "abc123def456")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
query: `
|
||||
query Transcript($id: String!) {
|
||||
transcript(id: $id) {
|
||||
id
|
||||
title
|
||||
date
|
||||
dateString
|
||||
duration
|
||||
privacy
|
||||
transcript_url
|
||||
audio_url
|
||||
video_url
|
||||
meeting_link
|
||||
host_email
|
||||
organizer_email
|
||||
participants
|
||||
fireflies_users
|
||||
speakers {
|
||||
id
|
||||
name
|
||||
}
|
||||
meeting_attendees {
|
||||
displayName
|
||||
email
|
||||
phoneNumber
|
||||
name
|
||||
location
|
||||
}
|
||||
sentences {
|
||||
index
|
||||
speaker_name
|
||||
speaker_id
|
||||
text
|
||||
raw_text
|
||||
start_time
|
||||
end_time
|
||||
ai_filters {
|
||||
task
|
||||
pricing
|
||||
metric
|
||||
question
|
||||
date_and_time
|
||||
sentiment
|
||||
}
|
||||
}
|
||||
summary {
|
||||
keywords
|
||||
action_items
|
||||
outline
|
||||
shorthand_bullet
|
||||
overview
|
||||
bullet_gist
|
||||
gist
|
||||
short_summary
|
||||
short_overview
|
||||
meeting_type
|
||||
topics_discussed
|
||||
}
|
||||
analytics {
|
||||
sentiments {
|
||||
negative_pct
|
||||
neutral_pct
|
||||
positive_pct
|
||||
}
|
||||
categories {
|
||||
questions
|
||||
date_times
|
||||
metrics
|
||||
tasks
|
||||
}
|
||||
speakers {
|
||||
speaker_id
|
||||
name
|
||||
duration
|
||||
word_count
|
||||
longest_monologue
|
||||
monologues_count
|
||||
filler_words
|
||||
questions
|
||||
duration_pct
|
||||
words_per_minute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: params.transcriptId,
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to fetch transcript',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const transcript = data.data?.transcript
|
||||
if (!transcript) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Transcript not found',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
transcript: {
|
||||
id: transcript.id,
|
||||
title: transcript.title,
|
||||
date: transcript.date,
|
||||
dateString: transcript.dateString,
|
||||
duration: transcript.duration,
|
||||
privacy: transcript.privacy,
|
||||
transcript_url: transcript.transcript_url,
|
||||
audio_url: transcript.audio_url,
|
||||
video_url: transcript.video_url,
|
||||
meeting_link: transcript.meeting_link,
|
||||
host_email: transcript.host_email,
|
||||
organizer_email: transcript.organizer_email,
|
||||
participants: transcript.participants,
|
||||
fireflies_users: transcript.fireflies_users,
|
||||
speakers: transcript.speakers,
|
||||
meeting_attendees: transcript.meeting_attendees,
|
||||
sentences: transcript.sentences,
|
||||
summary: transcript.summary,
|
||||
analytics: transcript.analytics,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
transcript: {
|
||||
type: 'object',
|
||||
description: 'The transcript with full details',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Transcript ID' },
|
||||
title: { type: 'string', description: 'Meeting title' },
|
||||
date: { type: 'number', description: 'Meeting timestamp' },
|
||||
duration: { type: 'number', description: 'Meeting duration in seconds' },
|
||||
transcript_url: { type: 'string', description: 'URL to view transcript' },
|
||||
audio_url: { type: 'string', description: 'URL to audio recording' },
|
||||
host_email: { type: 'string', description: 'Host email address' },
|
||||
participants: { type: 'array', description: 'List of participant emails' },
|
||||
speakers: { type: 'array', description: 'List of speakers' },
|
||||
sentences: { type: 'array', description: 'Transcript sentences' },
|
||||
summary: { type: 'object', description: 'Meeting summary and action items' },
|
||||
analytics: { type: 'object', description: 'Meeting analytics and sentiment' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { FirefliesGetUserParams, FirefliesGetUserResponse } from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesGetUserTool: ToolConfig<FirefliesGetUserParams, FirefliesGetUserResponse> = {
|
||||
id: 'fireflies_get_user',
|
||||
name: 'Fireflies Get User',
|
||||
description: 'Get user information from Fireflies.ai. Returns current user if no ID specified.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID to retrieve (e.g., "user_abc123", defaults to API key owner)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
query: `
|
||||
query User($id: String) {
|
||||
user(id: $id) {
|
||||
user_id
|
||||
name
|
||||
email
|
||||
integrations
|
||||
is_admin
|
||||
minutes_consumed
|
||||
num_transcripts
|
||||
recent_transcript
|
||||
recent_meeting
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: params.userId ? { id: params.userId } : {},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to fetch user',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const user = data.data?.user
|
||||
if (!user) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'User not found',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
user: {
|
||||
user_id: user.user_id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
integrations: user.integrations,
|
||||
is_admin: user.is_admin,
|
||||
minutes_consumed: user.minutes_consumed,
|
||||
num_transcripts: user.num_transcripts,
|
||||
recent_transcript: user.recent_transcript,
|
||||
recent_meeting: user.recent_meeting,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User information',
|
||||
properties: {
|
||||
user_id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
integrations: { type: 'array', description: 'Connected integrations' },
|
||||
is_admin: { type: 'boolean', description: 'Whether user is admin' },
|
||||
minutes_consumed: { type: 'number', description: 'Total minutes transcribed' },
|
||||
num_transcripts: { type: 'number', description: 'Number of transcripts' },
|
||||
recent_transcript: { type: 'string', description: 'Most recent transcript ID' },
|
||||
recent_meeting: { type: 'string', description: 'Most recent meeting date' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export { firefliesAddToLiveMeetingTool } from './add_to_live_meeting'
|
||||
export { firefliesCreateBiteTool } from './create_bite'
|
||||
export { firefliesDeleteTranscriptTool } from './delete_transcript'
|
||||
export { firefliesGetTranscriptTool } from './get_transcript'
|
||||
export { firefliesGetUserTool } from './get_user'
|
||||
export { firefliesListBitesTool } from './list_bites'
|
||||
export { firefliesListContactsTool } from './list_contacts'
|
||||
export { firefliesListTranscriptsTool } from './list_transcripts'
|
||||
export { firefliesListUsersTool } from './list_users'
|
||||
export * from './types'
|
||||
export { firefliesUploadAudioTool } from './upload_audio'
|
||||
@@ -0,0 +1,155 @@
|
||||
import type { FirefliesListBitesParams, FirefliesListBitesResponse } from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesListBitesTool: ToolConfig<
|
||||
FirefliesListBitesParams,
|
||||
FirefliesListBitesResponse
|
||||
> = {
|
||||
id: 'fireflies_list_bites',
|
||||
name: 'Fireflies List Bites',
|
||||
description: 'List soundbites/highlights from Fireflies.ai',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
transcriptId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter bites for a specific transcript (e.g., "abc123def456")',
|
||||
},
|
||||
mine: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return bites owned by the API key owner (default: true)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of bites to return (e.g., 10, max 50)',
|
||||
},
|
||||
skip: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of bites to skip for pagination (e.g., 0, 10, 20)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const variables: Record<string, unknown> = {
|
||||
mine: params.mine !== false,
|
||||
}
|
||||
|
||||
if (params.transcriptId) variables.transcript_id = params.transcriptId
|
||||
if (params.limit) variables.limit = Math.min(Number(params.limit), 50)
|
||||
if (params.skip) variables.skip = Number(params.skip)
|
||||
|
||||
return {
|
||||
query: `
|
||||
query Bites(
|
||||
$mine: Boolean
|
||||
$transcript_id: ID
|
||||
$limit: Int
|
||||
$skip: Int
|
||||
) {
|
||||
bites(
|
||||
mine: $mine
|
||||
transcript_id: $transcript_id
|
||||
limit: $limit
|
||||
skip: $skip
|
||||
) {
|
||||
id
|
||||
name
|
||||
transcript_id
|
||||
user_id
|
||||
start_time
|
||||
end_time
|
||||
status
|
||||
summary
|
||||
media_type
|
||||
thumbnail
|
||||
preview
|
||||
created_at
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to fetch bites',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const bites = data.data?.bites || []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
bites: bites.map(
|
||||
(b: {
|
||||
id: string
|
||||
name?: string
|
||||
transcript_id?: string
|
||||
user_id?: string
|
||||
start_time?: number
|
||||
end_time?: number
|
||||
status?: string
|
||||
summary?: string
|
||||
media_type?: string
|
||||
thumbnail?: string
|
||||
preview?: string
|
||||
created_at?: string
|
||||
}) => ({
|
||||
id: b.id,
|
||||
name: b.name,
|
||||
transcript_id: b.transcript_id,
|
||||
user_id: b.user_id,
|
||||
start_time: b.start_time,
|
||||
end_time: b.end_time,
|
||||
status: b.status,
|
||||
summary: b.summary,
|
||||
media_type: b.media_type,
|
||||
thumbnail: b.thumbnail,
|
||||
preview: b.preview,
|
||||
created_at: b.created_at,
|
||||
})
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
bites: {
|
||||
type: 'array',
|
||||
description: 'List of bites/soundbites',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import type {
|
||||
FirefliesListContactsParams,
|
||||
FirefliesListContactsResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesListContactsTool: ToolConfig<
|
||||
FirefliesListContactsParams,
|
||||
FirefliesListContactsResponse
|
||||
> = {
|
||||
id: 'fireflies_list_contacts',
|
||||
name: 'Fireflies List Contacts',
|
||||
description: 'List all contacts from your Fireflies.ai meetings',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: () => ({
|
||||
query: `
|
||||
query Contacts {
|
||||
contacts {
|
||||
email
|
||||
name
|
||||
picture
|
||||
last_meeting_date
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to fetch contacts',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const contacts = data.data?.contacts || []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
contacts: contacts.map(
|
||||
(c: { email?: string; name?: string; picture?: string; last_meeting_date?: string }) => ({
|
||||
email: c.email,
|
||||
name: c.name,
|
||||
picture: c.picture,
|
||||
last_meeting_date: c.last_meeting_date,
|
||||
})
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
contacts: {
|
||||
type: 'array',
|
||||
description: 'List of contacts from meetings',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
import type {
|
||||
FirefliesListTranscriptsParams,
|
||||
FirefliesListTranscriptsResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesListTranscriptsTool: ToolConfig<
|
||||
FirefliesListTranscriptsParams,
|
||||
FirefliesListTranscriptsResponse
|
||||
> = {
|
||||
id: 'fireflies_list_transcripts',
|
||||
name: 'Fireflies List Transcripts',
|
||||
description: 'List meeting transcripts from Fireflies.ai with optional filtering',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
keyword: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search keyword in meeting title or transcript (e.g., "quarterly review")',
|
||||
},
|
||||
fromDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter transcripts from this date (ISO 8601 format)',
|
||||
},
|
||||
toDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter transcripts until this date (ISO 8601 format)',
|
||||
},
|
||||
hostEmail: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by meeting host email',
|
||||
},
|
||||
participants: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by participant emails (comma-separated)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of transcripts to return (e.g., 10, max 50)',
|
||||
},
|
||||
skip: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of transcripts to skip for pagination (e.g., 0, 10, 20)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const variables: Record<string, unknown> = {}
|
||||
|
||||
if (params.keyword) variables.keyword = params.keyword
|
||||
if (params.fromDate) variables.fromDate = params.fromDate
|
||||
if (params.toDate) variables.toDate = params.toDate
|
||||
if (params.hostEmail) variables.host_email = params.hostEmail
|
||||
if (params.participants) {
|
||||
variables.participants = params.participants.split(',').map((p) => p.trim())
|
||||
}
|
||||
if (params.limit) variables.limit = Math.min(Number(params.limit), 50)
|
||||
if (params.skip) variables.skip = Number(params.skip)
|
||||
|
||||
return {
|
||||
query: `
|
||||
query Transcripts(
|
||||
$keyword: String
|
||||
$fromDate: DateTime
|
||||
$toDate: DateTime
|
||||
$host_email: String
|
||||
$participants: [String!]
|
||||
$limit: Int
|
||||
$skip: Int
|
||||
) {
|
||||
transcripts(
|
||||
keyword: $keyword
|
||||
fromDate: $fromDate
|
||||
toDate: $toDate
|
||||
host_email: $host_email
|
||||
participants: $participants
|
||||
limit: $limit
|
||||
skip: $skip
|
||||
) {
|
||||
id
|
||||
title
|
||||
date
|
||||
duration
|
||||
host_email
|
||||
participants
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to fetch transcripts',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const transcripts = data.data?.transcripts || []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
transcripts: transcripts.map(
|
||||
(t: {
|
||||
id: string
|
||||
title: string
|
||||
date: number
|
||||
duration: number
|
||||
host_email?: string
|
||||
participants?: string[]
|
||||
}) => ({
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
date: t.date,
|
||||
duration: t.duration,
|
||||
host_email: t.host_email,
|
||||
participants: t.participants,
|
||||
})
|
||||
),
|
||||
count: transcripts.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
transcripts: {
|
||||
type: 'array',
|
||||
description: 'List of transcripts',
|
||||
},
|
||||
count: {
|
||||
type: 'number',
|
||||
description: 'Number of transcripts returned',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import type { FirefliesListUsersParams, FirefliesListUsersResponse } from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesListUsersTool: ToolConfig<
|
||||
FirefliesListUsersParams,
|
||||
FirefliesListUsersResponse
|
||||
> = {
|
||||
id: 'fireflies_list_users',
|
||||
name: 'Fireflies List Users',
|
||||
description: 'List all users within your Fireflies.ai team',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: () => ({
|
||||
query: `
|
||||
query Users {
|
||||
users {
|
||||
user_id
|
||||
email
|
||||
name
|
||||
num_transcripts
|
||||
recent_meeting
|
||||
minutes_consumed
|
||||
is_admin
|
||||
integrations
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to fetch users',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const users = data.data?.users || []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users: users.map(
|
||||
(u: {
|
||||
user_id: string
|
||||
email: string
|
||||
name: string
|
||||
num_transcripts?: number
|
||||
recent_meeting?: string
|
||||
minutes_consumed?: number
|
||||
is_admin?: boolean
|
||||
integrations?: string[]
|
||||
}) => ({
|
||||
user_id: u.user_id,
|
||||
email: u.email,
|
||||
name: u.name,
|
||||
num_transcripts: u.num_transcripts,
|
||||
recent_meeting: u.recent_meeting,
|
||||
minutes_consumed: u.minutes_consumed,
|
||||
is_admin: u.is_admin,
|
||||
integrations: u.integrations,
|
||||
})
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
users: {
|
||||
type: 'array',
|
||||
description: 'List of team users',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
interface FirefliesTranscript {
|
||||
id: string
|
||||
title: string
|
||||
date: number
|
||||
dateString?: string
|
||||
duration: number
|
||||
privacy?: string
|
||||
transcript_url?: string
|
||||
audio_url?: string
|
||||
video_url?: string
|
||||
meeting_link?: string
|
||||
host_email?: string
|
||||
organizer_email?: string
|
||||
participants?: string[]
|
||||
fireflies_users?: string[]
|
||||
speakers?: FirefliesSpeaker[]
|
||||
meeting_attendees?: FirefliesAttendee[]
|
||||
sentences?: FirefliesSentence[]
|
||||
summary?: FirefliesSummary
|
||||
analytics?: FirefliesAnalytics
|
||||
}
|
||||
|
||||
interface FirefliesSpeaker {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface FirefliesAttendee {
|
||||
displayName?: string
|
||||
email?: string
|
||||
phoneNumber?: string
|
||||
name?: string
|
||||
location?: string
|
||||
}
|
||||
|
||||
interface FirefliesSentence {
|
||||
index: number
|
||||
speaker_name?: string
|
||||
speaker_id?: number
|
||||
text: string
|
||||
raw_text?: string
|
||||
start_time: number
|
||||
end_time: number
|
||||
ai_filters?: {
|
||||
task?: boolean
|
||||
pricing?: boolean
|
||||
metric?: boolean
|
||||
question?: boolean
|
||||
date_and_time?: boolean
|
||||
text_cleanup?: string
|
||||
sentiment?: string
|
||||
}
|
||||
}
|
||||
|
||||
interface FirefliesSummary {
|
||||
keywords?: string[]
|
||||
action_items?: string
|
||||
outline?: string
|
||||
shorthand_bullet?: string
|
||||
overview?: string
|
||||
bullet_gist?: string
|
||||
gist?: string
|
||||
short_summary?: string
|
||||
short_overview?: string
|
||||
meeting_type?: string
|
||||
topics_discussed?: string
|
||||
transcript_chapters?: Array<{
|
||||
title?: string
|
||||
start_time?: number
|
||||
end_time?: number
|
||||
}>
|
||||
}
|
||||
|
||||
interface FirefliesAnalytics {
|
||||
sentiments?: {
|
||||
negative_pct?: number
|
||||
neutral_pct?: number
|
||||
positive_pct?: number
|
||||
}
|
||||
categories?: {
|
||||
questions?: number
|
||||
date_times?: number
|
||||
metrics?: number
|
||||
tasks?: number
|
||||
}
|
||||
speakers?: FirefliesSpeakerAnalytics[]
|
||||
}
|
||||
|
||||
interface FirefliesSpeakerAnalytics {
|
||||
speaker_id?: number
|
||||
name?: string
|
||||
duration?: number
|
||||
word_count?: number
|
||||
longest_monologue?: number
|
||||
monologues_count?: number
|
||||
filler_words?: number
|
||||
questions?: number
|
||||
duration_pct?: number
|
||||
words_per_minute?: number
|
||||
}
|
||||
|
||||
interface FirefliesUser {
|
||||
user_id: string
|
||||
name: string
|
||||
email: string
|
||||
integrations?: string[]
|
||||
is_admin?: boolean
|
||||
minutes_consumed?: number
|
||||
num_transcripts?: number
|
||||
recent_transcript?: string
|
||||
recent_meeting?: string
|
||||
}
|
||||
|
||||
export interface FirefliesListTranscriptsParams {
|
||||
apiKey: string
|
||||
keyword?: string
|
||||
fromDate?: string
|
||||
toDate?: string
|
||||
hostEmail?: string
|
||||
participants?: string
|
||||
limit?: number
|
||||
skip?: number
|
||||
}
|
||||
|
||||
export interface FirefliesGetTranscriptParams {
|
||||
apiKey: string
|
||||
transcriptId: string
|
||||
}
|
||||
|
||||
export interface FirefliesGetUserParams {
|
||||
apiKey: string
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export interface FirefliesUploadAudioParams {
|
||||
apiKey: string
|
||||
audioUrl?: string
|
||||
audioFile?: {
|
||||
url?: string
|
||||
path?: string
|
||||
name?: string
|
||||
size?: number
|
||||
type?: string
|
||||
key?: string
|
||||
}
|
||||
title?: string
|
||||
webhook?: string
|
||||
language?: string
|
||||
attendees?: string
|
||||
clientReferenceId?: string
|
||||
}
|
||||
|
||||
export interface FirefliesDeleteTranscriptParams {
|
||||
apiKey: string
|
||||
transcriptId: string
|
||||
}
|
||||
|
||||
export interface FirefliesAddToLiveMeetingParams {
|
||||
apiKey: string
|
||||
meetingLink: string
|
||||
title?: string
|
||||
meetingPassword?: string
|
||||
duration?: number
|
||||
language?: string
|
||||
}
|
||||
|
||||
export interface FirefliesListUsersParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface FirefliesCreateBiteParams {
|
||||
apiKey: string
|
||||
transcriptId: string
|
||||
startTime: number
|
||||
endTime: number
|
||||
name?: string
|
||||
mediaType?: string
|
||||
summary?: string
|
||||
}
|
||||
|
||||
export interface FirefliesListBitesParams {
|
||||
apiKey: string
|
||||
transcriptId?: string
|
||||
mine?: boolean
|
||||
limit?: number
|
||||
skip?: number
|
||||
}
|
||||
|
||||
export interface FirefliesListContactsParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface FirefliesListTranscriptsResponse extends ToolResponse {
|
||||
output: {
|
||||
transcripts?: Array<{
|
||||
id: string
|
||||
title: string
|
||||
date: number
|
||||
duration: number
|
||||
host_email?: string
|
||||
participants?: string[]
|
||||
}>
|
||||
count?: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesGetTranscriptResponse extends ToolResponse {
|
||||
output: {
|
||||
transcript?: FirefliesTranscript
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesGetUserResponse extends ToolResponse {
|
||||
output: {
|
||||
user?: FirefliesUser
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesUploadAudioResponse extends ToolResponse {
|
||||
output: {
|
||||
success?: boolean
|
||||
title?: string
|
||||
message?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesDeleteTranscriptResponse extends ToolResponse {
|
||||
output: {
|
||||
success?: boolean
|
||||
transcript?: {
|
||||
id: string
|
||||
title: string | null
|
||||
date: number | null
|
||||
duration: number | null
|
||||
host_email: string | null
|
||||
organizer_email: string | null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesAddToLiveMeetingResponse extends ToolResponse {
|
||||
output: {
|
||||
success?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesListUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
users?: FirefliesUser[]
|
||||
}
|
||||
}
|
||||
|
||||
interface FirefliesBite {
|
||||
id: string
|
||||
name?: string
|
||||
transcript_id?: string
|
||||
user_id?: string
|
||||
start_time?: number
|
||||
end_time?: number
|
||||
status?: string
|
||||
summary?: string
|
||||
media_type?: string
|
||||
thumbnail?: string
|
||||
preview?: string
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface FirefliesCreateBiteResponse extends ToolResponse {
|
||||
output: {
|
||||
bite?: {
|
||||
id: string
|
||||
name?: string
|
||||
status?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FirefliesListBitesResponse extends ToolResponse {
|
||||
output: {
|
||||
bites?: FirefliesBite[]
|
||||
}
|
||||
}
|
||||
|
||||
interface FirefliesContact {
|
||||
email?: string
|
||||
name?: string
|
||||
picture?: string
|
||||
last_meeting_date?: string
|
||||
}
|
||||
|
||||
export interface FirefliesListContactsResponse extends ToolResponse {
|
||||
output: {
|
||||
contacts?: FirefliesContact[]
|
||||
}
|
||||
}
|
||||
|
||||
export type FirefliesResponse =
|
||||
| FirefliesListTranscriptsResponse
|
||||
| FirefliesGetTranscriptResponse
|
||||
| FirefliesGetUserResponse
|
||||
| FirefliesUploadAudioResponse
|
||||
| FirefliesDeleteTranscriptResponse
|
||||
| FirefliesAddToLiveMeetingResponse
|
||||
| FirefliesListUsersResponse
|
||||
| FirefliesCreateBiteResponse
|
||||
| FirefliesListBitesResponse
|
||||
| FirefliesListContactsResponse
|
||||
@@ -0,0 +1,176 @@
|
||||
import type {
|
||||
FirefliesUploadAudioParams,
|
||||
FirefliesUploadAudioResponse,
|
||||
} from '@/tools/fireflies/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const firefliesUploadAudioTool: ToolConfig<
|
||||
FirefliesUploadAudioParams,
|
||||
FirefliesUploadAudioResponse
|
||||
> = {
|
||||
id: 'fireflies_upload_audio',
|
||||
name: 'Fireflies Upload Audio',
|
||||
description: 'Upload an audio file URL to Fireflies.ai for transcription',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Fireflies API key',
|
||||
},
|
||||
audioFile: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Audio/video file to upload for transcription',
|
||||
},
|
||||
audioUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Public HTTPS URL of the audio/video file (MP3, MP4, WAV, M4A, OGG)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Title for the meeting/transcript',
|
||||
},
|
||||
webhook: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Webhook URL to notify when transcription is complete',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for transcription (e.g., "es" for Spanish, "de" for German)',
|
||||
},
|
||||
attendees: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Attendees in JSON format: [{"displayName": "Name", "email": "email@example.com"}]',
|
||||
},
|
||||
clientReferenceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom reference ID for tracking',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.fireflies.ai/graphql',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.apiKey) {
|
||||
throw new Error('Missing API key for Fireflies API request')
|
||||
}
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
let url: string | undefined
|
||||
|
||||
if (params.audioFile) {
|
||||
url = params.audioFile.url || params.audioFile.path
|
||||
}
|
||||
|
||||
if (!url && params.audioUrl) {
|
||||
url = params.audioUrl
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
throw new Error('Either an audio file or audio URL is required')
|
||||
}
|
||||
|
||||
if (!url.startsWith('https://')) {
|
||||
throw new Error('Audio URL must be a valid HTTPS URL')
|
||||
}
|
||||
|
||||
const input: Record<string, unknown> = {
|
||||
url,
|
||||
}
|
||||
|
||||
if (params.title) input.title = params.title
|
||||
if (params.webhook) input.webhook = params.webhook
|
||||
if (params.language) input.custom_language = params.language
|
||||
if (params.clientReferenceId) input.client_reference_id = params.clientReferenceId
|
||||
if (params.attendees) {
|
||||
try {
|
||||
input.attendees = JSON.parse(params.attendees)
|
||||
} catch {
|
||||
throw new Error('Invalid attendees JSON format')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
query: `
|
||||
mutation UploadAudio($input: AudioUploadInput) {
|
||||
uploadAudio(input: $input) {
|
||||
success
|
||||
title
|
||||
message
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.errors[0]?.message || 'Failed to upload audio',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const result = data.data?.uploadAudio
|
||||
if (!result) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Upload failed',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
output: {
|
||||
success: result.success,
|
||||
title: result.title,
|
||||
message: result.message,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the upload was successful',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Title of the uploaded meeting',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Status message from Fireflies',
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user