-
Notifications
You must be signed in to change notification settings - Fork 5.7k
[Components] speak_ai - add instant-hook sources and AI Chat / Find Media actions #21311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
79bc9fb
894d2f6
791e4f6
2b96972
6c13c36
0aa077b
febc7bf
f36f3a7
6669535
3712bc5
7542e70
dfbd0f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import app from "../../speak_ai.app.mjs"; | ||
|
|
||
| export default { | ||
| key: "speak_ai-find-media", | ||
| name: "Find Media", | ||
| description: "Find an existing Speak AI media item by its Media ID. [See the documentation](https://docs.speakai.co/).", | ||
| version: "0.0.1", | ||
| annotations: { | ||
| destructiveHint: false, | ||
| openWorldHint: true, | ||
| readOnlyHint: true, | ||
| }, | ||
| type: "action", | ||
| props: { | ||
| app, | ||
| folderId: { | ||
| propDefinition: [ | ||
| app, | ||
| "folderId", | ||
| ], | ||
| }, | ||
| mediaId: { | ||
| propDefinition: [ | ||
| app, | ||
| "mediaId", | ||
| ({ folderId }) => ({ | ||
| folderId, | ||
| }), | ||
| ], | ||
| }, | ||
| }, | ||
| async run({ $ }) { | ||
| const { | ||
| app, | ||
| mediaId, | ||
| } = this; | ||
|
|
||
| const results = await app.getInsights({ | ||
| $, | ||
| params: { | ||
| mediaId, | ||
| pageSize: 1, | ||
| }, | ||
| }); | ||
| const media = app.firstResult(results); | ||
|
|
||
| $.export("$summary", media | ||
| ? `Found media \`${mediaId}\`.` | ||
| : `No media found for \`${mediaId}\`.`); | ||
| return media; | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import app from "../../speak_ai.app.mjs"; | ||
|
|
||
| export default { | ||
| key: "speak_ai-run-ai-chat", | ||
| name: "Run AI Chat", | ||
| description: "Run a Speak AI Chat against a folder and/or specific media. [See the documentation](https://docs.speakai.co/).", | ||
| version: "0.0.1", | ||
| annotations: { | ||
| destructiveHint: false, | ||
| openWorldHint: true, | ||
| readOnlyHint: false, | ||
| }, | ||
| type: "action", | ||
| props: { | ||
| app, | ||
| prompt: { | ||
| propDefinition: [ | ||
| app, | ||
| "prompt", | ||
| ], | ||
| }, | ||
| folderId: { | ||
| propDefinition: [ | ||
| app, | ||
| "folderId", | ||
| ], | ||
| }, | ||
| assistantType: { | ||
| type: "string", | ||
| label: "Assistant Type", | ||
| description: "The assistant type to use for the prompt (e.g. `researcher`, `marketer`, `sales`, `general`, `recruiter`)", | ||
| optional: true, | ||
| default: "general", | ||
| }, | ||
| assistantTemplateId: { | ||
| type: "string", | ||
| label: "Assistant Template ID", | ||
| description: "Optional custom assistant template to apply", | ||
| optional: true, | ||
| }, | ||
| mediaIds: { | ||
| propDefinition: [ | ||
| app, | ||
| "mediaIds", | ||
| ], | ||
| }, | ||
| }, | ||
| async run({ $ }) { | ||
| const { | ||
| app, | ||
| prompt, | ||
| folderId, | ||
| assistantType, | ||
| assistantTemplateId, | ||
| mediaIds, | ||
| } = this; | ||
|
|
||
| const response = await app.runPrompt({ | ||
| $, | ||
| data: { | ||
| folderId, | ||
| prompt, | ||
| assistantType, | ||
| assistantTemplateId, | ||
| mediaIds: mediaIds || [], | ||
| }, | ||
| }); | ||
|
|
||
| const promptId = response?.data?.promptId; | ||
| $.export("$summary", promptId | ||
| ? `Successfully ran AI Chat \`${promptId}\`.` | ||
| : "Successfully ran AI Chat."); | ||
| return response; | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import { ConfigurationError } from "@pipedream/platform"; | ||
| import app from "../../speak_ai.app.mjs"; | ||
| import constants from "../../common/constants.mjs"; | ||
|
|
||
| export default { | ||
| props: { | ||
| app, | ||
| db: "$.service.db", | ||
| http: "$.interface.http", | ||
| }, | ||
| hooks: { | ||
| async activate() { | ||
| const { data } = await this.app.subscribeWebhook({ | ||
| data: { | ||
| callbackUrl: this.http.endpoint, | ||
| events: this.getEvents(), | ||
| }, | ||
| }); | ||
| this.setWebhookId(data.webhookId); | ||
| }, | ||
| async deactivate() { | ||
| const webhookId = this.getWebhookId(); | ||
| if (webhookId) { | ||
| await this.app.unsubscribeWebhook({ | ||
| webhookId, | ||
| }); | ||
| } | ||
| }, | ||
| }, | ||
| methods: { | ||
| setWebhookId(value) { | ||
| this.db.set(constants.WEBHOOK_ID, value); | ||
| }, | ||
| getWebhookId() { | ||
| return this.db.get(constants.WEBHOOK_ID); | ||
| }, | ||
| getEvents() { | ||
| throw new ConfigurationError("getEvents is not implemented"); | ||
| }, | ||
| getSummary(resource) { | ||
| return `New Speak AI event: ${resource.eventType || "delivery"}`; | ||
| }, | ||
| async hydrate(resource) { | ||
| return resource; | ||
| }, | ||
| generateMeta(resource, data) { | ||
| return { | ||
| id: resource.deliveryId || resource.mediaId || resource.messageId || resource.promptId, | ||
| summary: this.getSummary(resource, data), | ||
| ts: Date.parse(resource.createdAt) || Date.now(), | ||
| }; | ||
| }, | ||
|
Comment on lines
+46
to
+52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: Speak AI provides webhook functionality to support event-driven architectures, specifically for notifying your application when tasks such as transcription and analysis are complete [1][2]. To integrate with Speak AI webhooks, you must register your endpoint using the Speak AI REST API (specifically Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the shared base and the Speak AI sources that extend it
git ls-files 'components/speak_ai/**' | sed -n '1,200p'
echo '--- base outline ---'
ast-grep outline components/speak_ai/sources/common/base.mjs --view expanded
echo '--- source outlines ---'
for f in $(git ls-files 'components/speak_ai/sources/**/*.mjs'); do
echo "### $f"
ast-grep outline "$f" --view expanded
doneRepository: PipedreamHQ/pipedream Length of output: 4199 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the shared base and all Speak AI source files for generateMeta usage and timestamp/id fields
for f in components/speak_ai/sources/common/base.mjs $(git ls-files 'components/speak_ai/sources/**/*.mjs' | grep -v '^components/speak_ai/sources/common/base.mjs$'); do
echo "===== $f ====="
wc -l "$f"
rg -n "generateMeta|getSummary|getEvents|dedupe|emit|timestamp|createdAt|deliveryId|mediaId|messageId|promptId|eventTime|Date\.now" "$f" -n -C 3 || true
echo
doneRepository: PipedreamHQ/pipedream Length of output: 13438 🌐 Web query:
💡 Result: Speak AI webhooks are designed to notify external systems of specific events occurring within the Speak AI platform, most notably the media.analyzed event [1][2]. When a configured event is triggered, Speak AI sends an HTTP POST request containing a JSON payload to your specified callback URL [1]. While the exact structure of the payload can depend on the event type, standard integrations typically utilize the mediaId to retrieve comprehensive details about the analysis through the Speak AI REST API [2]. Core components frequently involved in Speak AI webhook and data integration workflows include: mediaId: A unique identifier for the processed media file, used to fetch full insight data, transcriptions, and sentiment analysis results via the API [2][3][4]. event: Specifies the type of occurrence, such as media.analyzed, which indicates that transcription and AI processing are complete [1][2]. timestamp: Included in the event data to provide the exact time the event occurred or was processed [1]. To implement a robust webhook handler, you should verify the requests using your Speak AI webhook secret to ensure they originate from the platform [5]. Because webhooks often serve as notifications, the recommended pattern for accessing full data is to use the mediaId provided in the webhook payload to make a secondary authenticated request to the Speak AI API [2]. This ensures you receive the complete, structured analysis rather than relying solely on the data contained in the initial webhook event [2][3]. For detailed API documentation regarding endpoints and specific response schemas, developers should refer to the official Speak AI documentation [6][7]. Webhooks can be registered via the API by providing your callback URL and the list of events you wish to subscribe to [2]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
# Search the Speak AI app for event-field assumptions and any mention of timestamp/id shapes
rg -n "promptId|messageId|mediaId|deliveryId|eventTime|createdAt|timestamp|generateMeta|getSummary|dedupe" components/speak_ai .github/pipedream-*.md -C 2Repository: PipedreamHQ/pipedream Length of output: 36344 🌐 Web query:
💡 Result: The available documentation for Speak AI does not explicitly define the specific payload fields promptId, messageId, or deliveryId in relation to their webhook system [1]. While Speak AI supports webhooks to send data to your platform, the official documentation does not provide a standard schema or example payload containing these specific identifiers [1]. If you are implementing webhooks with Speak AI, you should refer directly to the webhook configuration settings within your Speak AI dashboard or the internal API logs to inspect the actual JSON structure being delivered to your endpoint. Citations: 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Read the shared base and source headers to see which sources inherit generateMeta from base vs webhook
sed -n '1,120p' components/speak_ai/sources/common/base.mjs
echo '---'
sed -n '1,120p' components/speak_ai/sources/common/webhook.mjs
echo '---'
for f in \
components/speak_ai/sources/new-captions/new-captions.mjs \
components/speak_ai/sources/new-magic-prompt/new-magic-prompt.mjs \
components/speak_ai/sources/new-recording/new-recording.mjs \
components/speak_ai/sources/new-sentiment/new-sentiment.mjs \
components/speak_ai/sources/new-transcription/new-transcription.mjs \
components/speak_ai/sources/new-media-created-instant/new-media-created-instant.mjs \
components/speak_ai/sources/new-text-analyzed-instant/new-text-analyzed-instant.mjs
do
echo "--- $f ---"
sed -n '1,80p' "$f"
doneRepository: PipedreamHQ/pipedream Length of output: 12189 Use the event timestamp instead of 🤖 Prompt for AI AgentsSource: Path instructions |
||
| async processResource(resource) { | ||
| const data = await this.hydrate(resource); | ||
| this.$emit(data, this.generateMeta(resource, data)); | ||
| }, | ||
| }, | ||
| async run({ body }) { | ||
| if (!body || typeof body !== "object" || Array.isArray(body)) { | ||
| console.log("Skipping event: webhook body is missing or not a resource object."); | ||
| return; | ||
| } | ||
| await this.processResource(body); | ||
| }, | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import common from "../common/base.mjs"; | ||
| import events from "../common/events.mjs"; | ||
| import sampleEmit from "./test-event.mjs"; | ||
|
|
||
| export default { | ||
| ...common, | ||
| key: "speak_ai-new-ai-chat", | ||
| name: "New AI Chat Response (Instant)", | ||
| description: "Emit new event when a Speak AI Chat response is ready (`chat.status`). [See the documentation](https://docs.speakai.co/).", | ||
| version: "0.0.1", | ||
| type: "source", | ||
| dedupe: "unique", | ||
| methods: { | ||
| ...common.methods, | ||
| getEvents() { | ||
| return [ | ||
| events.CHAT_STATUS, | ||
| ]; | ||
| }, | ||
| getSummary(resource) { | ||
| return `New AI Chat response ${resource.messageId || resource.promptId || ""}`.trim(); | ||
| }, | ||
| async hydrate(resource) { | ||
| const res = await this.app.getPromptsHistory({ | ||
| headers: { | ||
| Accept: "application/json", | ||
| }, | ||
| params: { | ||
| pageSize: 100, | ||
| }, | ||
| }); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| const history = res?.data?.history || []; | ||
| const match = history.find((item) => | ||
| (resource.messageId && item.messageId === resource.messageId) | ||
| || (resource.promptId && item.promptId === resource.promptId)); | ||
| return match || { | ||
| promptId: resource.promptId, | ||
| messageId: resource.messageId, | ||
| prompt: resource.prompt, | ||
| answer: resource.answer, | ||
| mediaIds: resource.mediaIds, | ||
| folderId: resource.folderId, | ||
| }; | ||
| }, | ||
| }, | ||
| sampleEmit, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| export default { | ||
| "id": "64c2cfc863d1160cd6c2262a", | ||
| "promptId": "64c2cfc863d1160cd6c2262a", | ||
| "messageId": "45df1bc7b1b2", | ||
| "prompt": "What challenges is the team at Speak AI having with software sales?", | ||
| "answer": "From the text, it is mentioned that the team at Speak AI is facing several challenges with software sales, including customer segmentation, competition, and limited resources.", | ||
| "createdAt": "2023-07-27T20:12:56.645Z", | ||
| "assistantType": "General", | ||
| "mediaName": "Speak AI Office Hours", | ||
| "mediaIds": [], | ||
| "folderId": "27c6e4a90e32", | ||
| "state": "completed", | ||
| "link": "https://app.speakai.co/media/list?dialog=chat&tab=history", | ||
| "references": [ | ||
| { | ||
| "companyId": "5e21c8dd2d77242c64214816", | ||
| "createdAt": "2021-08-06T17:39:24.934Z", | ||
| "folderId": "27c6e4a90e32", | ||
| "mediaId": "a59b93b67685", | ||
| "name": "Office Hours 28", | ||
| "tags": [ | ||
| "Office Hours" | ||
| ], | ||
| "type": "video", | ||
| "userId": "5d03a9d5d4bca272e9c8cf89", | ||
| "sentences": [ | ||
| { | ||
| "speaker": "Vatsal Shah", | ||
| "startTime": "0:04:41.38", | ||
| "endTime": "0:05:26.46", | ||
| "text": "Working on the engineering side building the product and moving to the customer segmentation side.", | ||
| "link": "", | ||
| "score": 0.84037739 | ||
| } | ||
| ], | ||
| "score": 0.84037739 | ||
| } | ||
| ] | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import common from "../common/base.mjs"; | ||
| import events from "../common/events.mjs"; | ||
| import sampleEmit from "./test-event.mjs"; | ||
|
|
||
| export default { | ||
| ...common, | ||
| key: "speak_ai-new-captions", | ||
| name: "New Captions (Instant)", | ||
| description: "Emit new event with caption files (SRT or VTT) when Speak AI finishes analyzing a media file (`media.analyzed`, `media.reanalyzed`). [See the documentation](https://docs.speakai.co/).", | ||
| version: "0.0.1", | ||
| type: "source", | ||
| dedupe: "unique", | ||
| props: { | ||
| ...common.props, | ||
| fileType: { | ||
| type: "string", | ||
| label: "Caption File Type", | ||
| description: "The caption format to fetch", | ||
| options: [ | ||
| "SRT", | ||
| "VTT", | ||
| ], | ||
| default: "SRT", | ||
| }, | ||
| }, | ||
| methods: { | ||
| ...common.methods, | ||
| getEvents() { | ||
| return [ | ||
| events.MEDIA_ANALYZED, | ||
| events.MEDIA_REANALYZED, | ||
| ]; | ||
| }, | ||
| getSummary(resource) { | ||
| return `New ${this.fileType} captions for media ${resource.mediaId}`; | ||
| }, | ||
| async hydrate(resource) { | ||
| const results = await this.app.getExport({ | ||
| headers: { | ||
| Accept: "application/json", | ||
| }, | ||
| params: { | ||
| mediaId: resource.mediaId, | ||
| fileType: this.fileType.toLowerCase(), | ||
| }, | ||
| }); | ||
| return this.app.firstResult(results, resource); | ||
| }, | ||
| }, | ||
| sampleEmit, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| export default { | ||
| "id": "4f42d4179f68", | ||
| "mediaId": "4f42d4179f68", | ||
| "name": "GMT20211029-100117_Recording_1920x1080", | ||
| "description": "testing 123", | ||
| "durationInSecond": "185", | ||
| "caption": "1\n00:00:11,780 --> 00:00:13,750\nHi, that's all I just wanted to\n\n2\n00:00:15,180 --> 00:00:20,050\nwrite you a message, but it's taking\ntoo long so I think it's maybe it's\njust easier if I.\n" | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import common from "../common/base.mjs"; | ||
| import events from "../common/events.mjs"; | ||
| import sampleEmit from "./test-event.mjs"; | ||
|
|
||
| export default { | ||
| ...common, | ||
| key: "speak_ai-new-recording", | ||
| name: "New Recording Captured (Instant)", | ||
| description: "Emit new event when a recording is captured through a Speak AI embed recorder (`embed_recorder.recording_received`). [See the documentation](https://docs.speakai.co/).", | ||
| version: "0.0.1", | ||
| type: "source", | ||
| dedupe: "unique", | ||
| methods: { | ||
| ...common.methods, | ||
| getEvents() { | ||
| return [ | ||
| events.EMBED_RECORDER_RECORDING_RECEIVED, | ||
| ]; | ||
| }, | ||
| getSummary(resource) { | ||
| return `New recording captured for media ${resource.mediaId}`; | ||
| }, | ||
| async hydrate(resource) { | ||
| const results = await this.app.getInsights({ | ||
| params: { | ||
| uploadType: "recorder", | ||
| mediaId: resource.mediaId, | ||
| page: 0, | ||
| pageSize: 1, | ||
| }, | ||
| }); | ||
| return this.app.firstResult(results, resource); | ||
| }, | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }, | ||
| sampleEmit, | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.