@nodetool-ai/base-nodes 0.7.0-rc.22 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/index.d.ts +61 -67
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +179 -173
- package/dist/index.js.map +1 -1
- package/dist/platforms/browser.d.ts +19 -0
- package/dist/platforms/browser.d.ts.map +1 -0
- package/dist/platforms/browser.js +29 -0
- package/dist/platforms/browser.js.map +1 -0
- package/package.json +53 -65
- package/dist/lib/audio-wav.d.ts +0 -66
- package/dist/lib/audio-wav.d.ts.map +0 -1
- package/dist/lib/audio-wav.js +0 -215
- package/dist/lib/audio-wav.js.map +0 -1
- package/dist/nodes/agent-tool-hydration.d.ts +0 -10
- package/dist/nodes/agent-tool-hydration.d.ts.map +0 -1
- package/dist/nodes/agent-tool-hydration.js +0 -43
- package/dist/nodes/agent-tool-hydration.js.map +0 -1
- package/dist/nodes/agents.d.ts +0 -262
- package/dist/nodes/agents.d.ts.map +0 -1
- package/dist/nodes/agents.js +0 -2856
- package/dist/nodes/agents.js.map +0 -1
- package/dist/nodes/anthropic.d.ts +0 -26
- package/dist/nodes/anthropic.d.ts.map +0 -1
- package/dist/nodes/anthropic.js +0 -133
- package/dist/nodes/anthropic.js.map +0 -1
- package/dist/nodes/apify.d.ts +0 -120
- package/dist/nodes/apify.d.ts.map +0 -1
- package/dist/nodes/apify.js +0 -618
- package/dist/nodes/apify.js.map +0 -1
- package/dist/nodes/audio.d.ts +0 -301
- package/dist/nodes/audio.d.ts.map +0 -1
- package/dist/nodes/audio.js +0 -1274
- package/dist/nodes/audio.js.map +0 -1
- package/dist/nodes/code-node.d.ts +0 -34
- package/dist/nodes/code-node.d.ts.map +0 -1
- package/dist/nodes/code-node.js +0 -343
- package/dist/nodes/code-node.js.map +0 -1
- package/dist/nodes/code.d.ts +0 -301
- package/dist/nodes/code.d.ts.map +0 -1
- package/dist/nodes/code.js +0 -850
- package/dist/nodes/code.js.map +0 -1
- package/dist/nodes/compare.d.ts +0 -18
- package/dist/nodes/compare.d.ts.map +0 -1
- package/dist/nodes/compare.js +0 -122
- package/dist/nodes/compare.js.map +0 -1
- package/dist/nodes/constant.d.ts +0 -315
- package/dist/nodes/constant.d.ts.map +0 -1
- package/dist/nodes/constant.js +0 -715
- package/dist/nodes/constant.js.map +0 -1
- package/dist/nodes/control.d.ts +0 -83
- package/dist/nodes/control.d.ts.map +0 -1
- package/dist/nodes/control.js +0 -212
- package/dist/nodes/control.js.map +0 -1
- package/dist/nodes/data.d.ts +0 -369
- package/dist/nodes/data.d.ts.map +0 -1
- package/dist/nodes/data.js +0 -1590
- package/dist/nodes/data.js.map +0 -1
- package/dist/nodes/document.d.ts +0 -134
- package/dist/nodes/document.d.ts.map +0 -1
- package/dist/nodes/document.js +0 -984
- package/dist/nodes/document.js.map +0 -1
- package/dist/nodes/extended-placeholders.d.ts +0 -6
- package/dist/nodes/extended-placeholders.d.ts.map +0 -1
- package/dist/nodes/extended-placeholders.js +0 -19
- package/dist/nodes/extended-placeholders.js.map +0 -1
- package/dist/nodes/gemini.d.ts +0 -110
- package/dist/nodes/gemini.d.ts.map +0 -1
- package/dist/nodes/gemini.js +0 -871
- package/dist/nodes/gemini.js.map +0 -1
- package/dist/nodes/generators.d.ts +0 -84
- package/dist/nodes/generators.d.ts.map +0 -1
- package/dist/nodes/generators.js +0 -968
- package/dist/nodes/generators.js.map +0 -1
- package/dist/nodes/image.d.ts +0 -232
- package/dist/nodes/image.d.ts.map +0 -1
- package/dist/nodes/image.js +0 -1395
- package/dist/nodes/image.js.map +0 -1
- package/dist/nodes/input.d.ts +0 -445
- package/dist/nodes/input.d.ts.map +0 -1
- package/dist/nodes/input.js +0 -1459
- package/dist/nodes/input.js.map +0 -1
- package/dist/nodes/kie-dynamic.d.ts +0 -31
- package/dist/nodes/kie-dynamic.d.ts.map +0 -1
- package/dist/nodes/kie-dynamic.js +0 -270
- package/dist/nodes/kie-dynamic.js.map +0 -1
- package/dist/nodes/lib-audio-dsp.d.ts +0 -85
- package/dist/nodes/lib-audio-dsp.d.ts.map +0 -1
- package/dist/nodes/lib-audio-dsp.js +0 -450
- package/dist/nodes/lib-audio-dsp.js.map +0 -1
- package/dist/nodes/lib-audio-effects.d.ts +0 -116
- package/dist/nodes/lib-audio-effects.d.ts.map +0 -1
- package/dist/nodes/lib-audio-effects.js +0 -905
- package/dist/nodes/lib-audio-effects.js.map +0 -1
- package/dist/nodes/lib-browser.d.ts +0 -99
- package/dist/nodes/lib-browser.d.ts.map +0 -1
- package/dist/nodes/lib-browser.js +0 -692
- package/dist/nodes/lib-browser.js.map +0 -1
- package/dist/nodes/lib-charts.d.ts +0 -20
- package/dist/nodes/lib-charts.d.ts.map +0 -1
- package/dist/nodes/lib-charts.js +0 -245
- package/dist/nodes/lib-charts.js.map +0 -1
- package/dist/nodes/lib-datetime.d.ts +0 -108
- package/dist/nodes/lib-datetime.d.ts.map +0 -1
- package/dist/nodes/lib-datetime.js +0 -421
- package/dist/nodes/lib-datetime.js.map +0 -1
- package/dist/nodes/lib-doc-convert.d.ts +0 -19
- package/dist/nodes/lib-doc-convert.d.ts.map +0 -1
- package/dist/nodes/lib-doc-convert.js +0 -257
- package/dist/nodes/lib-doc-convert.js.map +0 -1
- package/dist/nodes/lib-doc-transform.d.ts +0 -39
- package/dist/nodes/lib-doc-transform.d.ts.map +0 -1
- package/dist/nodes/lib-doc-transform.js +0 -335
- package/dist/nodes/lib-doc-transform.js.map +0 -1
- package/dist/nodes/lib-docx.d.ts +0 -106
- package/dist/nodes/lib-docx.d.ts.map +0 -1
- package/dist/nodes/lib-docx.js +0 -595
- package/dist/nodes/lib-docx.js.map +0 -1
- package/dist/nodes/lib-epub.d.ts +0 -55
- package/dist/nodes/lib-epub.d.ts.map +0 -1
- package/dist/nodes/lib-epub.js +0 -220
- package/dist/nodes/lib-epub.js.map +0 -1
- package/dist/nodes/lib-excel.d.ts +0 -74
- package/dist/nodes/lib-excel.d.ts.map +0 -1
- package/dist/nodes/lib-excel.js +0 -448
- package/dist/nodes/lib-excel.js.map +0 -1
- package/dist/nodes/lib-graphql.d.ts +0 -66
- package/dist/nodes/lib-graphql.d.ts.map +0 -1
- package/dist/nodes/lib-graphql.js +0 -333
- package/dist/nodes/lib-graphql.js.map +0 -1
- package/dist/nodes/lib-grid.d.ts +0 -27
- package/dist/nodes/lib-grid.d.ts.map +0 -1
- package/dist/nodes/lib-grid.js +0 -172
- package/dist/nodes/lib-grid.js.map +0 -1
- package/dist/nodes/lib-html-parse.d.ts +0 -108
- package/dist/nodes/lib-html-parse.d.ts.map +0 -1
- package/dist/nodes/lib-html-parse.js +0 -365
- package/dist/nodes/lib-html-parse.js.map +0 -1
- package/dist/nodes/lib-http.d.ts +0 -98
- package/dist/nodes/lib-http.d.ts.map +0 -1
- package/dist/nodes/lib-http.js +0 -267
- package/dist/nodes/lib-http.js.map +0 -1
- package/dist/nodes/lib-image-color-grading.d.ts +0 -3
- package/dist/nodes/lib-image-color-grading.d.ts.map +0 -1
- package/dist/nodes/lib-image-color-grading.js +0 -1386
- package/dist/nodes/lib-image-color-grading.js.map +0 -1
- package/dist/nodes/lib-image-draw.d.ts +0 -3
- package/dist/nodes/lib-image-draw.d.ts.map +0 -1
- package/dist/nodes/lib-image-draw.js +0 -470
- package/dist/nodes/lib-image-draw.js.map +0 -1
- package/dist/nodes/lib-image-enhance.d.ts +0 -3
- package/dist/nodes/lib-image-enhance.d.ts.map +0 -1
- package/dist/nodes/lib-image-enhance.js +0 -731
- package/dist/nodes/lib-image-enhance.js.map +0 -1
- package/dist/nodes/lib-image-filter.d.ts +0 -3
- package/dist/nodes/lib-image-filter.d.ts.map +0 -1
- package/dist/nodes/lib-image-filter.js +0 -684
- package/dist/nodes/lib-image-filter.js.map +0 -1
- package/dist/nodes/lib-image-utils.d.ts +0 -24
- package/dist/nodes/lib-image-utils.d.ts.map +0 -1
- package/dist/nodes/lib-image-utils.js +0 -150
- package/dist/nodes/lib-image-utils.js.map +0 -1
- package/dist/nodes/lib-mail.d.ts +0 -74
- package/dist/nodes/lib-mail.d.ts.map +0 -1
- package/dist/nodes/lib-mail.js +0 -452
- package/dist/nodes/lib-mail.js.map +0 -1
- package/dist/nodes/lib-markdown.d.ts +0 -70
- package/dist/nodes/lib-markdown.d.ts.map +0 -1
- package/dist/nodes/lib-markdown.js +0 -236
- package/dist/nodes/lib-markdown.js.map +0 -1
- package/dist/nodes/lib-nlp.d.ts +0 -98
- package/dist/nodes/lib-nlp.d.ts.map +0 -1
- package/dist/nodes/lib-nlp.js +0 -374
- package/dist/nodes/lib-nlp.js.map +0 -1
- package/dist/nodes/lib-notion.d.ts +0 -99
- package/dist/nodes/lib-notion.d.ts.map +0 -1
- package/dist/nodes/lib-notion.js +0 -601
- package/dist/nodes/lib-notion.js.map +0 -1
- package/dist/nodes/lib-ocr.d.ts +0 -31
- package/dist/nodes/lib-ocr.d.ts.map +0 -1
- package/dist/nodes/lib-ocr.js +0 -143
- package/dist/nodes/lib-ocr.js.map +0 -1
- package/dist/nodes/lib-os.d.ts +0 -312
- package/dist/nodes/lib-os.d.ts.map +0 -1
- package/dist/nodes/lib-os.js +0 -909
- package/dist/nodes/lib-os.js.map +0 -1
- package/dist/nodes/lib-pdf.d.ts +0 -142
- package/dist/nodes/lib-pdf.d.ts.map +0 -1
- package/dist/nodes/lib-pdf.js +0 -871
- package/dist/nodes/lib-pdf.js.map +0 -1
- package/dist/nodes/lib-pptx.d.ts +0 -29
- package/dist/nodes/lib-pptx.d.ts.map +0 -1
- package/dist/nodes/lib-pptx.js +0 -122
- package/dist/nodes/lib-pptx.js.map +0 -1
- package/dist/nodes/lib-rss.d.ts +0 -31
- package/dist/nodes/lib-rss.d.ts.map +0 -1
- package/dist/nodes/lib-rss.js +0 -163
- package/dist/nodes/lib-rss.js.map +0 -1
- package/dist/nodes/lib-s3.d.ts +0 -112
- package/dist/nodes/lib-s3.d.ts.map +0 -1
- package/dist/nodes/lib-s3.js +0 -521
- package/dist/nodes/lib-s3.js.map +0 -1
- package/dist/nodes/lib-secret.d.ts +0 -15
- package/dist/nodes/lib-secret.d.ts.map +0 -1
- package/dist/nodes/lib-secret.js +0 -42
- package/dist/nodes/lib-secret.js.map +0 -1
- package/dist/nodes/lib-sqlite.d.ts +0 -102
- package/dist/nodes/lib-sqlite.d.ts.map +0 -1
- package/dist/nodes/lib-sqlite.js +0 -532
- package/dist/nodes/lib-sqlite.js.map +0 -1
- package/dist/nodes/lib-supabase.d.ts +0 -86
- package/dist/nodes/lib-supabase.d.ts.map +0 -1
- package/dist/nodes/lib-supabase.js +0 -425
- package/dist/nodes/lib-supabase.js.map +0 -1
- package/dist/nodes/lib-svg.d.ts +0 -199
- package/dist/nodes/lib-svg.d.ts.map +0 -1
- package/dist/nodes/lib-svg.js +0 -1027
- package/dist/nodes/lib-svg.js.map +0 -1
- package/dist/nodes/lib-tensorflow.d.ts +0 -71
- package/dist/nodes/lib-tensorflow.d.ts.map +0 -1
- package/dist/nodes/lib-tensorflow.js +0 -351
- package/dist/nodes/lib-tensorflow.js.map +0 -1
- package/dist/nodes/lib-twilio.d.ts +0 -71
- package/dist/nodes/lib-twilio.d.ts.map +0 -1
- package/dist/nodes/lib-twilio.js +0 -469
- package/dist/nodes/lib-twilio.js.map +0 -1
- package/dist/nodes/lib-validate.d.ts +0 -84
- package/dist/nodes/lib-validate.d.ts.map +0 -1
- package/dist/nodes/lib-validate.js +0 -215
- package/dist/nodes/lib-validate.js.map +0 -1
- package/dist/nodes/lib-video-download.d.ts +0 -26
- package/dist/nodes/lib-video-download.d.ts.map +0 -1
- package/dist/nodes/lib-video-download.js +0 -299
- package/dist/nodes/lib-video-download.js.map +0 -1
- package/dist/nodes/messaging.d.ts +0 -88
- package/dist/nodes/messaging.d.ts.map +0 -1
- package/dist/nodes/messaging.js +0 -418
- package/dist/nodes/messaging.js.map +0 -1
- package/dist/nodes/mistral.d.ts +0 -83
- package/dist/nodes/mistral.d.ts.map +0 -1
- package/dist/nodes/mistral.js +0 -450
- package/dist/nodes/mistral.js.map +0 -1
- package/dist/nodes/model3d/analysis.d.ts +0 -13
- package/dist/nodes/model3d/analysis.d.ts.map +0 -1
- package/dist/nodes/model3d/analysis.js +0 -36
- package/dist/nodes/model3d/analysis.js.map +0 -1
- package/dist/nodes/model3d/base.d.ts +0 -11
- package/dist/nodes/model3d/base.d.ts.map +0 -1
- package/dist/nodes/model3d/base.js +0 -20
- package/dist/nodes/model3d/base.js.map +0 -1
- package/dist/nodes/model3d/boolean-ops.d.ts +0 -2
- package/dist/nodes/model3d/boolean-ops.d.ts.map +0 -1
- package/dist/nodes/model3d/boolean-ops.js +0 -172
- package/dist/nodes/model3d/boolean-ops.js.map +0 -1
- package/dist/nodes/model3d/cleanup.d.ts +0 -47
- package/dist/nodes/model3d/cleanup.d.ts.map +0 -1
- package/dist/nodes/model3d/cleanup.js +0 -136
- package/dist/nodes/model3d/cleanup.js.map +0 -1
- package/dist/nodes/model3d/convert.d.ts +0 -14
- package/dist/nodes/model3d/convert.d.ts.map +0 -1
- package/dist/nodes/model3d/convert.js +0 -64
- package/dist/nodes/model3d/convert.js.map +0 -1
- package/dist/nodes/model3d/defaults.d.ts +0 -34
- package/dist/nodes/model3d/defaults.d.ts.map +0 -1
- package/dist/nodes/model3d/defaults.js +0 -28
- package/dist/nodes/model3d/defaults.js.map +0 -1
- package/dist/nodes/model3d/document-ops.d.ts +0 -4
- package/dist/nodes/model3d/document-ops.d.ts.map +0 -1
- package/dist/nodes/model3d/document-ops.js +0 -40
- package/dist/nodes/model3d/document-ops.js.map +0 -1
- package/dist/nodes/model3d/generation.d.ts +0 -39
- package/dist/nodes/model3d/generation.d.ts.map +0 -1
- package/dist/nodes/model3d/generation.js +0 -179
- package/dist/nodes/model3d/generation.js.map +0 -1
- package/dist/nodes/model3d/glb.d.ts +0 -29
- package/dist/nodes/model3d/glb.d.ts.map +0 -1
- package/dist/nodes/model3d/glb.js +0 -255
- package/dist/nodes/model3d/glb.js.map +0 -1
- package/dist/nodes/model3d/index.d.ts +0 -20
- package/dist/nodes/model3d/index.d.ts.map +0 -1
- package/dist/nodes/model3d/index.js +0 -38
- package/dist/nodes/model3d/index.js.map +0 -1
- package/dist/nodes/model3d/io.d.ts +0 -38
- package/dist/nodes/model3d/io.d.ts.map +0 -1
- package/dist/nodes/model3d/io.js +0 -162
- package/dist/nodes/model3d/io.js.map +0 -1
- package/dist/nodes/model3d/mesh-ops.d.ts +0 -40
- package/dist/nodes/model3d/mesh-ops.d.ts.map +0 -1
- package/dist/nodes/model3d/mesh-ops.js +0 -775
- package/dist/nodes/model3d/mesh-ops.js.map +0 -1
- package/dist/nodes/model3d/nodes.d.ts +0 -2
- package/dist/nodes/model3d/nodes.d.ts.map +0 -1
- package/dist/nodes/model3d/nodes.js +0 -2
- package/dist/nodes/model3d/nodes.js.map +0 -1
- package/dist/nodes/model3d/ops.d.ts +0 -37
- package/dist/nodes/model3d/ops.d.ts.map +0 -1
- package/dist/nodes/model3d/ops.js +0 -137
- package/dist/nodes/model3d/ops.js.map +0 -1
- package/dist/nodes/model3d/transforms.d.ts +0 -49
- package/dist/nodes/model3d/transforms.d.ts.map +0 -1
- package/dist/nodes/model3d/transforms.js +0 -142
- package/dist/nodes/model3d/transforms.js.map +0 -1
- package/dist/nodes/model3d/types.d.ts +0 -81
- package/dist/nodes/model3d/types.d.ts.map +0 -1
- package/dist/nodes/model3d/types.js +0 -2
- package/dist/nodes/model3d/types.js.map +0 -1
- package/dist/nodes/model3d/utils.d.ts +0 -34
- package/dist/nodes/model3d/utils.d.ts.map +0 -1
- package/dist/nodes/model3d/utils.js +0 -185
- package/dist/nodes/model3d/utils.js.map +0 -1
- package/dist/nodes/model3d.d.ts +0 -2
- package/dist/nodes/model3d.d.ts.map +0 -1
- package/dist/nodes/model3d.js +0 -2
- package/dist/nodes/model3d.js.map +0 -1
- package/dist/nodes/openai.d.ts +0 -172
- package/dist/nodes/openai.d.ts.map +0 -1
- package/dist/nodes/openai.js +0 -1294
- package/dist/nodes/openai.js.map +0 -1
- package/dist/nodes/output.d.ts +0 -29
- package/dist/nodes/output.d.ts.map +0 -1
- package/dist/nodes/output.js +0 -87
- package/dist/nodes/output.js.map +0 -1
- package/dist/nodes/sandbox.d.ts +0 -63
- package/dist/nodes/sandbox.d.ts.map +0 -1
- package/dist/nodes/sandbox.js +0 -633
- package/dist/nodes/sandbox.js.map +0 -1
- package/dist/nodes/search.d.ts +0 -117
- package/dist/nodes/search.d.ts.map +0 -1
- package/dist/nodes/search.js +0 -607
- package/dist/nodes/search.js.map +0 -1
- package/dist/nodes/team.d.ts +0 -74
- package/dist/nodes/team.d.ts.map +0 -1
- package/dist/nodes/team.js +0 -309
- package/dist/nodes/team.js.map +0 -1
- package/dist/nodes/text-extra.d.ts +0 -633
- package/dist/nodes/text-extra.d.ts.map +0 -1
- package/dist/nodes/text-extra.js +0 -2226
- package/dist/nodes/text-extra.js.map +0 -1
- package/dist/nodes/tool-agents.d.ts +0 -334
- package/dist/nodes/tool-agents.d.ts.map +0 -1
- package/dist/nodes/tool-agents.js +0 -1861
- package/dist/nodes/tool-agents.js.map +0 -1
- package/dist/nodes/triggers.d.ts +0 -107
- package/dist/nodes/triggers.d.ts.map +0 -1
- package/dist/nodes/triggers.js +0 -706
- package/dist/nodes/triggers.js.map +0 -1
- package/dist/nodes/vector.d.ts +0 -168
- package/dist/nodes/vector.d.ts.map +0 -1
- package/dist/nodes/vector.js +0 -1121
- package/dist/nodes/vector.js.map +0 -1
- package/dist/nodes/video.d.ts +0 -387
- package/dist/nodes/video.d.ts.map +0 -1
- package/dist/nodes/video.js +0 -2383
- package/dist/nodes/video.js.map +0 -1
- package/dist/nodes/workflow.d.ts +0 -24
- package/dist/nodes/workflow.d.ts.map +0 -1
- package/dist/nodes/workflow.js +0 -146
- package/dist/nodes/workflow.js.map +0 -1
- package/dist/nodes/workspace.d.ts +0 -203
- package/dist/nodes/workspace.d.ts.map +0 -1
- package/dist/nodes/workspace.js +0 -795
- package/dist/nodes/workspace.js.map +0 -1
package/dist/nodes/openai.js
DELETED
|
@@ -1,1294 +0,0 @@
|
|
|
1
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
7
|
-
import { BaseNode, prop } from "@nodetool-ai/node-sdk";
|
|
8
|
-
const OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
9
|
-
function getApiKey(secrets) {
|
|
10
|
-
const key = secrets.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
|
|
11
|
-
if (!key)
|
|
12
|
-
throw new Error("OPENAI_API_KEY is not configured");
|
|
13
|
-
return key;
|
|
14
|
-
}
|
|
15
|
-
function authHeaders(apiKey) {
|
|
16
|
-
return {
|
|
17
|
-
Authorization: `Bearer ${apiKey}`,
|
|
18
|
-
"Content-Type": "application/json"
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// 1. Embedding
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
export class EmbeddingNode extends BaseNode {
|
|
25
|
-
static nodeType = "openai.text.Embedding";
|
|
26
|
-
static title = "Embedding";
|
|
27
|
-
static description = "Generate vector representations of text for semantic analysis.\n embeddings, similarity, search, clustering, classification\n\n Uses OpenAI's embedding models to create dense vector representations of text.\n These vectors capture semantic meaning, enabling:\n - Semantic search\n - Text clustering\n - Document classification\n - Recommendation systems\n - Anomaly detection\n - Measuring text similarity and diversity";
|
|
28
|
-
static metadataOutputTypes = {
|
|
29
|
-
output: "list"
|
|
30
|
-
};
|
|
31
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
32
|
-
static exposeAsTool = true;
|
|
33
|
-
async process() {
|
|
34
|
-
const apiKey = getApiKey(this._secrets);
|
|
35
|
-
const text = String(this.input ?? "");
|
|
36
|
-
const model = String(this.model ?? "text-embedding-3-small");
|
|
37
|
-
const chunkSize = Number(this.chunk_size ?? 4096);
|
|
38
|
-
// Chunk input text
|
|
39
|
-
const chunks = [];
|
|
40
|
-
for (let i = 0; i < text.length; i += chunkSize) {
|
|
41
|
-
chunks.push(text.slice(i, i + chunkSize));
|
|
42
|
-
}
|
|
43
|
-
if (chunks.length === 0)
|
|
44
|
-
chunks.push("");
|
|
45
|
-
const res = await fetch(`${OPENAI_API_BASE}/embeddings`, {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers: authHeaders(apiKey),
|
|
48
|
-
body: JSON.stringify({ input: chunks, model })
|
|
49
|
-
});
|
|
50
|
-
if (!res.ok) {
|
|
51
|
-
const err = await res.text();
|
|
52
|
-
throw new Error(`OpenAI Embedding API error ${res.status}: ${err}`);
|
|
53
|
-
}
|
|
54
|
-
const data = (await res.json());
|
|
55
|
-
// Average embeddings across chunks
|
|
56
|
-
const allEmbeddings = data.data.map((d) => d.embedding);
|
|
57
|
-
const dim = allEmbeddings[0]?.length ?? 0;
|
|
58
|
-
const avg = new Array(dim).fill(0);
|
|
59
|
-
for (const emb of allEmbeddings) {
|
|
60
|
-
for (let i = 0; i < dim; i++) {
|
|
61
|
-
avg[i] += emb[i] / allEmbeddings.length;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return { output: avg };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
__decorate([
|
|
68
|
-
prop({ type: "str", default: "", title: "Input" })
|
|
69
|
-
], EmbeddingNode.prototype, "input", void 0);
|
|
70
|
-
__decorate([
|
|
71
|
-
prop({
|
|
72
|
-
type: "enum",
|
|
73
|
-
default: "text-embedding-3-small",
|
|
74
|
-
title: "Model",
|
|
75
|
-
values: ["text-embedding-3-large", "text-embedding-3-small"]
|
|
76
|
-
})
|
|
77
|
-
], EmbeddingNode.prototype, "model", void 0);
|
|
78
|
-
__decorate([
|
|
79
|
-
prop({ type: "int", default: 4096, title: "Chunk Size" })
|
|
80
|
-
], EmbeddingNode.prototype, "chunk_size", void 0);
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
// 2. WebSearch
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
export class WebSearchNode extends BaseNode {
|
|
85
|
-
static nodeType = "openai.text.WebSearch";
|
|
86
|
-
static title = "Web Search";
|
|
87
|
-
static description = "🔍 OpenAI Web Search - Searches the web using OpenAI's web search capabilities.\n\n This node uses an OpenAI model equipped with web search functionality\n (like gpt-4o with search preview) to answer queries based on current web information.\n Requires an OpenAI API key.";
|
|
88
|
-
static metadataOutputTypes = {
|
|
89
|
-
output: "str"
|
|
90
|
-
};
|
|
91
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
92
|
-
async process() {
|
|
93
|
-
const apiKey = getApiKey(this._secrets);
|
|
94
|
-
const query = String(this.query ?? "");
|
|
95
|
-
if (!query)
|
|
96
|
-
throw new Error("Search query cannot be empty");
|
|
97
|
-
const res = await fetch(`${OPENAI_API_BASE}/chat/completions`, {
|
|
98
|
-
method: "POST",
|
|
99
|
-
headers: authHeaders(apiKey),
|
|
100
|
-
body: JSON.stringify({
|
|
101
|
-
model: "gpt-4o-search-preview",
|
|
102
|
-
web_search_options: {},
|
|
103
|
-
messages: [{ role: "user", content: query }]
|
|
104
|
-
})
|
|
105
|
-
});
|
|
106
|
-
if (!res.ok) {
|
|
107
|
-
const err = await res.text();
|
|
108
|
-
throw new Error(`OpenAI WebSearch API error ${res.status}: ${err}`);
|
|
109
|
-
}
|
|
110
|
-
const data = (await res.json());
|
|
111
|
-
let content;
|
|
112
|
-
if (data.choices && Array.isArray(data.choices)) {
|
|
113
|
-
const first = data.choices[0];
|
|
114
|
-
const msg = first?.message;
|
|
115
|
-
content = String(msg?.content ?? JSON.stringify(data));
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
content = JSON.stringify(data);
|
|
119
|
-
}
|
|
120
|
-
return { output: content };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
__decorate([
|
|
124
|
-
prop({
|
|
125
|
-
type: "str",
|
|
126
|
-
default: "",
|
|
127
|
-
title: "Query",
|
|
128
|
-
description: "The search query to execute."
|
|
129
|
-
})
|
|
130
|
-
], WebSearchNode.prototype, "query", void 0);
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
// 3. Moderation
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
export class ModerationNode extends BaseNode {
|
|
135
|
-
static nodeType = "openai.text.Moderation";
|
|
136
|
-
static title = "Moderation";
|
|
137
|
-
static description = "Check text content for potential policy violations using OpenAI's moderation API.\n moderation, safety, content, filter, policy, harmful, toxic\n\n Uses OpenAI's moderation models to detect potentially harmful content including:\n - Hate speech\n - Harassment\n - Self-harm content\n - Sexual content\n - Violence\n - Graphic violence\n\n Returns flagged status and category scores for comprehensive content analysis.";
|
|
138
|
-
static metadataOutputTypes = {
|
|
139
|
-
flagged: "bool",
|
|
140
|
-
categories: "dict[str, bool]",
|
|
141
|
-
category_scores: "dict[str, float]"
|
|
142
|
-
};
|
|
143
|
-
static basicFields = ["input"];
|
|
144
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
145
|
-
static exposeAsTool = true;
|
|
146
|
-
async process() {
|
|
147
|
-
const apiKey = getApiKey(this._secrets);
|
|
148
|
-
const text = String(this.input ?? "");
|
|
149
|
-
const model = String(this.model ?? "omni-moderation-latest");
|
|
150
|
-
if (!text)
|
|
151
|
-
throw new Error("Input text cannot be empty");
|
|
152
|
-
const res = await fetch(`${OPENAI_API_BASE}/moderations`, {
|
|
153
|
-
method: "POST",
|
|
154
|
-
headers: authHeaders(apiKey),
|
|
155
|
-
body: JSON.stringify({ input: text, model })
|
|
156
|
-
});
|
|
157
|
-
if (!res.ok) {
|
|
158
|
-
const err = await res.text();
|
|
159
|
-
throw new Error(`OpenAI Moderation API error ${res.status}: ${err}`);
|
|
160
|
-
}
|
|
161
|
-
const data = (await res.json());
|
|
162
|
-
const results = data.results;
|
|
163
|
-
if (results && results.length > 0) {
|
|
164
|
-
const result = results[0];
|
|
165
|
-
return {
|
|
166
|
-
flagged: result.flagged ?? false,
|
|
167
|
-
categories: result.categories ?? {},
|
|
168
|
-
category_scores: result.category_scores ?? {}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
return { flagged: false, categories: {}, category_scores: {} };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
__decorate([
|
|
175
|
-
prop({
|
|
176
|
-
type: "str",
|
|
177
|
-
default: "",
|
|
178
|
-
title: "Input",
|
|
179
|
-
description: "The text content to check for policy violations."
|
|
180
|
-
})
|
|
181
|
-
], ModerationNode.prototype, "input", void 0);
|
|
182
|
-
__decorate([
|
|
183
|
-
prop({
|
|
184
|
-
type: "enum",
|
|
185
|
-
default: "omni-moderation-latest",
|
|
186
|
-
title: "Model",
|
|
187
|
-
description: "The moderation model to use.",
|
|
188
|
-
values: [
|
|
189
|
-
"omni-moderation-latest",
|
|
190
|
-
"omni-moderation-2024-09-26",
|
|
191
|
-
"text-moderation-latest",
|
|
192
|
-
"text-moderation-stable"
|
|
193
|
-
]
|
|
194
|
-
})
|
|
195
|
-
], ModerationNode.prototype, "model", void 0);
|
|
196
|
-
// ---------------------------------------------------------------------------
|
|
197
|
-
// 4. CreateImage
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
export class CreateImageNode extends BaseNode {
|
|
200
|
-
static nodeType = "openai.image.CreateImage";
|
|
201
|
-
static title = "Create Image";
|
|
202
|
-
static description = "Generates images from textual descriptions.\n image, t2i, tti, text-to-image, create, generate, picture, photo, art, drawing, illustration";
|
|
203
|
-
static metadataOutputTypes = {
|
|
204
|
-
output: "image"
|
|
205
|
-
};
|
|
206
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
207
|
-
static exposeAsTool = true;
|
|
208
|
-
static autoSaveAsset = true;
|
|
209
|
-
async process() {
|
|
210
|
-
const apiKey = getApiKey(this._secrets);
|
|
211
|
-
const prompt = String(this.prompt ?? "");
|
|
212
|
-
if (!prompt)
|
|
213
|
-
throw new Error("Prompt cannot be empty");
|
|
214
|
-
const model = String(this.model ?? "gpt-image-1");
|
|
215
|
-
const size = String(this.size ?? "1024x1024");
|
|
216
|
-
const quality = String(this.quality ?? "high");
|
|
217
|
-
const background = String(this.background ?? "auto");
|
|
218
|
-
const res = await fetch(`${OPENAI_API_BASE}/images/generations`, {
|
|
219
|
-
method: "POST",
|
|
220
|
-
headers: authHeaders(apiKey),
|
|
221
|
-
body: JSON.stringify({
|
|
222
|
-
prompt,
|
|
223
|
-
model,
|
|
224
|
-
n: 1,
|
|
225
|
-
size,
|
|
226
|
-
quality,
|
|
227
|
-
background,
|
|
228
|
-
response_format: "b64_json"
|
|
229
|
-
})
|
|
230
|
-
});
|
|
231
|
-
if (!res.ok) {
|
|
232
|
-
const err = await res.text();
|
|
233
|
-
throw new Error(`OpenAI CreateImage API error ${res.status}: ${err}`);
|
|
234
|
-
}
|
|
235
|
-
const data = (await res.json());
|
|
236
|
-
const item = data.data[0];
|
|
237
|
-
if (item.b64_json) {
|
|
238
|
-
return { output: { type: "image", data: `data:image/png;base64,${item.b64_json}` } };
|
|
239
|
-
}
|
|
240
|
-
else if (item.url) {
|
|
241
|
-
return { output: { type: "image", uri: item.url } };
|
|
242
|
-
}
|
|
243
|
-
throw new Error("No image data in response");
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
__decorate([
|
|
247
|
-
prop({
|
|
248
|
-
type: "str",
|
|
249
|
-
default: "",
|
|
250
|
-
title: "Prompt",
|
|
251
|
-
description: "The prompt to use."
|
|
252
|
-
})
|
|
253
|
-
], CreateImageNode.prototype, "prompt", void 0);
|
|
254
|
-
__decorate([
|
|
255
|
-
prop({
|
|
256
|
-
type: "enum",
|
|
257
|
-
default: "gpt-image-1",
|
|
258
|
-
title: "Model",
|
|
259
|
-
description: "The model to use for image generation.",
|
|
260
|
-
values: ["gpt-image-1"]
|
|
261
|
-
})
|
|
262
|
-
], CreateImageNode.prototype, "model", void 0);
|
|
263
|
-
__decorate([
|
|
264
|
-
prop({
|
|
265
|
-
type: "enum",
|
|
266
|
-
default: "1024x1024",
|
|
267
|
-
title: "Size",
|
|
268
|
-
description: "The size of the image to generate.",
|
|
269
|
-
values: ["1024x1024", "1536x1024", "1024x1536"]
|
|
270
|
-
})
|
|
271
|
-
], CreateImageNode.prototype, "size", void 0);
|
|
272
|
-
__decorate([
|
|
273
|
-
prop({
|
|
274
|
-
type: "enum",
|
|
275
|
-
default: "auto",
|
|
276
|
-
title: "Background",
|
|
277
|
-
description: "The background of the image to generate.",
|
|
278
|
-
values: ["transparent", "opaque", "auto"]
|
|
279
|
-
})
|
|
280
|
-
], CreateImageNode.prototype, "background", void 0);
|
|
281
|
-
__decorate([
|
|
282
|
-
prop({
|
|
283
|
-
type: "enum",
|
|
284
|
-
default: "high",
|
|
285
|
-
title: "Quality",
|
|
286
|
-
description: "The quality of the image to generate.",
|
|
287
|
-
values: ["high", "medium", "low"]
|
|
288
|
-
})
|
|
289
|
-
], CreateImageNode.prototype, "quality", void 0);
|
|
290
|
-
// ---------------------------------------------------------------------------
|
|
291
|
-
// 5. EditImage
|
|
292
|
-
// ---------------------------------------------------------------------------
|
|
293
|
-
export class EditImageNode extends BaseNode {
|
|
294
|
-
static nodeType = "openai.image.EditImage";
|
|
295
|
-
static title = "Edit Image";
|
|
296
|
-
static description = "Edit images using OpenAI's gpt-image-1 model.\n image, edit, modify, transform, inpaint, outpaint, variation\n\n Takes an input image and a text prompt to generate a modified version.\n Can be used for inpainting, outpainting, style transfer, and image modification.\n Optionally accepts a mask to specify which areas to edit.";
|
|
297
|
-
static metadataOutputTypes = {
|
|
298
|
-
output: "image"
|
|
299
|
-
};
|
|
300
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
301
|
-
static exposeAsTool = true;
|
|
302
|
-
static autoSaveAsset = true;
|
|
303
|
-
async process() {
|
|
304
|
-
const apiKey = getApiKey(this._secrets);
|
|
305
|
-
const prompt = String(this.prompt ?? "");
|
|
306
|
-
if (!prompt)
|
|
307
|
-
throw new Error("Edit prompt cannot be empty");
|
|
308
|
-
const image = this.image;
|
|
309
|
-
if (!image || (!image.data && !image.uri)) {
|
|
310
|
-
throw new Error("Input image is required");
|
|
311
|
-
}
|
|
312
|
-
const model = String(this.model ?? "gpt-image-1");
|
|
313
|
-
const size = String(this.size ?? "1024x1024");
|
|
314
|
-
const quality = String(this.quality ?? "high");
|
|
315
|
-
// Build multipart form data
|
|
316
|
-
const formData = new FormData();
|
|
317
|
-
formData.append("prompt", prompt);
|
|
318
|
-
formData.append("model", model);
|
|
319
|
-
formData.append("size", size);
|
|
320
|
-
formData.append("quality", quality);
|
|
321
|
-
formData.append("response_format", "b64_json");
|
|
322
|
-
// Convert image ref to blob
|
|
323
|
-
const imageBlob = await refToBlob(image);
|
|
324
|
-
formData.append("image", imageBlob, "image.png");
|
|
325
|
-
// Optional mask
|
|
326
|
-
const mask = this.mask;
|
|
327
|
-
if (mask && (mask.data || mask.uri)) {
|
|
328
|
-
const maskBlob = await refToBlob(mask);
|
|
329
|
-
formData.append("mask", maskBlob, "mask.png");
|
|
330
|
-
}
|
|
331
|
-
const res = await fetch(`${OPENAI_API_BASE}/images/edits`, {
|
|
332
|
-
method: "POST",
|
|
333
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
334
|
-
body: formData
|
|
335
|
-
});
|
|
336
|
-
if (!res.ok) {
|
|
337
|
-
const err = await res.text();
|
|
338
|
-
throw new Error(`OpenAI EditImage API error ${res.status}: ${err}`);
|
|
339
|
-
}
|
|
340
|
-
const data = (await res.json());
|
|
341
|
-
const item = data.data[0];
|
|
342
|
-
if (item.b64_json) {
|
|
343
|
-
return { output: { type: "image", data: `data:image/png;base64,${item.b64_json}` } };
|
|
344
|
-
}
|
|
345
|
-
else if (item.url) {
|
|
346
|
-
return { output: { type: "image", uri: item.url } };
|
|
347
|
-
}
|
|
348
|
-
throw new Error("No image data in response");
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
__decorate([
|
|
352
|
-
prop({
|
|
353
|
-
type: "image",
|
|
354
|
-
default: {
|
|
355
|
-
type: "image",
|
|
356
|
-
uri: "",
|
|
357
|
-
asset_id: null,
|
|
358
|
-
data: null,
|
|
359
|
-
metadata: null
|
|
360
|
-
},
|
|
361
|
-
title: "Image",
|
|
362
|
-
description: "The image to edit."
|
|
363
|
-
})
|
|
364
|
-
], EditImageNode.prototype, "image", void 0);
|
|
365
|
-
__decorate([
|
|
366
|
-
prop({
|
|
367
|
-
type: "image",
|
|
368
|
-
default: {
|
|
369
|
-
type: "image",
|
|
370
|
-
uri: "",
|
|
371
|
-
asset_id: null,
|
|
372
|
-
data: null,
|
|
373
|
-
metadata: null
|
|
374
|
-
},
|
|
375
|
-
title: "Mask",
|
|
376
|
-
description: "Optional mask image. White areas will be edited, black areas preserved."
|
|
377
|
-
})
|
|
378
|
-
], EditImageNode.prototype, "mask", void 0);
|
|
379
|
-
__decorate([
|
|
380
|
-
prop({
|
|
381
|
-
type: "str",
|
|
382
|
-
default: "",
|
|
383
|
-
title: "Prompt",
|
|
384
|
-
description: "The prompt describing the desired edit."
|
|
385
|
-
})
|
|
386
|
-
], EditImageNode.prototype, "prompt", void 0);
|
|
387
|
-
__decorate([
|
|
388
|
-
prop({
|
|
389
|
-
type: "enum",
|
|
390
|
-
default: "gpt-image-1",
|
|
391
|
-
title: "Model",
|
|
392
|
-
description: "The model to use for image editing.",
|
|
393
|
-
values: ["gpt-image-1"]
|
|
394
|
-
})
|
|
395
|
-
], EditImageNode.prototype, "model", void 0);
|
|
396
|
-
__decorate([
|
|
397
|
-
prop({
|
|
398
|
-
type: "enum",
|
|
399
|
-
default: "1024x1024",
|
|
400
|
-
title: "Size",
|
|
401
|
-
description: "The size of the output image.",
|
|
402
|
-
values: ["1024x1024", "1536x1024", "1024x1536"]
|
|
403
|
-
})
|
|
404
|
-
], EditImageNode.prototype, "size", void 0);
|
|
405
|
-
__decorate([
|
|
406
|
-
prop({
|
|
407
|
-
type: "enum",
|
|
408
|
-
default: "high",
|
|
409
|
-
title: "Quality",
|
|
410
|
-
description: "The quality of the generated image.",
|
|
411
|
-
values: ["high", "medium", "low"]
|
|
412
|
-
})
|
|
413
|
-
], EditImageNode.prototype, "quality", void 0);
|
|
414
|
-
/** Convert an image/audio ref object to a Blob for multipart upload. */
|
|
415
|
-
async function refToBlob(ref) {
|
|
416
|
-
if (ref.data && typeof ref.data === "string") {
|
|
417
|
-
const dataStr = ref.data;
|
|
418
|
-
// Handle data: URI
|
|
419
|
-
if (dataStr.startsWith("data:")) {
|
|
420
|
-
const commaIdx = dataStr.indexOf(",");
|
|
421
|
-
const b64 = dataStr.slice(commaIdx + 1);
|
|
422
|
-
const buf = Buffer.from(b64, "base64");
|
|
423
|
-
return new Blob([buf]);
|
|
424
|
-
}
|
|
425
|
-
// Assume raw base64
|
|
426
|
-
const buf = Buffer.from(dataStr, "base64");
|
|
427
|
-
return new Blob([buf]);
|
|
428
|
-
}
|
|
429
|
-
if (ref.uri && typeof ref.uri === "string") {
|
|
430
|
-
const r = await fetch(ref.uri);
|
|
431
|
-
return await r.blob();
|
|
432
|
-
}
|
|
433
|
-
throw new Error("Cannot convert ref to blob: no data or uri");
|
|
434
|
-
}
|
|
435
|
-
// ---------------------------------------------------------------------------
|
|
436
|
-
// 6. TextToSpeech
|
|
437
|
-
// ---------------------------------------------------------------------------
|
|
438
|
-
export class TextToSpeechNode extends BaseNode {
|
|
439
|
-
static nodeType = "openai.audio.TextToSpeech";
|
|
440
|
-
static title = "Text To Speech";
|
|
441
|
-
static description = "Converts text to speech using OpenAI TTS models.\n audio, tts, text-to-speech, voice, synthesis";
|
|
442
|
-
static metadataOutputTypes = {
|
|
443
|
-
output: "audio"
|
|
444
|
-
};
|
|
445
|
-
static basicFields = ["input", "model", "voice"];
|
|
446
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
447
|
-
static autoSaveAsset = true;
|
|
448
|
-
static exposeAsTool = true;
|
|
449
|
-
async process() {
|
|
450
|
-
const apiKey = getApiKey(this._secrets);
|
|
451
|
-
const text = String(this.input ?? "");
|
|
452
|
-
const model = String(this.model ?? "tts-1");
|
|
453
|
-
const voice = String(this.voice ?? "alloy");
|
|
454
|
-
const speed = Number(this.speed ?? 1.0);
|
|
455
|
-
const res = await fetch(`${OPENAI_API_BASE}/audio/speech`, {
|
|
456
|
-
method: "POST",
|
|
457
|
-
headers: authHeaders(apiKey),
|
|
458
|
-
body: JSON.stringify({
|
|
459
|
-
model,
|
|
460
|
-
input: text,
|
|
461
|
-
voice,
|
|
462
|
-
speed,
|
|
463
|
-
response_format: "mp3"
|
|
464
|
-
})
|
|
465
|
-
});
|
|
466
|
-
if (!res.ok) {
|
|
467
|
-
const err = await res.text();
|
|
468
|
-
throw new Error(`OpenAI TTS API error ${res.status}: ${err}`);
|
|
469
|
-
}
|
|
470
|
-
const arrayBuf = await res.arrayBuffer();
|
|
471
|
-
const b64 = Buffer.from(arrayBuf).toString("base64");
|
|
472
|
-
return { output: { type: "audio", data: `data:audio/mp3;base64,${b64}` } };
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
__decorate([
|
|
476
|
-
prop({
|
|
477
|
-
type: "enum",
|
|
478
|
-
default: "tts-1",
|
|
479
|
-
title: "Model",
|
|
480
|
-
values: ["tts-1", "tts-1-hd", "gpt-4o-mini-tts"]
|
|
481
|
-
})
|
|
482
|
-
], TextToSpeechNode.prototype, "model", void 0);
|
|
483
|
-
__decorate([
|
|
484
|
-
prop({
|
|
485
|
-
type: "enum",
|
|
486
|
-
default: "alloy",
|
|
487
|
-
title: "Voice",
|
|
488
|
-
values: [
|
|
489
|
-
"alloy",
|
|
490
|
-
"ash",
|
|
491
|
-
"ballad",
|
|
492
|
-
"coral",
|
|
493
|
-
"echo",
|
|
494
|
-
"fable",
|
|
495
|
-
"onyx",
|
|
496
|
-
"nova",
|
|
497
|
-
"sage",
|
|
498
|
-
"shimmer",
|
|
499
|
-
"verse"
|
|
500
|
-
]
|
|
501
|
-
})
|
|
502
|
-
], TextToSpeechNode.prototype, "voice", void 0);
|
|
503
|
-
__decorate([
|
|
504
|
-
prop({ type: "str", default: "", title: "Input" })
|
|
505
|
-
], TextToSpeechNode.prototype, "input", void 0);
|
|
506
|
-
__decorate([
|
|
507
|
-
prop({ type: "float", default: 1, title: "Speed", min: 0.25, max: 4 })
|
|
508
|
-
], TextToSpeechNode.prototype, "speed", void 0);
|
|
509
|
-
// ---------------------------------------------------------------------------
|
|
510
|
-
// 7. Translate
|
|
511
|
-
// ---------------------------------------------------------------------------
|
|
512
|
-
export class TranslateNode extends BaseNode {
|
|
513
|
-
static nodeType = "openai.audio.Translate";
|
|
514
|
-
static title = "Translate";
|
|
515
|
-
static description = "Translates speech in audio to English text.\n audio, translation, speech-to-text, localization";
|
|
516
|
-
static metadataOutputTypes = {
|
|
517
|
-
output: "str"
|
|
518
|
-
};
|
|
519
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
520
|
-
static exposeAsTool = true;
|
|
521
|
-
async process() {
|
|
522
|
-
const apiKey = getApiKey(this._secrets);
|
|
523
|
-
const audio = this.audio;
|
|
524
|
-
if (!audio || (!audio.data && !audio.uri)) {
|
|
525
|
-
throw new Error("Audio input is required");
|
|
526
|
-
}
|
|
527
|
-
const temperature = Number(this.temperature ?? 0.0);
|
|
528
|
-
const audioBlob = await refToBlob(audio);
|
|
529
|
-
const formData = new FormData();
|
|
530
|
-
formData.append("file", audioBlob, "audio.mp3");
|
|
531
|
-
formData.append("model", "whisper-1");
|
|
532
|
-
formData.append("temperature", String(temperature));
|
|
533
|
-
const res = await fetch(`${OPENAI_API_BASE}/audio/translations`, {
|
|
534
|
-
method: "POST",
|
|
535
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
536
|
-
body: formData
|
|
537
|
-
});
|
|
538
|
-
if (!res.ok) {
|
|
539
|
-
const err = await res.text();
|
|
540
|
-
throw new Error(`OpenAI Translate API error ${res.status}: ${err}`);
|
|
541
|
-
}
|
|
542
|
-
const data = (await res.json());
|
|
543
|
-
return { output: data.text };
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
__decorate([
|
|
547
|
-
prop({
|
|
548
|
-
type: "audio",
|
|
549
|
-
default: {
|
|
550
|
-
type: "audio",
|
|
551
|
-
uri: "",
|
|
552
|
-
asset_id: null,
|
|
553
|
-
data: null,
|
|
554
|
-
metadata: null
|
|
555
|
-
},
|
|
556
|
-
title: "Audio",
|
|
557
|
-
description: "The audio file to translate."
|
|
558
|
-
})
|
|
559
|
-
], TranslateNode.prototype, "audio", void 0);
|
|
560
|
-
__decorate([
|
|
561
|
-
prop({
|
|
562
|
-
type: "float",
|
|
563
|
-
default: 0,
|
|
564
|
-
title: "Temperature",
|
|
565
|
-
description: "The temperature to use for the translation."
|
|
566
|
-
})
|
|
567
|
-
], TranslateNode.prototype, "temperature", void 0);
|
|
568
|
-
// ---------------------------------------------------------------------------
|
|
569
|
-
// 8. Transcribe
|
|
570
|
-
// ---------------------------------------------------------------------------
|
|
571
|
-
export class TranscribeNode extends BaseNode {
|
|
572
|
-
static nodeType = "openai.audio.Transcribe";
|
|
573
|
-
static title = "Transcribe";
|
|
574
|
-
static description = "Converts speech to text using OpenAI's speech-to-text API.\n audio, transcription, speech-to-text, stt, whisper";
|
|
575
|
-
static metadataOutputTypes = {
|
|
576
|
-
text: "str",
|
|
577
|
-
words: "list[audio_chunk]",
|
|
578
|
-
segments: "list[audio_chunk]"
|
|
579
|
-
};
|
|
580
|
-
static basicFields = ["audio", "language", "timestamps"];
|
|
581
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
582
|
-
static exposeAsTool = true;
|
|
583
|
-
async process() {
|
|
584
|
-
const apiKey = getApiKey(this._secrets);
|
|
585
|
-
const audio = this.audio;
|
|
586
|
-
if (!audio || (!audio.data && !audio.uri)) {
|
|
587
|
-
throw new Error("Audio input is required");
|
|
588
|
-
}
|
|
589
|
-
const model = String(this.model ?? "whisper-1");
|
|
590
|
-
const language = String(this.language ?? "auto_detect");
|
|
591
|
-
const timestamps = Boolean(this.timestamps ?? false);
|
|
592
|
-
const promptText = String(this.prompt ?? "");
|
|
593
|
-
const temperature = Number(this.temperature ?? 0);
|
|
594
|
-
const isNewModel = model === "gpt-4o-transcribe" || model === "gpt-4o-mini-transcribe";
|
|
595
|
-
const audioBlob = await refToBlob(audio);
|
|
596
|
-
const formData = new FormData();
|
|
597
|
-
formData.append("file", audioBlob, "audio.mp3");
|
|
598
|
-
formData.append("model", model);
|
|
599
|
-
formData.append("temperature", String(temperature));
|
|
600
|
-
if (timestamps) {
|
|
601
|
-
if (isNewModel) {
|
|
602
|
-
throw new Error("New transcription models do not support verbose_json timestamps");
|
|
603
|
-
}
|
|
604
|
-
formData.append("response_format", "verbose_json");
|
|
605
|
-
formData.append("timestamp_granularities[]", "segment");
|
|
606
|
-
formData.append("timestamp_granularities[]", "word");
|
|
607
|
-
}
|
|
608
|
-
else {
|
|
609
|
-
formData.append("response_format", "json");
|
|
610
|
-
}
|
|
611
|
-
if (language !== "auto_detect") {
|
|
612
|
-
formData.append("language", language);
|
|
613
|
-
}
|
|
614
|
-
if (promptText) {
|
|
615
|
-
formData.append("prompt", promptText);
|
|
616
|
-
}
|
|
617
|
-
const res = await fetch(`${OPENAI_API_BASE}/audio/transcriptions`, {
|
|
618
|
-
method: "POST",
|
|
619
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
620
|
-
body: formData
|
|
621
|
-
});
|
|
622
|
-
if (!res.ok) {
|
|
623
|
-
const err = await res.text();
|
|
624
|
-
throw new Error(`OpenAI Transcribe API error ${res.status}: ${err}`);
|
|
625
|
-
}
|
|
626
|
-
const data = (await res.json());
|
|
627
|
-
const text = String(data.text ?? "");
|
|
628
|
-
const words = [];
|
|
629
|
-
const segments = [];
|
|
630
|
-
if (timestamps && !isNewModel) {
|
|
631
|
-
const rawSegments = data.segments;
|
|
632
|
-
if (rawSegments) {
|
|
633
|
-
for (const seg of rawSegments) {
|
|
634
|
-
segments.push({
|
|
635
|
-
timestamp: [Number(seg.start), Number(seg.end)],
|
|
636
|
-
text: String(seg.text)
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
const rawWords = data.words;
|
|
641
|
-
if (rawWords) {
|
|
642
|
-
for (const w of rawWords) {
|
|
643
|
-
words.push({
|
|
644
|
-
timestamp: [Number(w.start), Number(w.end)],
|
|
645
|
-
text: String(w.word)
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
return { text, words, segments };
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
__decorate([
|
|
654
|
-
prop({
|
|
655
|
-
type: "enum",
|
|
656
|
-
default: "whisper-1",
|
|
657
|
-
title: "Model",
|
|
658
|
-
description: "The model to use for transcription.",
|
|
659
|
-
values: ["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"]
|
|
660
|
-
})
|
|
661
|
-
], TranscribeNode.prototype, "model", void 0);
|
|
662
|
-
__decorate([
|
|
663
|
-
prop({
|
|
664
|
-
type: "audio",
|
|
665
|
-
default: {
|
|
666
|
-
type: "audio",
|
|
667
|
-
uri: "",
|
|
668
|
-
asset_id: null,
|
|
669
|
-
data: null,
|
|
670
|
-
metadata: null
|
|
671
|
-
},
|
|
672
|
-
title: "Audio",
|
|
673
|
-
description: "The audio file to transcribe (max 25 MB)."
|
|
674
|
-
})
|
|
675
|
-
], TranscribeNode.prototype, "audio", void 0);
|
|
676
|
-
__decorate([
|
|
677
|
-
prop({
|
|
678
|
-
type: "enum",
|
|
679
|
-
default: "auto_detect",
|
|
680
|
-
title: "Language",
|
|
681
|
-
description: "The language of the input audio",
|
|
682
|
-
values: [
|
|
683
|
-
"auto_detect",
|
|
684
|
-
"af",
|
|
685
|
-
"ar",
|
|
686
|
-
"hy",
|
|
687
|
-
"az",
|
|
688
|
-
"be",
|
|
689
|
-
"bn",
|
|
690
|
-
"bs",
|
|
691
|
-
"bg",
|
|
692
|
-
"ca",
|
|
693
|
-
"hr",
|
|
694
|
-
"cs",
|
|
695
|
-
"da",
|
|
696
|
-
"nl",
|
|
697
|
-
"en",
|
|
698
|
-
"et",
|
|
699
|
-
"tl",
|
|
700
|
-
"fi",
|
|
701
|
-
"fr",
|
|
702
|
-
"gl",
|
|
703
|
-
"de",
|
|
704
|
-
"el",
|
|
705
|
-
"gu",
|
|
706
|
-
"he",
|
|
707
|
-
"hi",
|
|
708
|
-
"hu",
|
|
709
|
-
"is",
|
|
710
|
-
"id",
|
|
711
|
-
"it",
|
|
712
|
-
"ja",
|
|
713
|
-
"kn",
|
|
714
|
-
"kk",
|
|
715
|
-
"ko",
|
|
716
|
-
"lv",
|
|
717
|
-
"lt",
|
|
718
|
-
"mk",
|
|
719
|
-
"ms",
|
|
720
|
-
"zh",
|
|
721
|
-
"mi",
|
|
722
|
-
"mr",
|
|
723
|
-
"ne",
|
|
724
|
-
"no",
|
|
725
|
-
"fa",
|
|
726
|
-
"pl",
|
|
727
|
-
"pt",
|
|
728
|
-
"pa",
|
|
729
|
-
"ro",
|
|
730
|
-
"ru",
|
|
731
|
-
"sr",
|
|
732
|
-
"sk",
|
|
733
|
-
"sl",
|
|
734
|
-
"es",
|
|
735
|
-
"sw",
|
|
736
|
-
"sv",
|
|
737
|
-
"ta",
|
|
738
|
-
"te",
|
|
739
|
-
"th",
|
|
740
|
-
"tr",
|
|
741
|
-
"uk",
|
|
742
|
-
"ur",
|
|
743
|
-
"vi",
|
|
744
|
-
"cy"
|
|
745
|
-
]
|
|
746
|
-
})
|
|
747
|
-
], TranscribeNode.prototype, "language", void 0);
|
|
748
|
-
__decorate([
|
|
749
|
-
prop({
|
|
750
|
-
type: "bool",
|
|
751
|
-
default: false,
|
|
752
|
-
title: "Timestamps",
|
|
753
|
-
description: "Whether to return timestamps for the generated text."
|
|
754
|
-
})
|
|
755
|
-
], TranscribeNode.prototype, "timestamps", void 0);
|
|
756
|
-
__decorate([
|
|
757
|
-
prop({
|
|
758
|
-
type: "str",
|
|
759
|
-
default: "",
|
|
760
|
-
title: "Prompt",
|
|
761
|
-
description: "Optional text to guide the model's style or continue a previous audio segment."
|
|
762
|
-
})
|
|
763
|
-
], TranscribeNode.prototype, "prompt", void 0);
|
|
764
|
-
__decorate([
|
|
765
|
-
prop({
|
|
766
|
-
type: "float",
|
|
767
|
-
default: 0,
|
|
768
|
-
title: "Temperature",
|
|
769
|
-
description: "The sampling temperature between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.",
|
|
770
|
-
min: 0,
|
|
771
|
-
max: 1
|
|
772
|
-
})
|
|
773
|
-
], TranscribeNode.prototype, "temperature", void 0);
|
|
774
|
-
// ---------------------------------------------------------------------------
|
|
775
|
-
// 9. RealtimeAgent
|
|
776
|
-
// ---------------------------------------------------------------------------
|
|
777
|
-
export class RealtimeAgentNode extends BaseNode {
|
|
778
|
-
static nodeType = "openai.agents.RealtimeAgent";
|
|
779
|
-
static title = "Realtime Agent";
|
|
780
|
-
static description = "Realtime conversational agent using OpenAI’s WebSocket Realtime API.\n" +
|
|
781
|
-
"Streams audio/text input and receives streaming text and audio responses with <300ms latency.\n" +
|
|
782
|
-
"realtime, streaming, openai, audio-input, text-output, websocket\n\n" +
|
|
783
|
-
"Uses a persistent WebSocket connection with server-side VAD for automatic turn detection.";
|
|
784
|
-
static metadataOutputTypes = {
|
|
785
|
-
chunk: "chunk",
|
|
786
|
-
audio: "audio",
|
|
787
|
-
text: "str"
|
|
788
|
-
};
|
|
789
|
-
static basicFields = ["model", "system", "chunk", "speed"];
|
|
790
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
791
|
-
static supportsDynamicOutputs = true;
|
|
792
|
-
static isStreamingInput = true;
|
|
793
|
-
static isStreamingOutput = true;
|
|
794
|
-
async process() {
|
|
795
|
-
return {};
|
|
796
|
-
}
|
|
797
|
-
async run(inputs, outputs, context) {
|
|
798
|
-
let apiKey = "";
|
|
799
|
-
if (context && typeof context.getSecret === "function") {
|
|
800
|
-
apiKey = (await context.getSecret("OPENAI_API_KEY")) ?? "";
|
|
801
|
-
}
|
|
802
|
-
if (!apiKey)
|
|
803
|
-
apiKey = process.env.OPENAI_API_KEY ?? "";
|
|
804
|
-
if (!apiKey)
|
|
805
|
-
throw new Error("OPENAI_API_KEY is not configured");
|
|
806
|
-
const model = String(this.model ?? "gpt-4o-mini-realtime-preview");
|
|
807
|
-
const voice = String(this.voice ?? "alloy");
|
|
808
|
-
const system = String(this.system ?? "");
|
|
809
|
-
const temperature = Number(this.temperature ?? 0.8);
|
|
810
|
-
const speed = Number(this.speed ?? 1);
|
|
811
|
-
const wantAudio = voice !== "none";
|
|
812
|
-
const { WebSocket } = await import("ws");
|
|
813
|
-
const wsUrl = `wss://api.openai.com/v1/realtime?model=${encodeURIComponent(model)}`;
|
|
814
|
-
const ws = new WebSocket(wsUrl, {
|
|
815
|
-
headers: {
|
|
816
|
-
Authorization: `Bearer ${apiKey}`,
|
|
817
|
-
"OpenAI-Beta": "realtime=v1"
|
|
818
|
-
}
|
|
819
|
-
});
|
|
820
|
-
await new Promise((resolve, reject) => {
|
|
821
|
-
ws.on("open", resolve);
|
|
822
|
-
ws.on("error", reject);
|
|
823
|
-
});
|
|
824
|
-
// Configure session
|
|
825
|
-
ws.send(JSON.stringify({
|
|
826
|
-
type: "session.update",
|
|
827
|
-
session: {
|
|
828
|
-
modalities: wantAudio ? ["text", "audio"] : ["text"],
|
|
829
|
-
instructions: system || undefined,
|
|
830
|
-
voice: wantAudio ? voice : undefined,
|
|
831
|
-
input_audio_format: "pcm16",
|
|
832
|
-
output_audio_format: "pcm16",
|
|
833
|
-
input_audio_transcription: { model: "gpt-4o-mini-transcribe" },
|
|
834
|
-
turn_detection: {
|
|
835
|
-
type: "server_vad",
|
|
836
|
-
silence_duration_ms: 200
|
|
837
|
-
},
|
|
838
|
-
temperature,
|
|
839
|
-
speed
|
|
840
|
-
}
|
|
841
|
-
}));
|
|
842
|
-
// Consumer: listen for response events
|
|
843
|
-
let fullText = "";
|
|
844
|
-
let consumerDone = false;
|
|
845
|
-
let resolveResponseDone;
|
|
846
|
-
const responseDonePromise = new Promise((resolve) => {
|
|
847
|
-
resolveResponseDone = resolve;
|
|
848
|
-
});
|
|
849
|
-
const consumerPromise = new Promise((resolve, reject) => {
|
|
850
|
-
ws.on("message", async (data) => {
|
|
851
|
-
try {
|
|
852
|
-
const msg = JSON.parse(data.toString());
|
|
853
|
-
const msgType = String(msg.type ?? "");
|
|
854
|
-
if (msgType === "response.text.delta") {
|
|
855
|
-
const delta = String(msg.delta ?? "");
|
|
856
|
-
if (delta) {
|
|
857
|
-
fullText += delta;
|
|
858
|
-
await outputs.emit("chunk", {
|
|
859
|
-
type: "chunk",
|
|
860
|
-
content: delta,
|
|
861
|
-
done: false,
|
|
862
|
-
content_type: "text"
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
else if (msgType === "response.audio.delta") {
|
|
867
|
-
const audioB64 = String(msg.delta ?? "");
|
|
868
|
-
if (audioB64) {
|
|
869
|
-
await outputs.emit("chunk", {
|
|
870
|
-
type: "chunk",
|
|
871
|
-
content: audioB64,
|
|
872
|
-
done: false,
|
|
873
|
-
content_type: "audio",
|
|
874
|
-
content_metadata: {
|
|
875
|
-
format: "pcm16",
|
|
876
|
-
encoding: "pcm16",
|
|
877
|
-
sample_rate: 24000,
|
|
878
|
-
channels: 1
|
|
879
|
-
}
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
else if (msgType === "response.audio_transcript.delta") {
|
|
884
|
-
const delta = String(msg.delta ?? "");
|
|
885
|
-
if (delta)
|
|
886
|
-
fullText += delta;
|
|
887
|
-
}
|
|
888
|
-
else if (msgType === "response.done") {
|
|
889
|
-
resolveResponseDone();
|
|
890
|
-
}
|
|
891
|
-
else if (msgType === "error") {
|
|
892
|
-
const errMsg = msg.error?.message ??
|
|
893
|
-
"Unknown realtime error";
|
|
894
|
-
reject(new Error(`OpenAI Realtime error: ${errMsg}`));
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
catch (err) {
|
|
898
|
-
if (!consumerDone)
|
|
899
|
-
reject(err);
|
|
900
|
-
}
|
|
901
|
-
});
|
|
902
|
-
ws.on("error", (err) => {
|
|
903
|
-
if (!consumerDone)
|
|
904
|
-
reject(err);
|
|
905
|
-
});
|
|
906
|
-
ws.on("close", () => {
|
|
907
|
-
consumerDone = true;
|
|
908
|
-
resolve();
|
|
909
|
-
});
|
|
910
|
-
});
|
|
911
|
-
// Producer: read streaming input and forward to WebSocket
|
|
912
|
-
try {
|
|
913
|
-
for await (const [handle, item] of inputs.any()) {
|
|
914
|
-
if (handle === "__control__")
|
|
915
|
-
continue;
|
|
916
|
-
const chunk = item;
|
|
917
|
-
let done = false;
|
|
918
|
-
if (typeof chunk === "string") {
|
|
919
|
-
// Raw base64 audio
|
|
920
|
-
if (chunk && ws.readyState === WebSocket.OPEN) {
|
|
921
|
-
ws.send(JSON.stringify({
|
|
922
|
-
type: "input_audio_buffer.append",
|
|
923
|
-
audio: chunk
|
|
924
|
-
}));
|
|
925
|
-
}
|
|
926
|
-
continue;
|
|
927
|
-
}
|
|
928
|
-
done = Boolean(chunk.done ?? false);
|
|
929
|
-
if (done)
|
|
930
|
-
break;
|
|
931
|
-
const contentType = String(chunk.content_type ?? "text");
|
|
932
|
-
const content = String(chunk.content ?? "");
|
|
933
|
-
if (!content)
|
|
934
|
-
continue;
|
|
935
|
-
if (contentType === "audio") {
|
|
936
|
-
// Audio chunk → append to audio buffer
|
|
937
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
938
|
-
ws.send(JSON.stringify({
|
|
939
|
-
type: "input_audio_buffer.append",
|
|
940
|
-
audio: content
|
|
941
|
-
}));
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
else {
|
|
945
|
-
// Text chunk → create conversation item and trigger response
|
|
946
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
947
|
-
ws.send(JSON.stringify({
|
|
948
|
-
type: "conversation.item.create",
|
|
949
|
-
item: {
|
|
950
|
-
type: "message",
|
|
951
|
-
role: "user",
|
|
952
|
-
content: [{ type: "input_text", text: content }]
|
|
953
|
-
}
|
|
954
|
-
}));
|
|
955
|
-
ws.send(JSON.stringify({ type: "response.create" }));
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
// Commit any remaining audio buffer
|
|
960
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
961
|
-
ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
|
|
962
|
-
}
|
|
963
|
-
// Wait for response.done with timeout
|
|
964
|
-
await Promise.race([
|
|
965
|
-
responseDonePromise,
|
|
966
|
-
consumerPromise,
|
|
967
|
-
new Promise((resolve) => setTimeout(resolve, 10000))
|
|
968
|
-
]);
|
|
969
|
-
}
|
|
970
|
-
finally {
|
|
971
|
-
if (ws.readyState === WebSocket.OPEN ||
|
|
972
|
-
ws.readyState === WebSocket.CONNECTING) {
|
|
973
|
-
ws.close();
|
|
974
|
-
}
|
|
975
|
-
consumerDone = true;
|
|
976
|
-
}
|
|
977
|
-
await consumerPromise;
|
|
978
|
-
// Emit final done chunks
|
|
979
|
-
await outputs.emit("chunk", {
|
|
980
|
-
type: "chunk",
|
|
981
|
-
content: "",
|
|
982
|
-
done: true,
|
|
983
|
-
content_type: "text"
|
|
984
|
-
});
|
|
985
|
-
await outputs.emit("text", fullText);
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
__decorate([
|
|
989
|
-
prop({
|
|
990
|
-
type: "enum",
|
|
991
|
-
default: "gpt-4o-mini-realtime-preview",
|
|
992
|
-
title: "Model",
|
|
993
|
-
values: ["gpt-4o-realtime-preview", "gpt-4o-mini-realtime-preview"]
|
|
994
|
-
})
|
|
995
|
-
], RealtimeAgentNode.prototype, "model", void 0);
|
|
996
|
-
__decorate([
|
|
997
|
-
prop({
|
|
998
|
-
type: "str",
|
|
999
|
-
default: "You are an AI assistant interacting in real-time. " +
|
|
1000
|
-
"Be concise but clear. Prioritize key information first. " +
|
|
1001
|
-
"Ask clarifying questions when needed.",
|
|
1002
|
-
title: "System",
|
|
1003
|
-
description: "System instructions for the realtime session"
|
|
1004
|
-
})
|
|
1005
|
-
], RealtimeAgentNode.prototype, "system", void 0);
|
|
1006
|
-
__decorate([
|
|
1007
|
-
prop({
|
|
1008
|
-
type: "chunk",
|
|
1009
|
-
default: {
|
|
1010
|
-
type: "chunk",
|
|
1011
|
-
node_id: null,
|
|
1012
|
-
thread_id: null,
|
|
1013
|
-
workflow_id: null,
|
|
1014
|
-
content_type: "text",
|
|
1015
|
-
content: "",
|
|
1016
|
-
content_metadata: {},
|
|
1017
|
-
done: false,
|
|
1018
|
-
thinking: false
|
|
1019
|
-
},
|
|
1020
|
-
title: "Chunk",
|
|
1021
|
-
description: "The audio chunk to use as input."
|
|
1022
|
-
})
|
|
1023
|
-
], RealtimeAgentNode.prototype, "chunk", void 0);
|
|
1024
|
-
__decorate([
|
|
1025
|
-
prop({
|
|
1026
|
-
type: "enum",
|
|
1027
|
-
default: "alloy",
|
|
1028
|
-
title: "Voice",
|
|
1029
|
-
description: "The voice for the audio output",
|
|
1030
|
-
values: [
|
|
1031
|
-
"none",
|
|
1032
|
-
"ash",
|
|
1033
|
-
"alloy",
|
|
1034
|
-
"ballad",
|
|
1035
|
-
"coral",
|
|
1036
|
-
"echo",
|
|
1037
|
-
"fable",
|
|
1038
|
-
"onyx",
|
|
1039
|
-
"nova",
|
|
1040
|
-
"shimmer",
|
|
1041
|
-
"sage",
|
|
1042
|
-
"verse"
|
|
1043
|
-
]
|
|
1044
|
-
})
|
|
1045
|
-
], RealtimeAgentNode.prototype, "voice", void 0);
|
|
1046
|
-
__decorate([
|
|
1047
|
-
prop({
|
|
1048
|
-
type: "float",
|
|
1049
|
-
default: 1,
|
|
1050
|
-
title: "Speed",
|
|
1051
|
-
description: "The speed of the model’s spoken response",
|
|
1052
|
-
min: 0.25,
|
|
1053
|
-
max: 1.5
|
|
1054
|
-
})
|
|
1055
|
-
], RealtimeAgentNode.prototype, "speed", void 0);
|
|
1056
|
-
__decorate([
|
|
1057
|
-
prop({
|
|
1058
|
-
type: "float",
|
|
1059
|
-
default: 0.8,
|
|
1060
|
-
title: "Temperature",
|
|
1061
|
-
description: "The temperature for the response",
|
|
1062
|
-
min: 0.6,
|
|
1063
|
-
max: 1.2
|
|
1064
|
-
})
|
|
1065
|
-
], RealtimeAgentNode.prototype, "temperature", void 0);
|
|
1066
|
-
// ---------------------------------------------------------------------------
|
|
1067
|
-
// 10. RealtimeTranscription
|
|
1068
|
-
// ---------------------------------------------------------------------------
|
|
1069
|
-
export class RealtimeTranscriptionNode extends BaseNode {
|
|
1070
|
-
static nodeType = "openai.agents.RealtimeTranscription";
|
|
1071
|
-
static title = "Realtime Transcription";
|
|
1072
|
-
static description = "Realtime speech-to-text using OpenAI's WebSocket Realtime API.\n" +
|
|
1073
|
-
"Streams audio chunks in and receives transcription results in real-time.\n" +
|
|
1074
|
-
"audio, transcription, stt, streaming, realtime, websocket, openai\n\n" +
|
|
1075
|
-
"Uses server-side VAD for automatic speech segment detection.";
|
|
1076
|
-
static metadataOutputTypes = {
|
|
1077
|
-
text: "str",
|
|
1078
|
-
chunk: "chunk"
|
|
1079
|
-
};
|
|
1080
|
-
static requiredSettings = ["OPENAI_API_KEY"];
|
|
1081
|
-
static isStreamingInput = true;
|
|
1082
|
-
static isStreamingOutput = true;
|
|
1083
|
-
async process() {
|
|
1084
|
-
return {};
|
|
1085
|
-
}
|
|
1086
|
-
async run(inputs, outputs, context) {
|
|
1087
|
-
let apiKey = "";
|
|
1088
|
-
if (context && typeof context.getSecret === "function") {
|
|
1089
|
-
apiKey = (await context.getSecret("OPENAI_API_KEY")) ?? "";
|
|
1090
|
-
}
|
|
1091
|
-
if (!apiKey)
|
|
1092
|
-
apiKey = process.env.OPENAI_API_KEY ?? "";
|
|
1093
|
-
if (!apiKey)
|
|
1094
|
-
throw new Error("OPENAI_API_KEY is not configured");
|
|
1095
|
-
const model = String(this.model ?? "gpt-4o-mini-realtime-preview");
|
|
1096
|
-
const temperature = Number(this.temperature ?? 0.8);
|
|
1097
|
-
const { WebSocket } = await import("ws");
|
|
1098
|
-
const wsUrl = `wss://api.openai.com/v1/realtime?model=${encodeURIComponent(model)}`;
|
|
1099
|
-
const ws = new WebSocket(wsUrl, {
|
|
1100
|
-
headers: {
|
|
1101
|
-
Authorization: `Bearer ${apiKey}`,
|
|
1102
|
-
"OpenAI-Beta": "realtime=v1"
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
await new Promise((resolve, reject) => {
|
|
1106
|
-
ws.on("open", resolve);
|
|
1107
|
-
ws.on("error", reject);
|
|
1108
|
-
});
|
|
1109
|
-
// Configure session for transcription only (no audio output)
|
|
1110
|
-
ws.send(JSON.stringify({
|
|
1111
|
-
type: "session.update",
|
|
1112
|
-
session: {
|
|
1113
|
-
modalities: ["text"],
|
|
1114
|
-
instructions: this.system ? String(this.system) : undefined,
|
|
1115
|
-
input_audio_format: "pcm16",
|
|
1116
|
-
input_audio_transcription: { model: "gpt-4o-mini-transcribe" },
|
|
1117
|
-
turn_detection: {
|
|
1118
|
-
type: "server_vad",
|
|
1119
|
-
silence_duration_ms: 500
|
|
1120
|
-
},
|
|
1121
|
-
temperature
|
|
1122
|
-
}
|
|
1123
|
-
}));
|
|
1124
|
-
// Consumer: listen for transcription events
|
|
1125
|
-
let fullText = "";
|
|
1126
|
-
let consumerDone = false;
|
|
1127
|
-
let resolveLastTranscript;
|
|
1128
|
-
const lastTranscriptPromise = new Promise((resolve) => {
|
|
1129
|
-
resolveLastTranscript = resolve;
|
|
1130
|
-
});
|
|
1131
|
-
let finalizeRequested = false;
|
|
1132
|
-
const consumerPromise = new Promise((resolve, reject) => {
|
|
1133
|
-
ws.on("message", async (data) => {
|
|
1134
|
-
try {
|
|
1135
|
-
const msg = JSON.parse(data.toString());
|
|
1136
|
-
const msgType = String(msg.type ?? "");
|
|
1137
|
-
if (msgType ===
|
|
1138
|
-
"conversation.item.input_audio_transcription.completed") {
|
|
1139
|
-
const text = String(msg.transcript ?? "");
|
|
1140
|
-
if (text) {
|
|
1141
|
-
fullText += (fullText ? " " : "") + text;
|
|
1142
|
-
await outputs.emit("chunk", {
|
|
1143
|
-
type: "chunk",
|
|
1144
|
-
content: text,
|
|
1145
|
-
done: false,
|
|
1146
|
-
content_type: "text"
|
|
1147
|
-
});
|
|
1148
|
-
if (finalizeRequested) {
|
|
1149
|
-
resolveLastTranscript();
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
else if (msgType === "response.done") {
|
|
1154
|
-
if (finalizeRequested) {
|
|
1155
|
-
resolveLastTranscript();
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
else if (msgType === "error") {
|
|
1159
|
-
const errMsg = msg.error?.message ??
|
|
1160
|
-
"Unknown realtime error";
|
|
1161
|
-
reject(new Error(`OpenAI Realtime error: ${errMsg}`));
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
catch (err) {
|
|
1165
|
-
if (!consumerDone)
|
|
1166
|
-
reject(err);
|
|
1167
|
-
}
|
|
1168
|
-
});
|
|
1169
|
-
ws.on("error", (err) => {
|
|
1170
|
-
if (!consumerDone)
|
|
1171
|
-
reject(err);
|
|
1172
|
-
});
|
|
1173
|
-
ws.on("close", () => {
|
|
1174
|
-
consumerDone = true;
|
|
1175
|
-
resolve();
|
|
1176
|
-
});
|
|
1177
|
-
});
|
|
1178
|
-
// Producer: read streaming audio input and forward to WebSocket
|
|
1179
|
-
try {
|
|
1180
|
-
for await (const [handle, item] of inputs.any()) {
|
|
1181
|
-
if (handle === "__control__")
|
|
1182
|
-
continue;
|
|
1183
|
-
const chunk = item;
|
|
1184
|
-
let audioB64;
|
|
1185
|
-
let done = false;
|
|
1186
|
-
if (typeof chunk === "string") {
|
|
1187
|
-
audioB64 = chunk;
|
|
1188
|
-
}
|
|
1189
|
-
else {
|
|
1190
|
-
if (chunk.content_type && chunk.content_type !== "audio")
|
|
1191
|
-
continue;
|
|
1192
|
-
audioB64 = String(chunk.content ?? "");
|
|
1193
|
-
done = Boolean(chunk.done ?? false);
|
|
1194
|
-
}
|
|
1195
|
-
if (done)
|
|
1196
|
-
break;
|
|
1197
|
-
if (!audioB64)
|
|
1198
|
-
continue;
|
|
1199
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
1200
|
-
ws.send(JSON.stringify({
|
|
1201
|
-
type: "input_audio_buffer.append",
|
|
1202
|
-
audio: audioB64
|
|
1203
|
-
}));
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
// Commit remaining audio
|
|
1207
|
-
finalizeRequested = true;
|
|
1208
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
1209
|
-
ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
|
|
1210
|
-
}
|
|
1211
|
-
// Wait for final transcript
|
|
1212
|
-
await Promise.race([
|
|
1213
|
-
lastTranscriptPromise,
|
|
1214
|
-
consumerPromise,
|
|
1215
|
-
new Promise((resolve) => setTimeout(resolve, 5000))
|
|
1216
|
-
]);
|
|
1217
|
-
}
|
|
1218
|
-
finally {
|
|
1219
|
-
if (ws.readyState === WebSocket.OPEN ||
|
|
1220
|
-
ws.readyState === WebSocket.CONNECTING) {
|
|
1221
|
-
ws.close();
|
|
1222
|
-
}
|
|
1223
|
-
consumerDone = true;
|
|
1224
|
-
}
|
|
1225
|
-
await consumerPromise;
|
|
1226
|
-
// Emit final done chunk and full text
|
|
1227
|
-
await outputs.emit("chunk", {
|
|
1228
|
-
type: "chunk",
|
|
1229
|
-
content: "",
|
|
1230
|
-
done: true,
|
|
1231
|
-
content_type: "text"
|
|
1232
|
-
});
|
|
1233
|
-
await outputs.emit("text", fullText);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
__decorate([
|
|
1237
|
-
prop({
|
|
1238
|
-
type: "enum",
|
|
1239
|
-
default: "gpt-4o-mini-realtime-preview",
|
|
1240
|
-
title: "Model",
|
|
1241
|
-
description: "The realtime model to use.",
|
|
1242
|
-
values: ["gpt-4o-realtime-preview", "gpt-4o-mini-realtime-preview"]
|
|
1243
|
-
})
|
|
1244
|
-
], RealtimeTranscriptionNode.prototype, "model", void 0);
|
|
1245
|
-
__decorate([
|
|
1246
|
-
prop({
|
|
1247
|
-
type: "chunk",
|
|
1248
|
-
default: {
|
|
1249
|
-
type: "chunk",
|
|
1250
|
-
node_id: null,
|
|
1251
|
-
thread_id: null,
|
|
1252
|
-
workflow_id: null,
|
|
1253
|
-
content_type: "audio",
|
|
1254
|
-
content: "",
|
|
1255
|
-
content_metadata: {},
|
|
1256
|
-
done: false,
|
|
1257
|
-
thinking: false
|
|
1258
|
-
},
|
|
1259
|
-
title: "Chunk",
|
|
1260
|
-
description: "Audio chunk input stream (base64-encoded PCM16 audio)."
|
|
1261
|
-
})
|
|
1262
|
-
], RealtimeTranscriptionNode.prototype, "chunk", void 0);
|
|
1263
|
-
__decorate([
|
|
1264
|
-
prop({
|
|
1265
|
-
type: "str",
|
|
1266
|
-
default: "",
|
|
1267
|
-
title: "System",
|
|
1268
|
-
description: "System instructions (optional)"
|
|
1269
|
-
})
|
|
1270
|
-
], RealtimeTranscriptionNode.prototype, "system", void 0);
|
|
1271
|
-
__decorate([
|
|
1272
|
-
prop({
|
|
1273
|
-
type: "float",
|
|
1274
|
-
default: 0.8,
|
|
1275
|
-
title: "Temperature",
|
|
1276
|
-
description: "Decoding temperature"
|
|
1277
|
-
})
|
|
1278
|
-
], RealtimeTranscriptionNode.prototype, "temperature", void 0);
|
|
1279
|
-
// ---------------------------------------------------------------------------
|
|
1280
|
-
// Export
|
|
1281
|
-
// ---------------------------------------------------------------------------
|
|
1282
|
-
export const OPENAI_NODES = [
|
|
1283
|
-
EmbeddingNode,
|
|
1284
|
-
WebSearchNode,
|
|
1285
|
-
ModerationNode,
|
|
1286
|
-
CreateImageNode,
|
|
1287
|
-
EditImageNode,
|
|
1288
|
-
TextToSpeechNode,
|
|
1289
|
-
TranslateNode,
|
|
1290
|
-
TranscribeNode,
|
|
1291
|
-
RealtimeAgentNode,
|
|
1292
|
-
RealtimeTranscriptionNode
|
|
1293
|
-
];
|
|
1294
|
-
//# sourceMappingURL=openai.js.map
|