From cfa5c384277b89e78b22a7f1ea911f3ad4949384 Mon Sep 17 00:00:00 2001 From: gbattistel <4660743+gbattistel@users.noreply.github.com> Date: Fri, 15 May 2026 14:42:49 -0400 Subject: [PATCH 1/5] feat(tools): add Telnyx messaging, lookup, and verify integrations --- .../credentials/TelnyxApi.credential.ts | 23 ++++ .../tools/TelnyxMessaging/TelnyxMessaging.ts | 122 ++++++++++++++++++ .../nodes/tools/TelnyxMessaging/telnyx.png | Bin 0 -> 25250 bytes .../TelnyxNumberLookup/TelnyxNumberLookup.ts | 83 ++++++++++++ .../nodes/tools/TelnyxNumberLookup/telnyx.png | Bin 0 -> 25250 bytes .../TelnyxVerifyCheck/TelnyxVerifyCheck.ts | 107 +++++++++++++++ .../nodes/tools/TelnyxVerifyCheck/telnyx.png | Bin 0 -> 25250 bytes .../TelnyxVerifySend/TelnyxVerifySend.ts | 107 +++++++++++++++ .../nodes/tools/TelnyxVerifySend/telnyx.png | Bin 0 -> 25250 bytes 9 files changed, 442 insertions(+) create mode 100644 packages/components/credentials/TelnyxApi.credential.ts create mode 100644 packages/components/nodes/tools/TelnyxMessaging/TelnyxMessaging.ts create mode 100644 packages/components/nodes/tools/TelnyxMessaging/telnyx.png create mode 100644 packages/components/nodes/tools/TelnyxNumberLookup/TelnyxNumberLookup.ts create mode 100644 packages/components/nodes/tools/TelnyxNumberLookup/telnyx.png create mode 100644 packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts create mode 100644 packages/components/nodes/tools/TelnyxVerifyCheck/telnyx.png create mode 100644 packages/components/nodes/tools/TelnyxVerifySend/TelnyxVerifySend.ts create mode 100644 packages/components/nodes/tools/TelnyxVerifySend/telnyx.png diff --git a/packages/components/credentials/TelnyxApi.credential.ts b/packages/components/credentials/TelnyxApi.credential.ts new file mode 100644 index 00000000000..6bdd70715f0 --- /dev/null +++ b/packages/components/credentials/TelnyxApi.credential.ts @@ -0,0 +1,23 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class TelnyxApi implements INodeCredential { + label: string + name: string + version: number + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx API' + this.name = 'telnyxApi' + this.version = 1.0 + this.inputs = [ + { + label: 'API Key', + name: 'apiKey', + type: 'password' + } + ] + } +} + +module.exports = { credClass: TelnyxApi } diff --git a/packages/components/nodes/tools/TelnyxMessaging/TelnyxMessaging.ts b/packages/components/nodes/tools/TelnyxMessaging/TelnyxMessaging.ts new file mode 100644 index 00000000000..2c6431bdd19 --- /dev/null +++ b/packages/components/nodes/tools/TelnyxMessaging/TelnyxMessaging.ts @@ -0,0 +1,122 @@ +import { DynamicStructuredTool } from '../OpenAPIToolkit/core' +import { z } from 'zod/v3' +import { secureFetch } from '../../../src/httpSecurity' +import { getBaseClasses, getCredentialData, getCredentialParam, handleErrorMessage } from '../../../src/utils' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' + +class TelnyxMessagingTool extends DynamicStructuredTool { + constructor(apiKey: string, defaultFrom?: string, defaultMessagingProfileId?: string) { + super({ + name: 'telnyx_send_sms', + description: 'Send an SMS message through Telnyx. Use this tool when you need to send a text message to a phone number in E.164 format.', + schema: z.object({ + to: z.string().describe('Destination phone number in E.164 format'), + text: z.string().describe('Text message body to send to the destination number'), + from: z.string().optional().describe('Sender phone number in E.164 format'), + messagingProfileId: z.string().optional().describe('Telnyx Messaging Profile ID to use for sending the message') + }), + baseUrl: '', + method: 'POST', + headers: {} + }) + this.apiKey = apiKey + this.defaultFrom = defaultFrom + this.defaultMessagingProfileId = defaultMessagingProfileId + } + + apiKey: string + defaultFrom?: string + defaultMessagingProfileId?: string + + async _call(arg: any): Promise { + const from = arg.from || this.defaultFrom + const messagingProfileId = arg.messagingProfileId || this.defaultMessagingProfileId + + if (!from && !messagingProfileId) { + throw new Error('Telnyx Messaging requires either a From number or a Messaging Profile ID') + } + + const body: Record = { + to: arg.to, + text: arg.text + } + + if (from) body.from = from + if (messagingProfileId) body.messaging_profile_id = messagingProfileId + + try { + const res = await secureFetch('https://api.telnyx.com/v2/messages', { + method: 'POST', + headers: { + Authorization: `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }) + + const text = await res.text() + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${text}`) + } + return text + } catch (error) { + throw new Error(handleErrorMessage(error)) + } + } +} + +class TelnyxMessaging_Tools implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx Messaging' + this.name = 'telnyxMessaging' + this.version = 1.0 + this.type = 'TelnyxMessaging' + this.icon = 'telnyx.png' + this.category = 'Tools' + this.description = 'Send outbound SMS messages through the Telnyx Messaging API.' + this.baseClasses = [this.type, 'Tool', ...getBaseClasses(TelnyxMessagingTool)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'] + } + this.inputs = [ + { + label: 'Default From Number', + name: 'from', + type: 'string', + description: 'Optional default sender number in E.164 format. Either this value or a Messaging Profile ID is required to send a message.', + optional: true + }, + { + label: 'Default Messaging Profile ID', + name: 'messagingProfileId', + type: 'string', + description: 'Optional default Telnyx Messaging Profile ID. Use this if you prefer profile-based sending instead of providing a from number.', + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + const from = nodeData.inputs?.from as string | undefined + const messagingProfileId = nodeData.inputs?.messagingProfileId as string | undefined + return new TelnyxMessagingTool(apiKey, from, messagingProfileId) + } +} + +module.exports = { nodeClass: TelnyxMessaging_Tools } diff --git a/packages/components/nodes/tools/TelnyxMessaging/telnyx.png b/packages/components/nodes/tools/TelnyxMessaging/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 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 literal 0 HcmV?d00001 diff --git a/packages/components/nodes/tools/TelnyxNumberLookup/TelnyxNumberLookup.ts b/packages/components/nodes/tools/TelnyxNumberLookup/TelnyxNumberLookup.ts new file mode 100644 index 00000000000..37a9fcf5674 --- /dev/null +++ b/packages/components/nodes/tools/TelnyxNumberLookup/TelnyxNumberLookup.ts @@ -0,0 +1,83 @@ +import { DynamicStructuredTool } from '../OpenAPIToolkit/core' +import { z } from 'zod/v3' +import { secureFetch } from '../../../src/httpSecurity' +import { getBaseClasses, getCredentialData, getCredentialParam, handleErrorMessage } from '../../../src/utils' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' + +class TelnyxNumberLookupTool extends DynamicStructuredTool { + constructor(apiKey: string) { + super({ + name: 'telnyx_number_lookup', + description: 'Look up a phone number with Telnyx. Use this to validate a number, inspect carrier details, and understand the line type before sending or calling.', + schema: z.object({ + phoneNumber: z.string().describe('Phone number to lookup in E.164 format') + }), + baseUrl: '', + method: 'GET', + headers: {} + }) + this.apiKey = apiKey + } + + apiKey: string + + async _call(arg: any): Promise { + try { + const url = `https://api.telnyx.com/v2/number_lookup/${encodeURIComponent(arg.phoneNumber)}` + const res = await secureFetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + } + }) + + const text = await res.text() + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${text}`) + } + return text + } catch (error) { + throw new Error(handleErrorMessage(error)) + } + } +} + +class TelnyxNumberLookup_Tools implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx Number Lookup' + this.name = 'telnyxNumberLookup' + this.version = 1.0 + this.type = 'TelnyxNumberLookup' + this.icon = 'telnyx.png' + this.category = 'Tools' + this.description = 'Validate a phone number and fetch carrier and line type data through the Telnyx Number Lookup API.' + this.baseClasses = [this.type, 'Tool', ...getBaseClasses(TelnyxNumberLookupTool)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'] + } + this.inputs = [] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + return new TelnyxNumberLookupTool(apiKey) + } +} + +module.exports = { nodeClass: TelnyxNumberLookup_Tools } diff --git a/packages/components/nodes/tools/TelnyxNumberLookup/telnyx.png b/packages/components/nodes/tools/TelnyxNumberLookup/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 zcmce81zaDy(&*yu?poZvh2qju+}+)ZyA>;1ic{R7SfRMIP^`GS7MB7=TBO)psGM`| zJ>Qk@_ud;qGMSl7CX;M7yPN!Prf)t0=+cspB>@N!m>`${;ARdG1E8UxZUZzFGz>H} z3@joXEC?tFi13JLDCp>DC}^k{Soqi&n0T0|XgI_;cmxDQL`3M=BxEFnWcY+cgttl{ zpkZKOVPKKr;E)M1&@c%9Yq)6xFyJBgA@`vmC;>Lwtp$Dq3@jWp z6yzNfBtyXbi3NbNkkGJjFgLRRG86;=i4KJh01!*9zYG80MBqBC2vp5uzdTsUzs_kW zCV5@@==#EkY&ia3RRY(WsgIN5^L=lzLGN*q=Q-Y4Ru=G}LFN6JX}{V<6DM1vMEyX7 z&%dx(y(Ef4_{C9bTJSaeT}?14$1mNas;%K?=sbaY|5N2SJFD&rOPoi;O#?qgP@`yB zwyWl*bVv@IKRx>Ua!|{1#P20(e2wtV$bU&-YWY62i?(To_xw@+FZ%D4;Y6n3$|+KQ zK=?Pw=Mrba#OWOtS#j|D`rp()BL~{*dCpF2jtWhKAX%_nZF9o>jb4mh0?a#kDWPXm z&E>}q>uuRI31=*ApupAG(RsnkMx9rvR3ci}$}s*&;HCH?yXp^PD*>|Tj#x&2q+ zXiHFFxwrNKaY~emY!;Z|5;$&L($?_!Zv4$a<&V<8FjzOce(Etl=vi`bfR-elKP@yl zVgKIB0`eVQ5{=)xA^J*DAyo(i0IO?roN8d67ZQ)cufo4m2-tvZc#|Wb6r^AhrlEwO zrr_G#av_G|t-sEF^VG;p2Xid(EnYq-J_BRXlOODF*?$Iz<7Dls9%qFl8Iby3rFxfRC2;cGlh>m()9$kFf{ZRO3 z$_X}u^LId!$Pir;o0n?v{~>$+oj#%Kv%uS}hCq3BGB(TAUtuLXSNw@ojcuOoLy>_kY{01Or8C+u!1evhO&0cecP^xy#QE{5GF z04kz7n)s|%<>)@XZV;%%z0Tx8GYQeWW|coE2LOmy4#gO-QWS{bv=2e?x>X}<`vO*Dqbzz;gd-3jj1SqxtB>Q}OP# zWw&Hdi`9E$&r<#yz&+XyR0F_e=1bPA3UX4#z$AC6~gtnRLMeLkn_T_pzqlvIM5oP_}9c~BB= zXhh~M=c9qmbwj@wpi2I{)dxEK&C}7YAk)!hc>kDDPg05o93Bu`Wr-I(Q&E$m8DMjP zKaW?4h4zKV-(Cm~3fQ&8MwWAc+?mSs6y|Blg;o%Ucx72}>i#OUdp!#P5G)dCW&}$T z1$>*adjNosqXfm#*9`A7D2NoSuNG~&h5l6-0O3D(5&)QUUnw(XzVb!-2KFujiahcA z&bnD~?E`=^-$}dKLcX0U!5?&WqrOeux##M))&l$68vgUa1OV7bHvo=z^Q-O6xSkKf z;HdA;zd$P!T#lZCeAOzl@pgpssP%4w)wj}0(|$f3;d5I90EG2@Lw;}PiD~5zKs7yY zjQdRNlcurYM?;`v0vb?d%+IA=A<^W2C#$&gU|*T{{sE>nm?i!GUd%4q0$gx0VA?%% zi`F0@E&|CuC0QDmSL{vb%5-;S{_G_Li`4Y@`GYJ2g2~UTQ3a?jWIm zrCk-#?4KkJvj4cEZ-vpwEY|KT^se6Gnp{k8R04Cl);$xo{UR}~3_zOx?m2D}oJ1_H z?XAS^j1P$kr)*UkSGw)i2r-xQ`9P8s@fu+N_$S(wOLVxq1gM6PqEi?Si{=I^6An~&U-skhcu|Z$6 zTigB$P<#5eN*s=;@q^3+or@Yz^8R2`)6=@f42;34Ao(Z&!Gkr|NZ*>X+0l3fZYSWs z!fyWMESyTeE=131%3Nk&uY6xyt^Y3(vRY5mj&0KG3TOMeIgof>K7-TQn_H_9#!cL5 zAKs!DP0}n% zI!2DH-2lpE7PInh0r!|cg<-9ECrpRFGE`Vxw+dLhPqsbrW*xDagZsWxobGZtXJl}L zxcqlbNlB_Ff=Zu9QFqT&l-`>8+DuMJ}f_vdx3N)EJ9WTU@Jh`Mk!@?QO37#A+M zo~*B zx1P@0cHT3LC9o9Gm;NNDNaVWg_-L!U_fZ=~(Xs8!<4d=ZriAT4$9WdIE5`0chrP;g z#vvI&?Z=zd#<0K#=di~9nDZh_E!9!^z2>5-e^k# zd|LLk_0pqhnF&hUCcgJQo;8Jgs6^ONO^SkH*ETUcW|jeEY^nu_)gRQ78OC6=3`%5h zev?!jcyL_`2q|&@ne|Ir7VicKNb$f(cpYEEkyxNK-mCTMi88&FDYH!eza=5iuTS!* zMqN`U#3*U^Bg?;kqN?4X`d=vcx7Das(WYg5YkxISH^=zE+M?@qPo!C#N6hvY=Y%s? zJ9E$F{vB=G_e*P^OAM7BZR;M|%nyRnW3|Bv_vp_h92z~mBX~LA*>5;!hmG|?Gn3wP z5n7vRNdNqtGv5rajoX0}m+biHN1+i?!We#SN#0AUuwJU4Lb=AzzxD13+i)u< z9*la+s$g}@`AxB>j@KEXPwxKhvYjcX}HNMl%vGSf-q!&hhH##4?4{imdG zbOVwl$@LElA#g7bnb}27_CG$LHIJ?8(OfG%QuAOGNXU>Ly7z-nOsrf2Mcmy|(-&CJ z_Ju|y*q~Hd9)B<8EZv#@^PdHG$sRj=!%WOk8kg1zd!k5hb&pv*zbL-u7fE_vP2s9x zBM;;8JyYL>pl?m-8W*;~XXnh#zFci0&5>~*YS%rz&hUDa~D<+Gp@ z5V{qtLfK?sOF=+d+$I^RkBSvsFUoYkiY?3eSo^G&rOtO3iz^hq3gPGvXi*CCe&XyoOMd}Ebvw7gvh6 z`7Uxvty1$9`bS|9I?Cy^uXByTh~{PSF` zYlTsc(7C#(c**sjb${;emHsi6gdfD^x-(AUu2<+doU;5>ZFIz%=4I=mtq7mZ_VnGj zv2;e>K%EU9dDL7IoCS*(Ys9pZ#Yqy3WTxG)e1Xr!h#t7^w^2N``&ry+3-`HdVUed5pTVT zVVh^C%=sqKwc_KX1Z0|Ib!J2}ELT76-kT5UqOx0*qbG8U^Smjn-`jqJ}22&?g)T)Ec26wgqM=j!zy9Py^@PjKdsx3 zgFy)11AEkz_9Sz1Lyhes(ZR>T`B#T|e&vm*T}iCyK`zS>+P0^qi>mn8OBtVQISP)g z!1bK7ciTC6hfhchmp0DRuoAgL2XE{J>x$9SYDxZ7NXYXX! zBI`N)E&? zJvTtedZIjL{Qg0pL(QQRiBwgg!q!2s!l8rW{A_)4I~t<`?i)H-^2Za;dRpGk$R~^ z^^M?!8RI;qd|)<83ZK z5L=78Vuf^n{H?U@yt^FQIBKanV(a;LSuFF&P-3nYC3s@-iZ;;6O7D(O%buC^UA;gI z?mBtE>%*W_URKZaXrjO0!mADOXf1^11KBAbY~qZOGwXL0TfNUNje&|7{F0Gj^WyJD zpkCI!;D?Pii7}?npUcPFe8Ck|&*%l6lc?6#F5?jECRQz|(mjXehFC4ZK~4~I?$B{w z%$*=bWMoC2++`TjTz3jrGxUIP3H}{HS#Ez`rK;e#GL9H!1bT3&Xa|K6dL_I@5R)h_ z1=9bL&`$Zfwk<8UvQHH!&3z1idixSvds$gh=ga>}xdNunEAxB3{6s1gZ9-RMe&8Zk z6a!q=LcpNFz{37qgVHSo?xsdxRivfcbGv>ZW!;B;OUf?9mO77I&KVJP%XN$TJ>|jxtDrmVD?s zf30z$?rmnZK-qK5v5#aaUxh3%u#i`UpK}=Bm$a~DX%=G-ITP}&=+(42p4m~qxNoQ0;uDWC9bpNdvvvBOI?F$mMu~XCHyWoIF-4wH=1mSDV!=?oEXJ0$ z3L)dhwL^Y6`ciKd;yo&m%x<_xctC%N(@HA=izU8Am0px^f)hv3&F z^~PuHYSzQpF2k@9vj#5Oh3#&@c21;OKH9DPC^%)(@KKZ;79r#wO+Zg~VTn4Ty5K2d zB8yd@I-0xdS{XV=!2}|msT?i0v1EPhs}))Q+*#t)oFoEc<}ZD-tci`X#IJHXRT{JJ zAyxwGMLvsa2J2Vb%4`r~gB+HVBOYHPQ(UU`e5h#BZSgW6^%QDLY>xh5gkVC3^1AV`fSnsUGfLxUX6VZ6J`!1;k$w3 zuhZzL_8IQIl1()0lN>SSJq<3AdDd_3bdBi=cTaH_uxCn41b;)wrWcT$G zx>vS|bRk)xX6}Ej>wi&uT$|t%MB#zLMtxK{!Ib-yl5PD>{sTo6?eUt0-dX+VC%X2l zGf!q%`7kgDKF*hWeejWzcYaTt=EJ4Llpa+QBxKUOyjne-!$3;Xth$pd@o;pr8E@SA z^vQD1$1zb+#1P2D$`IS|0mwYU95ruN9kbnFz0XdINIMU_@(&v*8 z#8<^BtFduvopGSAIJUt0oR&-$r9W<3;gWJYelW#bYT>SSIp^*o8T$-&$+?OODs0;( zB*=e@!oR{q2*r{ZJ&U#fMrdQwxrA1!{uyQ8Cr9KgW4QHkf8Rlbfh75mvo<6p+hZCq_G@N*GQyg*Yqqqc}_XJx=I`8gy^=nqM*KF z{=LU-S2kBK%Tb;2bNM#t#QL&y6*=Y^vBg$w+boRdV6NMKoP@uO$ z#Cx(5cJw`pp1y+HLg_Q{D(2jX<0oa*9*g;pT^BwUOkwIW$`8d*6*x_0VoDH|80Rf( zwwojd#vT>tiw9&E!@GVOOynq-k`^|7CWdS^urB$dT{$sN$LS4L-9^Yoq0I-m?2K1^ z-dEcNC!9m{xTs6Zc!b@D+L>wt%m)eibG+D~j1I}9m8{u4*APnd;lVe!0a2r8Io|Jc zC0e}YD3DGiKXH}cX^;~wQlu!x_Z;t^;t(s24>&~)fu2a}6JK_$ebk$h6+Foq3?ueq zVzNF4CniMoAI-zPI^+vtT_ExwZHSIK`97L7V;Iw(`1V}MXiKx6A^QrR!hanKH+y&i zX>K*pXs0~>WgoF4)`=M3Sff6+eA2i@-IuTq3g4}H*o1!Xr*)Gr{qbq=<5Q4mAv3_4 zUFUe7(rA7nSceNuCm{t^#YJS$6d9bwq#v1v)dHl$n}lwEYsT20rZydd;=8F$y3rt; zfza_{o?{wvT>pU*5tHS>dDFe?kbs|#U3yQ89(<8&PpHm*_d268iKA+AlVE)PBW0EHRGMP^Uu!y2XAcM3(;6LOs&>gevBg% z;GfM1Z_}VlsrsZmvi_97!CkM%@)2@HpJ9}u_gCJ(ZO4{rtwu@e%8Be1p}N4y!OHXm zijIZ!7YCj;ido_hkAgTtAaN^0WTkJX1)}^z@pB)^2;Z|bQM~=AR!g1RapugC%H}!! z_>DR~I(k3XBPSXIc58T}tv5so3fIx+35%;QB<;G*5Y+{GFIUzkV04`SzF)WUPc&c- zI*S&a^Wf@X4rq!N{Z`bSA~@LtDhF)I5st-F5GOzzs(+RyV203LM!Fa>k!JX$S@d-F zu5YI#=y~Co<(}}3LSRNNBTsc{$LLS8ywckfqpPE3z5(izj-eZ3feUHJkXUOo-5ICr#ueqnI2*{6`$OxnY-v+n z?|xZij6Ja9A0#bBoB+!SW=n33CWl~ej&IrzSpj8 zy4RG~(8iP_wW5~emF%=OflR?Jb7Xoh!pTF*ZA5;g7-XWi`ZJAf^UM z$5!SY$LBL0|D*Wd>3xoQT+{aT{SZ7xM*0Az3F^~vBYiQ%(MuuL_uY>^X#&w3Lzv&| zrE-WUcybJ9NUm7K8GW8eJ@3MQKhrliCO zy@5JCF^TF#XUJYc!f6)4|8PY_;PG268>EM3SC)KM5jl&+n#&UdQMLnLNh?a@zmbj_ ze;r!KZ=9ntF~FH2q5{ghXYgTMCA(W0-||R8=*GNdL)?vu@mDvoiG*zW4~2|A+eE!g zOF7i*HC{(4cQ7{B855I4nSt)X`&@pHKp7XQxbc5qr!cRE75NOp zW3Ufkk?JaKrgP<5$$VD0~xElZiT;)?@01&q?vTt`~{djv0zSPDb$7Ev{ zMyC)___>OQ{IQC+4AV#myH>F3&_<7Vbj>7<5+%{nz5~g_Cx4Ja<@Ef3PyiL<3nWZf zke-?ZT$b4&=EDJ88VpWhL$oG7k&|b*9#tr5)L!^PHZm5lHp{1<*V9b43@b4o%8=DM zM9Fe69d41iIi$<78_^DrLiRJY-3H3@Y0@erw%dTv!3?*q2@ zQ*)Pw-g~fTOAEXEh4{kKkOI;BCvJ)yDOA1JU(B6b!rmPVHO(xr${Y*5M_`bS&3kJ{S%e7bpGAkz?gSGSUX|z&o5Jut z8f@za_}L9EKalRI9&# z{erJ#*5|q5LuY5Vf53$9PbbKaEA_Y5J0d$?)KDW6O3$PiDq0Nt~jUMXKXfkEZ1hYv?ouGBsFPh8xX zHOMd)y>Zy}C9$iJv2n%6_T`|NpJ#XXUV~~(15<8*EyI**zlD;NLkQGF_RDX08 zr1$g_hSvZK|6*OQYXGXL!NLV(&7)~s*(EQyc~?IJSJ_#gv!|`KIavB1UkD25?D&@! ziVN5+eIu#FTKX6SyJ+oFgPyGOo@#dJn_Dh_&VCow?t7vXu<438!G)h~h+Tu_4nICT z<)Rh$*%*DMsdWQ{_ZAtTkcu7cbU?Is=iC5oQ45okHtj5zyWu z?#DIRb2de1V>$c*PD@^8{N}rL=0t)cf#7qom+)$Zm0Of6b3@&##U#g3DoP#C!0-@J zr9^qNzU54l6PG5dI3=qR6>)U!JJ2_80KDlV}kqwlmMaSWMfl1QOZT zIP*G4l)tN+#b@DuyDOkkZ%deJWz27RYG_AQ7bRD1BY7aQSASN9GHoh4Qo8K%L4g5s zq2Fgv&iIREH5*BUx5Cce`)up-y}GIT(7g|nQC_T;vY~Tn0x~fb47OQthBZx&OfH>* zMD<$usH@uEPpa(Z<*gM^=So~RsCZ76g0q~gC@WIY9S$L7Kit3MGqfz<3sFB^K+@Hq zA09%gsNPL~dr^4^0n;Skf+P%MQcxAO|IuYni@*P*I!`a(+}^%Q=42 zB;zKvw4ub4Wy_+y&;*}LzPIH0`HvAFDT&5nzsw7!rl4908y&Tp7D!Kh|RpJgR9N+10*eqNkNn!9OwL_Ill=7E`0sx+zxI*e#$Lf z8}oB0N%7M*^%iX88BRCAW)S%C^p<>KZY#LUE^Z}*WF%L`R`3ndb)jF>K!ez|?jB(?KHWjf9Y2)R0>ZvTZ0UQteDF`r z2GJW_l7cdV1Xps`I%l#h0xF5GnU%EiY93p~PTVVg@()=BCkie;hY=!__T{Rk4fU>w z6ARVn{FwQk;R!no>P4^ha{l+|WWa#Bk+(Dt?(ECpd)5+X(KmpAZABKp>QsI3!z-23 zZ^}y(@_wp+g9227ao=PyNXoBikGtB&*)yHiY7DQ2GNLTXQ4pistM=D9)q%p6;*9HJ zukWwF?A)QX(AOlLZ){y0l+z0D%Q>d4-|uyb-=rV>nO#Gwol~y+DDE;K=k9Upy}(orUc2vQw{uO!TqkVp zCx*9C!D*-tIlL4n3+c#*4Y>6R5|YEME_-G5c3n|*Ds##NbMj3{eAAk(n}k%3WVZ8C zwu7%HqeA;;Q%-c28)JEhlx+zI@zT&CvzS&5&7smPqPl(R+{9dNXE4~|R#)Z)ZtdFd z?*AzogS7(hRtnJ>wLrcoH4>MSVG?qiks|C3i zqGp@>TP$`;4P|?HV4kgdccBVv$sV1^qik9$Bt2KV><^*xktT(!GOaJkDxb}N+*J0f z_eiSH78iMN?eBMm&Oe{?a`IKywy(~fLo1qtN9ml5=XtAac(@wXY{T@)DGH+jt@iYZ z>x@PXA$V(6LR-N;`)`r`CRa-J(`xH89eb7V50$%fpq(QXGhT5T zeJQ;7zx(T3|EiaR3eg@vo-M~N6H>^?>)HbT`tgO{-%aw}(|&t#cy}WjC?#rncy}kd z8y#6J^M5#JQ%pe7x|lN8!hk>6_VS~;?KdR732tu zfc5^-~m&DP;D{katW8hPN^pEUkl~n zQp$N55;|YoM$O-rpL9s7y=^T>EpMWaJ?ae`GKD>l;S@!^WHo4*BScX*8M3ETbos=u zN>wrSl#9N!zo3dErN7sj^S?(^(KyOE1-lXsQZ_TfqiiM5dFD|#HDAe^nR361pEOhc z7bp*@sg8nunc|t*&J92r=8%`cGc1+b^_;g~q3AG~>O2Y~`alo@Xc(%*pH$xNZOWL zD$`i`eD!8>8!rwIeP^b4cAfd6aup6x+-zIsjv9&f>4%qAa)?;L?y(;!9-BASzOGuY zy#d~A9=XrZ)vP>6!-B|(Rb@&3{MG8M@&3{`Z8xm7K#FjnBG1hTDV!;k&i)Xcke)VR zk8%q821r3#(TS8=blV|Pc5i?c!_ibkH}Fp2m$?#a|7M8j-CJlyi)Xy-&EK^YYRfz^l>f=g5Tg`D|!4ssaaLnPa4miJGsHz zBIxK%Ff!EU#~VOu34FR?zI(cXhyCNo69jPkXd^75Xjpl0h_2x9EPKQtX88m8%ilZ2 zgebsWB5(lWs46#VBd(grmFQSw-Fa8h8{nSx>lr2Ld7bYJnPJ2L;$!-erNWbJn1F^0 zMiSz$nYkixqXu~O65Cv;6J*bUj%0fIQu5X&lH&RvFhB8XaT_O;EbdYj%t%C}u!{zD{E;BY5;4{=IsqDiYzCUo?^ zP}x=pRh*Bxz)@?FPYP=$WM~x9N*cOO$~4>kV0R1_v-JZ=Kn9?~Pn0@o!rx;{eGd$J z*SNc;^U<*yira0r`404BO@4%F`=B2-Ny6l`2^A~%nKm5#V_S#x_yc&&6;IqVqy}fb zo~-$F8V^4Kf&PPH54>$Q6BY$P3WWE=!fi_nz|V!8~DaA znRsUg%SKh*A^D0 zG1J;baB|0|Z2Us|^+}!bo?YSWf_%-N$fE55{tRDQN#H8~iS-X9e7uDHK1G^oLzV}? zlm8{4GZ;Vw;MJJ5q&YbC)3LK`XqGK`)p>D)k*s|Pl z0%;kkyHVKT!TShJ1m-9kYh!J~>B+8I?#i-1XNm>8iIL=OMJm##MYOlo;>pB6)m(kn zU;JN3I{_oNUsTL&93JKL|@|N^>SyKAV!#gyrRd%59rscxrh&DKz+s0@+%xJtkq@F#=B7F^& zf&^ueY!R3R4}tNawhF`8&DTuRCp@eyC=(+&aZZT~B9a^uK?`DBSz5XX2>}D%fE|k% zDWenuaE7K7Pm$@;C(E@=SmDI%?94yI!rZAfAk{J*F$y@eka9^)#Bs|Ja^`uJm|ireT~(W)Uowz^|GNxayOOHMO4N z^*3ve_&kn0w=B%!wXBx~``AawExNIm*w2WGO<&vG+VD^1egv) z1gJDQCYgg`7r1Q>NNtx&ps1+xCd@j! zh!G-T&9heENcrKQ`XO+Pq!KOdYQ&qd!=r4eg>lB3aq#)(D*kT#w8b#KRYvJ9 zD}MT`I(^go`Nz!7&&28Mg!%^k3f>!jPumv(D)13AK|fD^S%wW^xJnDmAyzSFpWncZ zJ|rXRR_4Lpfl&rMocu5vS?NgZqMI(>wLq~QLNN^L1s9s~*%&g*OKJ*H$k(7D-`q%1 zxJ$nh`-`(;jEkG{9#yfQcJ22|J)zN}yZ)w+ksOaed;@4?(IYe-xD)NC1x$%;%v%a} zxbyl}$TCyXDvrWGAS?{+e~eE3S?OIGtZRUoM$5}UY&d1K-Ngvs;p+p3)imP!t19l1 zZN`qj{-Cok-yhRm6=XGr$&B-)$}6|Np#RoGv?$soF>;PjkCLunbk^r4^U!mPfO1W7 zJbm>D6AR)2U%Z?|gX{$Br^Sv#0fDSpbPB8-cyGacM(eWj+Q!+>|0W6D#;6+Rf+D1@yda+k~39X=RV6g^Ao z{>LQ2GXphwa!U=VExr%+sawW_c2ws=izp?Jg#6N;ZPuU8MIlYDrp?7wqhib5d%1?L ze36+Z?vTch5-L(}bicmSwTt1|Bn$oegSW^^Y`;d z@OC4f8aDvNy{_K0XClq|0}`cLc6ixTD2)esW-k)|FGsbnBO#SloA zu$YxSdhLhJm5nKeJh-nvv8#YnAWGine>I0ITXmspo{jHTY!IyGj|J~(A-7O1F(V$f z3>rE!lde=usOZql_sP{aytod*CUBLMy&0ZnvP6 z1h(Se2gs-eZi%n{daOB$gUyCt(cK4I?I;}%@V#6 zfgZYN_*X5kn~2}4{R^Ov$#PBl|0H1zaE-}Q|Ah~Q%+`Jv+5m_rzi^P!`xmJn z83)n7A^wGg`r@|L{}LFT{w4d4Y5#9Uu>OZGzAXqL;P8>y;UjwXofg8#Uu$C6Kb!+c z4HAi4?*srkVt;7#R`BB*2!EIld7ITCzl}kapTG#@aR`H{KlpzK@GrrH@dpk;P7Wf_ z;8{9*Fa-~e{s3^FD0qPNX9k%KL~g4=%>tvJ1p)k4Fas961E{-h6bA8I-9W@`1fDkp z|DpZC=nn1<%Ateko$@<70e}k3!&dwd006dD5WCY!eM^G8%|hH3K>`1Nz;9y!!1$KJ z2L5B*7SZqrqdWM+7yzafq#*i(N`*m00sQ|7zm56PG9VJ9gB3x5hg^TbAI1O>1|ShK z6kq@oh%7Mr3DEW+a@PT1dTR;Iowd;9Ao>%~@oy2~AGx~;=kcwz(7z{-ADrJ^hf}Z*thd_c#vE4#?SJvOkO*Rthhp z|JpEdyX_hM2H5S)FnYdwpJG`5!wnFourlbE73Ln%;U{#2jZiTrGCw>tYCmd1clxH8 zPqXY2i$y&$WB7GIiHA!XwJfJHO<~pZYsN6D5`V4sL4~pbJ5_Ui`nR^%J;@JrVG*-3 z&*ZS1qMk)y2~oTx{bIXemqpKhwH3>T5C*Xq{FpSUOy&;#?wdVC0Od)HE1`c&dwIQEdd2QpGm!c-r z`=-z51R#u0ukA6fBv4l)rIYqujq<*4zoP4QFOe^u*V?`o&j>7DTx^Dj?Mi|Fld}6b---s2p#uU#A(JWfxI5)wWLK4&%sU_BdQ3Z)fUmK}FBFC@ z2P`Px!um!A1cuA0mBU1{un>nS>aSYo$|EGveA=zO@*z-^JQS>F|un9*r<6M|tp9f!e> z0LpCUY&Ka|#e(r<(r9-=!fS?U5#pcekt{j=qF%u;+;iT0lDjT)ly$D)T_mVN#(oC4`H)_&mFnAVR+ALoRb>4i627oT%UxK~|@1tzQu8dEdIZ z=in&%It!LbLn;4V&F5KSmZ6@hBmKS~9C1FPe=4gu$qCFGW5bVs8=Xx;!R2?tePO zHXABh24l9N^1^? zRQ!c?hPSr8ZRFW21-d$piLm-XRJW)=Q?4MftC@E2Rg(-Jw-?H#sz4cYTiqmwKZ7uY zy}c_c<)b2QGVv&i^Zb|y>rd^xareG<$$ep@tc%s0-%;*lkWL7*&L>FgIuS09(VpGX zuyBwG|AItf-K+C3FbJAKVW*?2;vKx?q`>QVLW{M^kz86-8oPG{iQ9TFp&v)HSb%5R z@RHI?pdL+*urA5J# zGO*Aog`IWpo;{Pt6e`Pywx@V8wYvSU9xeU2K1y1%SyeM+MDghTqgK^vye$e-+C|gm z`SOjcQS`=EcZq2ZJ%~)_d+e17@dw!Tino=Mus_VMR9l?buW2M^y|a37^4!d#B%%!~ zj9?b_xyRuNDwcb5)Z2ZVf!^`1_QB*sb=Stklz6enY*>2twZK~QHHX55aTp_v`w|6S z{Iq8vKJ7{73U>;ZDGPnh$?)-1__#DIL!;U{DyH?5HUpYU3+DR?UrXeqeztj2CdmBy z+3Q6e`LBuKjAm5ME|4lq$Y-{vgtp1?*w=n_Pe2JDQt?vY%vvCyfMo63v!{_g34L+IQ_ z5Ah)%M}zv|M6HE!Dt6WDSq86=C{$IiX_MqXrDTmb({`0e_vGDUnm?M5WPQmEM1n1Qnnuu+V=qpH&FA^|lOrhjv1$`2_rlxskC8 z6;t}1tX*V;GKjV78vrsbPd1&UBZ_=FnJP|P%PhOOa{Ric_e*UcibidvsC(q~fFxS0aVp7s5S_@Eof@~E_ zen0kaQeyy+5K!Q+=Kc5`g4=}&20+fnPI3EO!y4je90amK z6m~qvQ$j_5l@-)M?S}}M!Uwz#RtYE`7Q|GXL}a1cQMU6p6~@pD!cmK1Z+WCO&LHCD zZwiPYCReX;lV>BzzE>+Q0)ks1%2X8)qFxOkC8(;pwm?CmM5wB&s)$KmTU!nbOXR^1 zwQ*`ah8GhB(9g`Qsk2s5?;RpfJ{E2X=hS>C92j>f{HWt=M%<{U{NwG1w0YEN{S$$7 zkHhvr{7>Ys882g<+~FW0!^Y)uT(FAL#ZVAARnmZXpkE#1LD)Eene|~fY+ax*{wPC% z+R`I7R-n7KyN5Rvg-ZU58%1E9r7Qj1k6w~49Gp=U|4;`sFC|x zVir~bT6E2_1j z$iX6q*UuWL@(H!7vF(}V#fTWH6KQoCbcpm6tt?uxvjwfIb`2v>Vx-h0S*jF%7SfP^ z9Un4ofLP9xq4|a#O-#>_T5shPul`-T{@M^6 z{zZUSH8Btm0rM8NW}!N0U7s37#y7x%4I^Ye;Z+cYH1Gx>2q{KFn1Q$-hDrS>hEn`@ zg6;&cBgJnB`-}cTr-0+k!IbmM^Sk>r3NE6dU}0eXyr5>M5K(;g;ebup&>;q0A-i(q z(185q@*kf@!4-8Y;(k7^j%LfptF5g) z9GPq3IsFExC0GV>Ll0lhW;z2yWKidyvM2^ydP7LwVS8$Tn^4;<&cR=Z!3g7e3rVdm zH=p`G^aCQU>vIw<$mAXP%x#;OH52lO#+aR%sVDV2?IF8H<+5Lu6Ovp*fhi+zKA{x? z^TfDJ?UFnm>Xd@!GL5!vgBW94nlf&12VV%J|D!$F8Bt8n;sNy}5xPpcq%il+7t&Z( z9i2Mh0rq<+&nH@@;2+7WDy!qY%Ai%{I=-qETY1p^O%HMzbpf_n^f(LS@GnBevmCSfYey<_u! z843jjFD~~Y%Dfo6`0|Dceecwfd#VYuqK|psWI3Z!vbxJA*ug{B^;YaN!r7YuCVm43 z0_1D0&`1j9J`~+qJ%rJiX*4cqofoHlm8$I1hbZA;5UT#{g0XTAQZt`=%L}yBPaM?R zbiPN4eO}DV7+AqIsJY~%-%wy`GFKL){6DQ(S6CCs77iE|q!WyS zASeNXp=zXv2#8cc>V{$~*>4ON0b=uN!2yN~x_ z_kQ<2&CGw!{O6pR^PTx-M$nkW?8Fcd34W2aXRA50xYct;thj??O6n>=W);sLFxd>tA!Tfjc)O=T9s1M- ztWYIMwwi#&R)ROGx(L)|`A(n_sDU`PAojlO4n)R<&>hV4-6d*1682jlD7@T+;FmJb zcCnCO^Ci780<@JEbt~P?%L(q4{ShyA=Jf|Y^ZTB=oRukMG>MN5Gz4ozU>dLtqg09_ z%rXV{mN*`EX_>S~81?Pt&@}7N2Lh@1_btW;QXTNiwa8)Ah*16 zr99+ta0s=l;K_&22SP3Q><7lguaTLoZ9KVB^AeYB{LWoE8#S-mSmIj+vt7>^+>UNz zBORs=GU3D#hv7zpThFt!wZF?Bs6n^GBZp}|rGh#{Dx2ia0&`riP)|l6*d<*bNARbo z*jjhPR{HK$wNxo%Zme2NCzlJP6(cPi$y*>=nstS;R9D^)!6kJ-O$<70#ZAa-#kw7w z+!p!KJ32GCpY3KL26GL(_gi*7&k@1JP(<10k2*BB=I^L+$=SVRjxLN;1PC^i9$6IW zDv-<;KZF_SMcRR01gGB$MpL)$s% zDNvLBuGDIL{k`bM_l|=QCtEIe^|>_F`LTZl(!$ZapX|X2m0^AW7L(x(dS?kzlpn4} zM~Q3^CR3?1)6piM|6g+cy>(rvZ;cE0#ljm=fddZ(8Sd-x%e3zs>nrApLp#?=?_uMJ zB58uOnf5EEj-*W7=Js2pHXxA7f+t+v4?4WB67mJ*jB{$rpb!X^2z8Jdxi1BR0J-b4 zk#=*@o`=!X2HA6!%d6oj5)9Kh${#=png~+K-1?M$s6l?Y{CLxb`X9VE-NYm)O7? z+*z-dt-&lXQg)0}uwU@J|D&1tiDCilpw3@&%t~;`;r7z((Vr2-=aoI2(lzPFQSkww z7PmGptrE^qq~hW4Gj4*pcU{(LN0~m{SWBs+1axL=0+H;h`T^a1rE$hP_Og3F`yUP} zF@|Ux`2gspqMrMJw_3r{tR2`x5c>JjPYL7Z>TL-gMR z*SrRb#g!G>Wj{$J%9Muli#n`{tDrW4nA_WiMRfcjheq8X{ltRXPxRwK?PbEW7jSp| zMzcQI8s*u{vB288)x6XDg>X@08lxIO&pudQ>byhiJ-3Y#WsdUl2K_NI@zoRFu{Zm< zKZO3XbNl*$XY(EBEZ3sC-YCVvc=kqi4zlR)NaUA44&7qHS%o8Kfa(&u(_dw;JmjB! z(Ws!N{?R;_UViEk}l!Ax`sDc!W>wGFuB`h?sEA#8J-lP zqjfZs87dX*_c-g3C+Z2-$xP3*GfSqrBO%+oOCUv3=V#;N5Ok)S+`d1Xf=7FLOI5` zHySE@cVp7x3>|XtXn+LW2it&Pm&k!(M0y{Ml5*$gEJt`)=)Njmo4-o$a=MehLAQi2 zy0GF`vVEekU0)f3sM%#?Z%kNA^7!2=c1#VSAogqkoR^YipMX!T+`19Rt*4>F&VyR? z>M5M*k3hqrO!YT|j9tZmF^H+*H}8DJ`7E6__UK0U=P8H4EMWn$FFq_vxf135p!q0M z4F*zbS)!5*2kB zE+u>7{#Zo;4<{DO-O!&*MO5XUs13qx7WJKcT6}mBsbp&!Hm7m}hOt+@ zW!}bBplE92u`O!(F57(w9`Tf#rC)f2{v}!Pyw`^8L3!0Rb3xla{b zDh8b5KgYFi5&aiN9!Y7NKQ$$FW!~Yc#Yt3m#K*|nU7O+usjsLQ zF~L$UQeaiIsYMi?Tzz~;Nzn3OgDk3Jk6bw6^`9%tW|7eh|v49W(tElPjZw<1#VGZa6;q)b#_ftOrJ*|<>a=*TzkVqud)gD8QO{V#G8Yi16| z0YNJv#WRvS(-ALl3+Zq=wk6EI)=xmQ6H+Wn4=2Xoor3<}b2B^7TBQoCed^m%gn%U#-f}5TM>)Gx zwuEz=H3#(oo1+nz0f--N@_F6AEk4l7^g(0c!ApY~;O+8nFc}>9{>^^>)3PR?v{8H< zX*gDX1Fz+AtvpoSmhV~VbjjepoW0}Q($vGte&e$`VAjN7CWyS={`g(mtewSP&FJNEr&b2balNujfQCbEQ(!burc1D7w#DFK zXRIn;8>g)6rj_@uC3V*?Hcs)99*x-`#r=SWu7fG*@~T$PXcE@h>^EN$*R=6s)8B&@ zoT-trpKy!FQP)Jsn&WYh^NFcv@lt`hTFU5Hpx{5^s^rI8T~xtS#0}5Gy&|jGXG2!m z%C$YpVdOaT5&0w|L3C}Pp(8u+D4nLV6{9oGVfuTPxqSP?N~@s4%kn@lbkU$%m~iZW zD$0!F5dVC5StTxDQkq1{2zSK(SVf9REqqBLe{=X4Ya`!YNM&BUf55b1vpTtY1w;QK z8QNUL`p6f}=D<3$B|`X^pl)n^sFt?3J~9Y@{fY8D1!6g-wZlsN1faHpaD=0zoq$td zrlDt7Px>Y^W;4EQPImRxz=czlg!X%0I4XU_58IKM18ZUvR{T7PYBhAum8|Ne6!YwQ zM$C01g;z(`YXoULEdsc5T1qgrV8ao4aGRNQuZBS-KCIf^whDFc57DISpk` zTfW9!#>zFLfF5N-y88HBNOkIcV5(qT&Y+)k@Njr)aATYM2Dhof4m(EzcxyOVDmlKU zCc5CgqbS7ImP=E8_L=I~*#853Euy39A6M|s{76K7q*5%N9uzhzaCgM@>w(>j)>i1% sL%wvZ$zv*Ey8qdt%FUzwTg|+nF!9b#d4rcXZ!x&;iT(t1Z2u(u3!wkLw*UYD literal 0 HcmV?d00001 diff --git a/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts b/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts new file mode 100644 index 00000000000..2bdaa217a9f --- /dev/null +++ b/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts @@ -0,0 +1,107 @@ +import { DynamicStructuredTool } from '../OpenAPIToolkit/core' +import { z } from 'zod/v3' +import { secureFetch } from '../../../src/httpSecurity' +import { getBaseClasses, getCredentialData, getCredentialParam, handleErrorMessage } from '../../../src/utils' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' + +class TelnyxVerifyCheckTool extends DynamicStructuredTool { + constructor(apiKey: string, defaultVerifyProfileId?: string) { + super({ + name: 'telnyx_verify_check', + description: 'Check a verification code with Telnyx Verify. Use this to validate an OTP submitted by a user for a phone number.', + schema: z.object({ + phoneNumber: z.string().describe('Phone number in E.164 format'), + code: z.string().describe('Verification code submitted by the user'), + verifyProfileId: z.string().optional().describe('Telnyx Verify Profile ID to use when checking the OTP code') + }), + baseUrl: '', + method: 'POST', + headers: {} + }) + this.apiKey = apiKey + this.defaultVerifyProfileId = defaultVerifyProfileId + } + + apiKey: string + defaultVerifyProfileId?: string + + async _call(arg: any): Promise { + const verifyProfileId = arg.verifyProfileId || this.defaultVerifyProfileId + if (!verifyProfileId) { + throw new Error('Telnyx Verify Check requires a Verify Profile ID') + } + + const body = { + phone_number: arg.phoneNumber, + verify_profile_id: verifyProfileId, + code: arg.code + } + + try { + const res = await secureFetch('https://api.telnyx.com/v2/verifications/by_phone_number/actions/verify', { + method: 'POST', + headers: { + Authorization: `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }) + + const text = await res.text() + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${text}`) + } + return text + } catch (error) { + throw new Error(handleErrorMessage(error)) + } + } +} + +class TelnyxVerifyCheck_Tools implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx Verify Check' + this.name = 'telnyxVerifyCheck' + this.version = 1.0 + this.type = 'TelnyxVerifyCheck' + this.icon = 'telnyx.png' + this.category = 'Tools' + this.description = 'Validate OTP codes through the Telnyx Verify API after the user submits a verification code.' + this.baseClasses = [this.type, 'Tool', ...getBaseClasses(TelnyxVerifyCheckTool)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'] + } + this.inputs = [ + { + label: 'Default Verify Profile ID', + name: 'verifyProfileId', + type: 'string', + description: 'Optional default Telnyx Verify Profile ID used when validating OTP codes.', + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + const verifyProfileId = nodeData.inputs?.verifyProfileId as string | undefined + return new TelnyxVerifyCheckTool(apiKey, verifyProfileId) + } +} + +module.exports = { nodeClass: TelnyxVerifyCheck_Tools } diff --git a/packages/components/nodes/tools/TelnyxVerifyCheck/telnyx.png b/packages/components/nodes/tools/TelnyxVerifyCheck/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 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 literal 0 HcmV?d00001 diff --git a/packages/components/nodes/tools/TelnyxVerifySend/TelnyxVerifySend.ts b/packages/components/nodes/tools/TelnyxVerifySend/TelnyxVerifySend.ts new file mode 100644 index 00000000000..d9afc29f772 --- /dev/null +++ b/packages/components/nodes/tools/TelnyxVerifySend/TelnyxVerifySend.ts @@ -0,0 +1,107 @@ +import { DynamicStructuredTool } from '../OpenAPIToolkit/core' +import { z } from 'zod/v3' +import { secureFetch } from '../../../src/httpSecurity' +import { getBaseClasses, getCredentialData, getCredentialParam, handleErrorMessage } from '../../../src/utils' +import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' + +class TelnyxVerifySendTool extends DynamicStructuredTool { + constructor(apiKey: string, defaultVerifyProfileId?: string) { + super({ + name: 'telnyx_verify_send', + description: 'Send a verification code with Telnyx Verify. Use this to start an OTP verification flow for a phone number.', + schema: z.object({ + phoneNumber: z.string().describe('Destination phone number in E.164 format'), + verifyProfileId: z.string().optional().describe('Telnyx Verify Profile ID to use for this verification request'), + channel: z.enum(['sms', 'call']).optional().describe('Verification channel to use when delivering the OTP code') + }), + baseUrl: '', + method: 'POST', + headers: {} + }) + this.apiKey = apiKey + this.defaultVerifyProfileId = defaultVerifyProfileId + } + + apiKey: string + defaultVerifyProfileId?: string + + async _call(arg: any): Promise { + const verifyProfileId = arg.verifyProfileId || this.defaultVerifyProfileId + if (!verifyProfileId) { + throw new Error('Telnyx Verify Send requires a Verify Profile ID') + } + + const body = { + phone_number: arg.phoneNumber, + verify_profile_id: verifyProfileId, + channel: arg.channel || 'sms' + } + + try { + const res = await secureFetch('https://api.telnyx.com/v2/verifications', { + method: 'POST', + headers: { + Authorization: `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }) + + const text = await res.text() + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${text}`) + } + return text + } catch (error) { + throw new Error(handleErrorMessage(error)) + } + } +} + +class TelnyxVerifySend_Tools implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx Verify Send' + this.name = 'telnyxVerifySend' + this.version = 1.0 + this.type = 'TelnyxVerifySend' + this.icon = 'telnyx.png' + this.category = 'Tools' + this.description = 'Start an OTP verification flow through the Telnyx Verify API.' + this.baseClasses = [this.type, 'Tool', ...getBaseClasses(TelnyxVerifySendTool)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'] + } + this.inputs = [ + { + label: 'Default Verify Profile ID', + name: 'verifyProfileId', + type: 'string', + description: 'Optional default Telnyx Verify Profile ID used when sending OTP codes.', + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + const verifyProfileId = nodeData.inputs?.verifyProfileId as string | undefined + return new TelnyxVerifySendTool(apiKey, verifyProfileId) + } +} + +module.exports = { nodeClass: TelnyxVerifySend_Tools } diff --git a/packages/components/nodes/tools/TelnyxVerifySend/telnyx.png b/packages/components/nodes/tools/TelnyxVerifySend/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 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 literal 0 HcmV?d00001 From b08dedbfec94380751b8536e567b2b2e1b0523c7 Mon Sep 17 00:00:00 2001 From: gbattistel <4660743+gbattistel@users.noreply.github.com> Date: Fri, 15 May 2026 14:42:49 -0400 Subject: [PATCH 2/5] feat(models): add Telnyx chat and embeddings providers --- .../nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts | 130 ++++++++++++++++++ .../nodes/chatmodels/ChatTelnyx/README.md | 13 ++ .../nodes/chatmodels/ChatTelnyx/telnyx.png | Bin 0 -> 25250 bytes .../embeddings/TelnyxEmbedding/README.md | 13 ++ .../TelnyxEmbedding/TelnyxEmbedding.ts | 119 ++++++++++++++++ .../embeddings/TelnyxEmbedding/telnyx.png | Bin 0 -> 25250 bytes 6 files changed, 275 insertions(+) create mode 100644 packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts create mode 100644 packages/components/nodes/chatmodels/ChatTelnyx/README.md create mode 100644 packages/components/nodes/chatmodels/ChatTelnyx/telnyx.png create mode 100644 packages/components/nodes/embeddings/TelnyxEmbedding/README.md create mode 100644 packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts create mode 100644 packages/components/nodes/embeddings/TelnyxEmbedding/telnyx.png diff --git a/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts b/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts new file mode 100644 index 00000000000..2d9f66f7059 --- /dev/null +++ b/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts @@ -0,0 +1,130 @@ +import { ChatOpenAI, ChatOpenAIFields } from '@langchain/openai' +import { BaseCache } from '@langchain/core/caches' +import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface' +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' + +const TELNYX_OPENAI_BASE = 'https://api.telnyx.com/v2/ai/openai' +const TELNYX_CHAT_MODELS_URL = 'https://api.telnyx.com/v2/ai/openai/models' + +const fetchTelnyxModels = async (apiKey: string) => { + const response = await fetch(TELNYX_CHAT_MODELS_URL, { + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json' + } + }) + + if (!response.ok) { + throw new Error(`Failed to fetch Telnyx models: ${response.status} ${response.statusText}`) + } + + const json = await response.json() + return json.data || [] +} + +class ChatTelnyx_ChatModels implements INode { + label: string + name: string + version: number + type: string + icon: string + category: string + description: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx Chat' + this.name = 'chatTelnyx' + this.version = 1.1 + this.type = 'ChatTelnyx' + this.icon = 'telnyx.png' + this.category = 'Chat Models' + this.description = 'Use Telnyx OpenAI-compatible chat completions as a native Flowise chat model' + this.baseClasses = [this.type, ...getBaseClasses(ChatOpenAI)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'], + refresh: true + } + this.inputs = [ + { label: 'Cache', name: 'cache', type: 'BaseCache', optional: true }, + { label: 'Model Name', name: 'modelName', type: 'asyncOptions', loadMethod: 'listModels', default: 'openai/gpt-4o', refresh: true }, + { label: 'Temperature', name: 'temperature', type: 'number', step: 0.1, default: 0.9, optional: true }, + { label: 'Streaming', name: 'streaming', type: 'boolean', default: true, optional: true, additionalParams: true }, + { label: 'Max Tokens', name: 'maxTokens', type: 'number', step: 1, optional: true, additionalParams: true }, + { label: 'Top Probability', name: 'topP', type: 'number', step: 0.1, optional: true, additionalParams: true }, + { label: 'Frequency Penalty', name: 'frequencyPenalty', type: 'number', step: 0.1, optional: true, additionalParams: true }, + { label: 'Presence Penalty', name: 'presencePenalty', type: 'number', step: 0.1, optional: true, additionalParams: true }, + { label: 'Timeout', name: 'timeout', type: 'number', step: 1, optional: true, additionalParams: true } + ] + } + + //@ts-ignore + loadMethods = { + async listModels(nodeData: INodeData, options: ICommonObject): Promise { + const credentialId = nodeData.credential || nodeData.inputs?.credentialId + if (!credentialId) { + return [{ label: 'Select a Telnyx API credential to load models', name: 'openai/gpt-4o' }] + } + + try { + const credentialData = await getCredentialData(credentialId as string, options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + const models = await fetchTelnyxModels(apiKey) + + return models + .map((model: any) => ({ + label: model.id, + name: model.id, + description: [model.task, model.context_length ? `context ${model.context_length}` : '', model.tier || ''] + .filter(Boolean) + .join(' • ') + })) + } catch (error) { + console.warn('Falling back to static Telnyx chat model list:', error) + return [{ label: 'openai/gpt-4o', name: 'openai/gpt-4o' }] + } + } + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const temperature = nodeData.inputs?.temperature as string + const modelName = nodeData.inputs?.modelName as string + const maxTokens = nodeData.inputs?.maxTokens as string + const topP = nodeData.inputs?.topP as string + const frequencyPenalty = nodeData.inputs?.frequencyPenalty as string + const presencePenalty = nodeData.inputs?.presencePenalty as string + const timeout = nodeData.inputs?.timeout as string + const streaming = nodeData.inputs?.streaming as boolean + const cache = nodeData.inputs?.cache as BaseCache + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + + const obj: ChatOpenAIFields = { + temperature: parseFloat(temperature), + modelName, + openAIApiKey: apiKey, + apiKey, + streaming: streaming ?? true, + configuration: { + baseURL: TELNYX_OPENAI_BASE + } + } + + if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10) + if (topP) obj.topP = parseFloat(topP) + if (frequencyPenalty) obj.frequencyPenalty = parseFloat(frequencyPenalty) + if (presencePenalty) obj.presencePenalty = parseFloat(presencePenalty) + if (timeout) obj.timeout = parseInt(timeout, 10) + if (cache) obj.cache = cache + + return new ChatOpenAI(obj) + } +} + +module.exports = { nodeClass: ChatTelnyx_ChatModels } diff --git a/packages/components/nodes/chatmodels/ChatTelnyx/README.md b/packages/components/nodes/chatmodels/ChatTelnyx/README.md new file mode 100644 index 00000000000..4ca10e66776 --- /dev/null +++ b/packages/components/nodes/chatmodels/ChatTelnyx/README.md @@ -0,0 +1,13 @@ +# Telnyx Chat Model + +Telnyx Chat Model integration for Flowise + +## 🌱 Env Variables + +| Variable | Description | Type | Default | +| --------------- | ----------------------------------------------------- | ------ | ------- | +| TELNYX_API_KEY | Default `credential.apiKey` for the Telnyx API | String | | + +## License + +Source code in this repository is made available under the [Apache License Version 2.0](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md). diff --git a/packages/components/nodes/chatmodels/ChatTelnyx/telnyx.png b/packages/components/nodes/chatmodels/ChatTelnyx/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 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 literal 0 HcmV?d00001 diff --git a/packages/components/nodes/embeddings/TelnyxEmbedding/README.md b/packages/components/nodes/embeddings/TelnyxEmbedding/README.md new file mode 100644 index 00000000000..f4686a26ce8 --- /dev/null +++ b/packages/components/nodes/embeddings/TelnyxEmbedding/README.md @@ -0,0 +1,13 @@ +# Telnyx Embedding Model + +Telnyx Embedding Model integration for Flowise + +## 🌱 Env Variables + +| Variable | Description | Type | Default | +| --------------- | ----------------------------------------------------- | ------ | ------- | +| TELNYX_API_KEY | Default `credential.apiKey` for the Telnyx API | String | | + +## License + +Source code in this repository is made available under the [Apache License Version 2.0](https://github.com/FlowiseAI/Flowise/blob/master/LICENSE.md). diff --git a/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts b/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts new file mode 100644 index 00000000000..50c21602bb4 --- /dev/null +++ b/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts @@ -0,0 +1,119 @@ +import { ClientOptions, OpenAIEmbeddings, OpenAIEmbeddingsParams } from '@langchain/openai' +import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface' +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' + +const TELNYX_OPENAI_BASE = 'https://api.telnyx.com/v2/ai/openai' +const TELNYX_EMBEDDINGS_MODELS_URL = 'https://api.telnyx.com/v2/ai/embeddings/models' + +const fetchTelnyxModels = async (apiKey: string) => { + const response = await fetch(TELNYX_EMBEDDINGS_MODELS_URL, { + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json' + } + }) + + if (!response.ok) { + throw new Error(`Failed to fetch Telnyx models: ${response.status} ${response.statusText}`) + } + + const json = await response.json() + return json.data || [] +} + +class TelnyxEmbedding_Embeddings implements INode { + label: string + name: string + version: number + type: string + icon: string + category: string + description: string + baseClasses: string[] + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Telnyx Embeddings' + this.name = 'telnyxEmbeddings' + this.version = 1.1 + this.type = 'TelnyxEmbeddings' + this.icon = 'telnyx.png' + this.category = 'Embeddings' + this.description = 'Use Telnyx OpenAI-compatible embeddings as a native Flowise embeddings node' + this.baseClasses = [this.type, ...getBaseClasses(OpenAIEmbeddings)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'], + refresh: true + } + this.inputs = [ + { label: 'Model Name', name: 'modelName', type: 'asyncOptions', loadMethod: 'listModels', default: 'text-embedding-3-small', refresh: true }, + { label: 'Strip New Lines', name: 'stripNewLines', type: 'boolean', optional: true, additionalParams: true }, + { label: 'Batch Size', name: 'batchSize', type: 'number', optional: true, additionalParams: true }, + { label: 'Timeout', name: 'timeout', type: 'number', optional: true, additionalParams: true }, + { label: 'Dimensions', name: 'dimensions', type: 'number', optional: true, additionalParams: true }, + { label: 'Encoding Format', name: 'encodingFormat', type: 'options', options: [{ label: 'float', name: 'float' }, { label: 'base64', name: 'base64' }], optional: true, additionalParams: true } + ] + } + + //@ts-ignore + loadMethods = { + async listModels(nodeData: INodeData, options: ICommonObject): Promise { + const credentialId = nodeData.credential || nodeData.inputs?.credentialId + if (!credentialId) { + return [{ label: 'Select a Telnyx API credential to load models', name: 'text-embedding-3-small' }] + } + + try { + const credentialData = await getCredentialData(credentialId as string, options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + const models = await fetchTelnyxModels(apiKey) + + return models + .map((model: any) => ({ + label: model.id, + name: model.id, + description: [model.task, model.context_length ? `context ${model.context_length}` : '', model.tier || ''] + .filter(Boolean) + .join(' • ') + })) + } catch (error) { + console.warn('Falling back to static Telnyx embeddings model list:', error) + return [{ label: 'text-embedding-3-small', name: 'text-embedding-3-small' }] + } + } + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const stripNewLines = nodeData.inputs?.stripNewLines as boolean + const batchSize = nodeData.inputs?.batchSize as string + const timeout = nodeData.inputs?.timeout as string + const modelName = nodeData.inputs?.modelName as string + const dimensions = nodeData.inputs?.dimensions as string + const encodingFormat = nodeData.inputs?.encodingFormat as 'float' | 'base64' | undefined + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + + const obj: Partial & { openAIApiKey?: string; configuration?: ClientOptions } = { + openAIApiKey: apiKey, + modelName, + configuration: { + baseURL: TELNYX_OPENAI_BASE + } + } + + if (stripNewLines) obj.stripNewLines = stripNewLines + if (batchSize) obj.batchSize = parseInt(batchSize, 10) + if (timeout) obj.timeout = parseInt(timeout, 10) + if (dimensions) obj.dimensions = parseInt(dimensions, 10) + if (encodingFormat) obj.encodingFormat = encodingFormat + + return new OpenAIEmbeddings(obj) + } +} + +module.exports = { nodeClass: TelnyxEmbedding_Embeddings } diff --git a/packages/components/nodes/embeddings/TelnyxEmbedding/telnyx.png b/packages/components/nodes/embeddings/TelnyxEmbedding/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 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 literal 0 HcmV?d00001 From 5e49542adcc88f54fa15a505ed5e4c48f1bc22ce Mon Sep 17 00:00:00 2001 From: gbattistel <4660743+gbattistel@users.noreply.github.com> Date: Fri, 15 May 2026 14:42:50 -0400 Subject: [PATCH 3/5] feat(audio): add Telnyx STT and TTS providers --- packages/components/src/speechToText.ts | 28 ++++++++- packages/components/src/textToSpeech.ts | 37 ++++++++++- packages/ui/src/assets/images/telnyx.png | Bin 0 -> 25250 bytes .../ui-component/extended/SpeechToText.jsx | 56 +++++++++++++++-- .../ui-component/extended/TextToSpeech.jsx | 59 +++++++++++++++++- 5 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 packages/ui/src/assets/images/telnyx.png diff --git a/packages/components/src/speechToText.ts b/packages/components/src/speechToText.ts index e59368a5eae..b413ad0db58 100644 --- a/packages/components/src/speechToText.ts +++ b/packages/components/src/speechToText.ts @@ -11,7 +11,8 @@ const SpeechToTextType = { ASSEMBLYAI_TRANSCRIBE: 'assemblyAiTranscribe', LOCALAI_STT: 'localAISTT', AZURE_COGNITIVE: 'azureCognitive', - GROQ_WHISPER: 'groqWhisper' + GROQ_WHISPER: 'groqWhisper', + TELNYX_STT: 'telnyxStt' } export const convertSpeechToText = async (upload: IFileUpload, speechToTextConfig: ICommonObject, options: ICommonObject) => { @@ -81,7 +82,7 @@ export const convertSpeechToText = async (upload: IFileUpload, speechToTextConfi const formData = new FormData() const audioBlob = new Blob([new Uint8Array(audio_file)], { type: upload.type }) - formData.append('audio', audioBlob, upload.name) + formData.append('file', audioBlob, upload.name) const channelsStr = speechToTextConfig.channels || '0,1' const channels = channelsStr.split(',').map(Number) @@ -108,6 +109,29 @@ export const convertSpeechToText = async (upload: IFileUpload, speechToTextConfi throw error.response?.data || error } } + + case SpeechToTextType.TELNYX_STT: { + const formData = new FormData() + const audioBlob = new Blob([new Uint8Array(audio_file)], { type: upload.type || 'audio/mpeg' }) + formData.append('file', audioBlob, upload.name) + formData.append('model', speechToTextConfig?.model || 'openai/whisper-large-v3-turbo') + if (speechToTextConfig?.language) formData.append('language', speechToTextConfig.language) + if (speechToTextConfig?.prompt) formData.append('prompt', speechToTextConfig.prompt) + if (speechToTextConfig?.temperature) formData.append('temperature', speechToTextConfig.temperature) + + const response = await axios.post('https://api.telnyx.com/v2/ai/audio/transcriptions', formData, { + headers: { + Authorization: `Bearer ${credentialData.apiKey}`, + Accept: 'application/json' + } + }) + + const text = response?.data?.data?.text || response?.data?.text || response?.data?.transcript + if (text) { + return text + } + break + } case SpeechToTextType.GROQ_WHISPER: { const groqClient = new Groq({ apiKey: credentialData.groqApiKey diff --git a/packages/components/src/textToSpeech.ts b/packages/components/src/textToSpeech.ts index c4611806406..e3c7c99a5f6 100644 --- a/packages/components/src/textToSpeech.ts +++ b/packages/components/src/textToSpeech.ts @@ -3,11 +3,13 @@ import { getCredentialData } from './utils' import OpenAI from 'openai' import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js' import { Readable } from 'node:stream' +import axios from 'axios' import type { ReadableStream } from 'node:stream/web' const TextToSpeechType = { OPENAI_TTS: 'openai', - ELEVEN_LABS_TTS: 'elevenlabs' + ELEVEN_LABS_TTS: 'elevenlabs', + TELNYX_TTS: 'telnyxTts' } export const convertTextToSpeechStream = async ( @@ -100,6 +102,39 @@ export const convertTextToSpeechStream = async ( }) break } + + + case TextToSpeechType.TELNYX_TTS: { + onStart((textToSpeechConfig.output_format || 'mp3') as string) + + const response = await axios.post( + 'https://api.telnyx.com/v2/text-to-speech/speech', + { + text, + voice: textToSpeechConfig.voice || 'Telnyx.NaturalHD.astra', + output_format: textToSpeechConfig.output_format || 'mp3', + sample_rate: textToSpeechConfig.sample_rate ? Number(textToSpeechConfig.sample_rate) : undefined, + language_code: textToSpeechConfig.language_code || undefined, + speed: textToSpeechConfig.speed ? Number(textToSpeechConfig.speed) : undefined + }, + { + headers: { + Authorization: `Bearer ${credentialData.apiKey}`, + 'Content-Type': 'application/json', + Accept: 'audio/mpeg' + }, + responseType: 'arraybuffer', + signal: abortController.signal + } + ) + + const buffer = Buffer.from(response.data) + const stream = Readable.from(buffer) + await processStreamWithRateLimit(stream, onChunk, onEnd, resolve, reject, 640, 20, abortController, () => { + streamDestroyed = true + }) + break + } } } else { reject(new Error('Text to speech is not selected. Please configure TTS in the chatflow.')) diff --git a/packages/ui/src/assets/images/telnyx.png b/packages/ui/src/assets/images/telnyx.png new file mode 100644 index 0000000000000000000000000000000000000000..13ea5d395e534ba88cdc5290f17292910a088471 GIT binary patch literal 25250 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/ui-component/extended/SpeechToText.jsx b/packages/ui/src/ui-component/extended/SpeechToText.jsx index 2ca7fd95c28..cf38731dce2 100644 --- a/packages/ui/src/ui-component/extended/SpeechToText.jsx +++ b/packages/ui/src/ui-component/extended/SpeechToText.jsx @@ -20,6 +20,7 @@ import assemblyAIPng from '@/assets/images/assemblyai.png' import localAiPng from '@/assets/images/localai.png' import azureSvg from '@/assets/images/azure_openai.svg' import groqPng from '@/assets/images/groq.png' +import telnyxPng from '@/assets/images/telnyx.png' // store import useNotifier from '@/utils/useNotifier' @@ -34,7 +35,8 @@ const SpeechToTextType = { ASSEMBLYAI_TRANSCRIBE: 'assemblyAiTranscribe', LOCALAI_STT: 'localAISTT', AZURE_COGNITIVE: 'azureCognitive', - GROQ_WHISPER: 'groqWhisper' + GROQ_WHISPER: 'groqWhisper', + TELNYX_STT: 'telnyxStt' } // Weird quirk - the key must match the name property value. @@ -56,7 +58,7 @@ const speechToTextProviders = { name: 'language', type: 'string', description: - 'The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.', + 'Optional language of the input audio. Supplying the language can improve recognition accuracy. Supplying the input language in ISO-639-1 format will improve accuracy and latency.', placeholder: 'en', optional: true }, @@ -115,7 +117,7 @@ const speechToTextProviders = { name: 'language', type: 'string', description: - 'The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.', + 'Optional language of the input audio. Supplying the language can improve recognition accuracy. Supplying the input language in ISO-639-1 format will improve accuracy and latency.', placeholder: 'en', optional: true }, @@ -197,6 +199,52 @@ const speechToTextProviders = { } ] }, + [SpeechToTextType.TELNYX_STT]: { + label: 'Telnyx STT', + name: SpeechToTextType.TELNYX_STT, + icon: telnyxPng, + url: 'https://developers.telnyx.com/docs/voice/stt/models', + inputs: [ + { + label: 'Model', + name: 'model', + type: 'string', + description: 'Speech-to-text model to use. Defaults to openai/whisper-large-v3-turbo when left blank.', + placeholder: 'openai/whisper-large-v3-turbo', + optional: true + }, + { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'] + }, + { + label: 'Language', + name: 'language', + type: 'string', + description: 'Optional language of the input audio. Supplying the language can improve recognition accuracy.', + placeholder: 'en', + optional: true + }, + { + label: 'Prompt', + name: 'prompt', + type: 'string', + rows: 4, + description: 'Optional prompt to guide the transcription style or continue a previous audio segment.', + optional: true + }, + { + label: 'Temperature', + name: 'temperature', + type: 'number', + step: 0.1, + description: 'Optional sampling temperature between 0 and 1. Lower values are more deterministic.', + optional: true + } + ] + }, [SpeechToTextType.GROQ_WHISPER]: { label: 'Groq Whisper', name: SpeechToTextType.GROQ_WHISPER, @@ -222,7 +270,7 @@ const speechToTextProviders = { name: 'language', type: 'string', description: - 'The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.', + 'Optional language of the input audio. Supplying the language can improve recognition accuracy. Supplying the input language in ISO-639-1 format will improve accuracy and latency.', placeholder: 'en', optional: true }, diff --git a/packages/ui/src/ui-component/extended/TextToSpeech.jsx b/packages/ui/src/ui-component/extended/TextToSpeech.jsx index 8ce171615a4..34fabbdeefe 100644 --- a/packages/ui/src/ui-component/extended/TextToSpeech.jsx +++ b/packages/ui/src/ui-component/extended/TextToSpeech.jsx @@ -31,6 +31,7 @@ import { Dropdown } from '@/ui-component/dropdown/Dropdown' import AudioWaveform from '@/ui-component/extended/AudioWaveform' import openAISVG from '@/assets/images/openai.svg' import elevenLabsSVG from '@/assets/images/elevenlabs.svg' +import telnyxPng from '@/assets/images/telnyx.png' // store import useNotifier from '@/utils/useNotifier' @@ -41,7 +42,8 @@ import ttsApi from '@/api/tts' const TextToSpeechType = { OPENAI_TTS: 'openai', - ELEVEN_LABS_TTS: 'elevenlabs' + ELEVEN_LABS_TTS: 'elevenlabs', + TELNYX_TTS: 'telnyxTts' } // Weird quirk - the key must match the name property value. @@ -84,11 +86,64 @@ const textToSpeechProviders = { label: 'Voice', name: 'voice', type: 'voice_select', - description: 'The voice to use for text-to-speech', + description: 'Voice to use for text-to-speech synthesis', default: '21m00Tcm4TlvDq8ikWAM', optional: true } ] + }, + + [TextToSpeechType.TELNYX_TTS]: { + label: 'Telnyx TTS', + name: TextToSpeechType.TELNYX_TTS, + icon: telnyxPng, + url: 'https://developers.telnyx.com/docs/voice/programmable-voice/tts', + inputs: [ + { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['telnyxApi'] + }, + { + label: 'Voice', + name: 'voice', + type: 'voice_select', + description: 'Voice to use for text-to-speech synthesis', + default: 'Telnyx.NaturalHD.astra', + optional: true + }, + { + label: 'Output Format', + name: 'output_format', + type: 'string', + description: 'Audio output format returned by the Telnyx TTS API', + default: 'mp3', + optional: true + }, + { + label: 'Sample Rate', + name: 'sample_rate', + type: 'number', + description: 'Optional audio sample rate for the generated output.', + optional: true + }, + { + label: 'Language Code', + name: 'language_code', + type: 'string', + description: 'Optional language code to guide pronunciation or locale-specific speech output.', + optional: true + }, + { + label: 'Speed', + name: 'speed', + type: 'number', + step: 0.1, + description: 'Optional speech speed multiplier.', + optional: true + } + ] } } From 688c4bb532f6e2495bbb5e7dbb8727e017cf5fae Mon Sep 17 00:00:00 2001 From: gbattistel <4660743+gbattistel@users.noreply.github.com> Date: Fri, 15 May 2026 17:03:54 -0400 Subject: [PATCH 4/5] fix(tools): correct Telnyx verify check endpoint --- .../nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts b/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts index 2bdaa217a9f..abb1524e6b9 100644 --- a/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts +++ b/packages/components/nodes/tools/TelnyxVerifyCheck/TelnyxVerifyCheck.ts @@ -32,13 +32,12 @@ class TelnyxVerifyCheckTool extends DynamicStructuredTool { } const body = { - phone_number: arg.phoneNumber, verify_profile_id: verifyProfileId, code: arg.code } try { - const res = await secureFetch('https://api.telnyx.com/v2/verifications/by_phone_number/actions/verify', { + const res = await secureFetch(`https://api.telnyx.com/v2/verifications/by_phone_number/${encodeURIComponent(arg.phoneNumber)}/actions/verify`, { method: 'POST', headers: { Authorization: `Bearer ${this.apiKey}`, From 8a34d4af0b0d0979b8836bce7e1b655935d10e93 Mon Sep 17 00:00:00 2001 From: gbattistel <4660743+gbattistel@users.noreply.github.com> Date: Fri, 15 May 2026 18:25:21 -0400 Subject: [PATCH 5/5] fix: address review feedback for Telnyx integrations --- .../nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts | 10 ++++++++-- .../embeddings/TelnyxEmbedding/TelnyxEmbedding.ts | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts b/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts index 2d9f66f7059..f7757c1ffde 100644 --- a/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts +++ b/packages/components/nodes/chatmodels/ChatTelnyx/ChatTelnyx.ts @@ -2,12 +2,13 @@ import { ChatOpenAI, ChatOpenAIFields } from '@langchain/openai' import { BaseCache } from '@langchain/core/caches' import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { secureFetch } from '../../../src/httpSecurity' const TELNYX_OPENAI_BASE = 'https://api.telnyx.com/v2/ai/openai' const TELNYX_CHAT_MODELS_URL = 'https://api.telnyx.com/v2/ai/openai/models' const fetchTelnyxModels = async (apiKey: string) => { - const response = await fetch(TELNYX_CHAT_MODELS_URL, { + const response = await secureFetch(TELNYX_CHAT_MODELS_URL, { headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' @@ -105,8 +106,13 @@ class ChatTelnyx_ChatModels implements INode { const credentialData = await getCredentialData(nodeData.credential ?? '', options) const apiKey = getCredentialParam('apiKey', credentialData, nodeData) + const parsedTemperature = temperature ? parseFloat(temperature) : 0.9 + if (Number.isNaN(parsedTemperature)) { + throw new Error('Temperature must be a valid number') + } + const obj: ChatOpenAIFields = { - temperature: parseFloat(temperature), + temperature: parsedTemperature, modelName, openAIApiKey: apiKey, apiKey, diff --git a/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts b/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts index 50c21602bb4..bb9502b4cf1 100644 --- a/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts +++ b/packages/components/nodes/embeddings/TelnyxEmbedding/TelnyxEmbedding.ts @@ -1,12 +1,13 @@ import { ClientOptions, OpenAIEmbeddings, OpenAIEmbeddingsParams } from '@langchain/openai' import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { secureFetch } from '../../../src/httpSecurity' const TELNYX_OPENAI_BASE = 'https://api.telnyx.com/v2/ai/openai' const TELNYX_EMBEDDINGS_MODELS_URL = 'https://api.telnyx.com/v2/ai/embeddings/models' const fetchTelnyxModels = async (apiKey: string) => { - const response = await fetch(TELNYX_EMBEDDINGS_MODELS_URL, { + const response = await secureFetch(TELNYX_EMBEDDINGS_MODELS_URL, { headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json'