11import { BaseNode , prop } from "@nodetool-ai/node-sdk" ;
2+ import type { ImageRef , AudioRef } from "@nodetool-ai/node-sdk" ;
23import { tagAsServer } from "@nodetool-ai/nodes-utils" ;
34import { safeFetch } from "@nodetool-ai/runtime" ;
45import {
@@ -15,7 +16,9 @@ function getGeminiApiKey(secrets: Record<string, string>): string {
1516 return key ;
1617}
1718
18- function getAudioBytes ( audio : Record < string , unknown > ) : Uint8Array {
19+ type MediaRefLike = { uri ?: string ; asset_id ?: string | null ; data ?: unknown } ;
20+
21+ function getAudioBytes ( audio : MediaRefLike ) : Uint8Array {
1922 if ( isString ( audio . data ) ) {
2023 return Uint8Array . from ( Buffer . from ( audio . data , "base64" ) ) ;
2124 }
@@ -25,7 +28,7 @@ function getAudioBytes(audio: Record<string, unknown>): Uint8Array {
2528 throw new Error ( "Audio data is required" ) ;
2629}
2730
28- function getImageBytes ( image : Record < string , unknown > ) : Uint8Array | null {
31+ function getImageBytes ( image : MediaRefLike ) : Uint8Array | null {
2932 if ( isNonEmptyString ( image . data ) ) {
3033 return Uint8Array . from ( Buffer . from ( image . data , "base64" ) ) ;
3134 }
@@ -70,7 +73,7 @@ export class GroundedSearchNode extends BaseNode {
7073 title : "Query" ,
7174 description : "The search query to execute"
7275 } )
73- declare query : any ;
76+ declare query : string ;
7477
7578 @prop ( {
7679 type : "enum" ,
@@ -86,12 +89,12 @@ export class GroundedSearchNode extends BaseNode {
8689 "gemini-2.5-flash-lite"
8790 ]
8891 } )
89- declare model : any ;
92+ declare model : string ;
9093
9194 async process ( ) : Promise < GroundedSearchNodeOutputs > {
9295 const apiKey = getGeminiApiKey ( this . _secrets ) ;
93- const query = String ( this . query ?? "" ) ;
94- const model = String ( this . model ?? "gemini-3.5-flash" ) ;
96+ const query = this . query ;
97+ const model = this . model ;
9598
9699 if ( ! query ) throw new Error ( "Search query is required" ) ;
97100
@@ -184,7 +187,7 @@ export class EmbeddingNode extends BaseNode {
184187 title : "Input" ,
185188 description : "The text to embed."
186189 } )
187- declare input : any ;
190+ declare input : string ;
188191
189192 @prop ( {
190193 type : "enum" ,
@@ -193,12 +196,12 @@ export class EmbeddingNode extends BaseNode {
193196 description : "The embedding model to use" ,
194197 values : [ "gemini-embedding-2" ]
195198 } )
196- declare model : any ;
199+ declare model : string ;
197200
198201 async process ( ) : Promise < Record < string , unknown > > {
199202 const apiKey = getGeminiApiKey ( this . _secrets ) ;
200- const input = String ( this . input ?? "" ) ;
201- const model = String ( this . model ?? "gemini-embedding-2" ) ;
203+ const input = this . input ;
204+ const model = this . model ;
202205
203206 if ( ! input )
204207 throw new Error ( "Input text is required for embedding generation" ) ;
@@ -256,7 +259,7 @@ export class ImageGenerationNode extends BaseNode {
256259 title : "Prompt" ,
257260 description : "The text prompt describing the image to generate."
258261 } )
259- declare prompt : any ;
262+ declare prompt : string ;
260263
261264 @prop ( {
262265 type : "enum" ,
@@ -270,7 +273,7 @@ export class ImageGenerationNode extends BaseNode {
270273 "imagen-4.0-generate-001"
271274 ]
272275 } )
273- declare model : any ;
276+ declare model : string ;
274277
275278 @prop ( {
276279 type : "image" ,
@@ -284,7 +287,7 @@ export class ImageGenerationNode extends BaseNode {
284287 title : "Image" ,
285288 description : "The image to use as a base for the generation."
286289 } )
287- declare image : any ;
290+ declare image : ImageRef ;
288291
289292 @prop ( {
290293 type : "enum" ,
@@ -308,7 +311,7 @@ export class ImageGenerationNode extends BaseNode {
308311 "21:9"
309312 ]
310313 } )
311- declare aspect_ratio : any ;
314+ declare aspect_ratio : string ;
312315
313316 @prop ( {
314317 type : "enum" ,
@@ -317,15 +320,15 @@ export class ImageGenerationNode extends BaseNode {
317320 description : "The output image resolution" ,
318321 values : [ "512px" , "1K" , "2K" , "4K" ]
319322 } )
320- declare resolution : any ;
323+ declare resolution : string ;
321324
322325 async process ( ) : Promise < ImageGenerationNodeOutputs > {
323326 const apiKey = getGeminiApiKey ( this . _secrets ) ;
324- const prompt = String ( this . prompt ?? "" ) ;
325- const model = String ( this . model ?? "gemini-3.1-flash-image" ) ;
326- const image = ( this . image ?? { } ) as Record < string , unknown > ;
327- const aspectRatio = String ( this . aspect_ratio ?? "1:1" ) ;
328- const resolution = String ( this . resolution ?? "1K" ) ;
327+ const prompt = this . prompt ;
328+ const model = this . model ;
329+ const image = this . image ;
330+ const aspectRatio = this . aspect_ratio ;
331+ const resolution = this . resolution ;
329332
330333 if ( ! prompt ) throw new Error ( "The input prompt cannot be empty." ) ;
331334
@@ -473,7 +476,7 @@ export class TextToVideoGeminiNode extends BaseNode {
473476 title : "Prompt" ,
474477 description : "The text prompt describing the video to generate"
475478 } )
476- declare prompt : any ;
479+ declare prompt : string ;
477480
478481 @prop ( {
479482 type : "enum" ,
@@ -486,7 +489,7 @@ export class TextToVideoGeminiNode extends BaseNode {
486489 "veo-3.1-lite-generate-preview"
487490 ]
488491 } )
489- declare model : any ;
492+ declare model : string ;
490493
491494 @prop ( {
492495 type : "enum" ,
@@ -495,15 +498,15 @@ export class TextToVideoGeminiNode extends BaseNode {
495498 description : "The aspect ratio of the generated video" ,
496499 values : [ "16:9" , "9:16" ]
497500 } )
498- declare aspect_ratio : any ;
501+ declare aspect_ratio : string ;
499502
500503 @prop ( {
501504 type : "str" ,
502505 default : "" ,
503506 title : "Negative Prompt" ,
504507 description : "Negative prompt to guide what to avoid in the video"
505508 } )
506- declare negative_prompt : any ;
509+ declare negative_prompt : string ;
507510
508511 @prop ( {
509512 type : "enum" ,
@@ -512,15 +515,15 @@ export class TextToVideoGeminiNode extends BaseNode {
512515 description : "The output video resolution" ,
513516 values : [ "720p" , "1080p" , "4k" ]
514517 } )
515- declare resolution : any ;
518+ declare resolution : string ;
516519
517520 async process ( ) : Promise < TextToVideoGeminiNodeOutputs > {
518521 const apiKey = getGeminiApiKey ( this . _secrets ) ;
519- const prompt = String ( this . prompt ?? "" ) ;
520- const model = String ( this . model ?? "veo-3.1-generate-preview" ) ;
521- const aspectRatio = String ( this . aspect_ratio ?? "16:9" ) ;
522- const negativePrompt = String ( this . negative_prompt ?? "" ) ;
523- const resolution = String ( this . resolution ?? "720p" ) ;
522+ const prompt = this . prompt ;
523+ const model = this . model ;
524+ const aspectRatio = this . aspect_ratio ;
525+ const negativePrompt = this . negative_prompt ;
526+ const resolution = this . resolution ;
524527
525528 if ( ! prompt ) throw new Error ( "Video generation prompt is required" ) ;
526529
@@ -583,15 +586,15 @@ export class ImageToVideoGeminiNode extends BaseNode {
583586 title : "Image" ,
584587 description : "The image to animate into a video"
585588 } )
586- declare image : any ;
589+ declare image : ImageRef ;
587590
588591 @prop ( {
589592 type : "str" ,
590593 default : "" ,
591594 title : "Prompt" ,
592595 description : "Optional text prompt describing the desired animation"
593596 } )
594- declare prompt : any ;
597+ declare prompt : string ;
595598
596599 @prop ( {
597600 type : "enum" ,
@@ -604,7 +607,7 @@ export class ImageToVideoGeminiNode extends BaseNode {
604607 "veo-3.1-lite-generate-preview"
605608 ]
606609 } )
607- declare model : any ;
610+ declare model : string ;
608611
609612 @prop ( {
610613 type : "enum" ,
@@ -613,15 +616,15 @@ export class ImageToVideoGeminiNode extends BaseNode {
613616 description : "The aspect ratio of the generated video" ,
614617 values : [ "16:9" , "9:16" ]
615618 } )
616- declare aspect_ratio : any ;
619+ declare aspect_ratio : string ;
617620
618621 @prop ( {
619622 type : "str" ,
620623 default : "" ,
621624 title : "Negative Prompt" ,
622625 description : "Negative prompt to guide what to avoid in the video"
623626 } )
624- declare negative_prompt : any ;
627+ declare negative_prompt : string ;
625628
626629 @prop ( {
627630 type : "enum" ,
@@ -630,16 +633,16 @@ export class ImageToVideoGeminiNode extends BaseNode {
630633 description : "The output video resolution" ,
631634 values : [ "720p" , "1080p" , "4k" ]
632635 } )
633- declare resolution : any ;
636+ declare resolution : string ;
634637
635638 async process ( ) : Promise < ImageToVideoGeminiNodeOutputs > {
636639 const apiKey = getGeminiApiKey ( this . _secrets ) ;
637- const image = ( this . image ?? { } ) as Record < string , unknown > ;
638- const prompt = String ( this . prompt ?? "Animate this image" ) ;
639- const model = String ( this . model ?? "veo-3.1-generate-preview" ) ;
640- const aspectRatio = String ( this . aspect_ratio ?? "16:9" ) ;
641- const negativePrompt = String ( this . negative_prompt ?? "" ) ;
642- const resolution = String ( this . resolution ?? "720p" ) ;
640+ const image = this . image ;
641+ const prompt = this . prompt ;
642+ const model = this . model ;
643+ const aspectRatio = this . aspect_ratio ;
644+ const negativePrompt = this . negative_prompt ;
645+ const resolution = this . resolution ;
643646
644647 if ( ! isRefSet ( image ) ) throw new Error ( "Input image is required" ) ;
645648
@@ -795,7 +798,7 @@ export class TextToSpeechGeminiNode extends BaseNode {
795798 title : "Text" ,
796799 description : "The text to convert to speech."
797800 } )
798- declare text : any ;
801+ declare text : string ;
799802
800803 @prop ( {
801804 type : "enum" ,
@@ -808,7 +811,7 @@ export class TextToSpeechGeminiNode extends BaseNode {
808811 "gemini-2.5-pro-preview-tts"
809812 ]
810813 } )
811- declare model : any ;
814+ declare model : string ;
812815
813816 @prop ( {
814817 type : "enum" ,
@@ -848,7 +851,7 @@ export class TextToSpeechGeminiNode extends BaseNode {
848851 "zubenelgenubi"
849852 ]
850853 } )
851- declare voice_name : any ;
854+ declare voice_name : string ;
852855
853856 @prop ( {
854857 type : "str" ,
@@ -857,13 +860,13 @@ export class TextToSpeechGeminiNode extends BaseNode {
857860 description :
858861 "Optional style prompt to control speech characteristics (e.g., 'Say cheerfully', 'Speak with excitement')"
859862 } )
860- declare style_prompt : any ;
863+ declare style_prompt : string ;
861864
862865 async process ( ) : Promise < TextToSpeechGeminiNodeOutputs > {
863866 const apiKey = getGeminiApiKey ( this . _secrets ) ;
864- const text = String ( this . text ?? "" ) ;
865- const model = String ( this . model ?? "gemini-3.1-flash-tts-preview" ) ;
866- const stylePrompt = String ( this . style_prompt ?? "" ) ;
867+ const text = this . text ;
868+ const model = this . model ;
869+ const stylePrompt = this . style_prompt ;
867870
868871 const VALID_VOICES = [
869872 "achernar" , "achird" , "algenib" , "algieba" , "alnilam" ,
@@ -873,7 +876,7 @@ export class TextToSpeechGeminiNode extends BaseNode {
873876 "pulcherrima" , "rasalgethi" , "sadachbia" , "sadaltager" , "schedar" ,
874877 "sulafat" , "umbriel" , "vindemiatrix" , "zephyr" , "zubenelgenubi"
875878 ] ;
876- const rawVoice = String ( this . voice_name ?? "kore" ) . toLowerCase ( ) ;
879+ const rawVoice = this . voice_name . toLowerCase ( ) ;
877880 const voiceName = VALID_VOICES . includes ( rawVoice ) ? rawVoice : "kore" ;
878881
879882 if ( ! text ) throw new Error ( "The input text cannot be empty." ) ;
@@ -979,7 +982,7 @@ export class TranscribeGeminiNode extends BaseNode {
979982 title : "Audio" ,
980983 description : "The audio file to transcribe."
981984 } )
982- declare audio : any ;
985+ declare audio : AudioRef ;
983986
984987 @prop ( {
985988 type : "enum" ,
@@ -988,7 +991,7 @@ export class TranscribeGeminiNode extends BaseNode {
988991 description : "The Gemini model to use for transcription" ,
989992 values : [ "gemini-3.5-flash" , "gemini-3.1-flash-lite" , "gemini-2.5-flash" ]
990993 } )
991- declare model : any ;
994+ declare model : string ;
992995
993996 @prop ( {
994997 type : "str" ,
@@ -998,16 +1001,13 @@ export class TranscribeGeminiNode extends BaseNode {
9981001 description :
9991002 "Instructions for the transcription. You can customize this to request specific formatting or focus."
10001003 } )
1001- declare prompt : any ;
1004+ declare prompt : string ;
10021005
10031006 async process ( ) : Promise < TranscribeGeminiNodeOutputs > {
10041007 const apiKey = getGeminiApiKey ( this . _secrets ) ;
1005- const audio = ( this . audio ?? { } ) as Record < string , unknown > ;
1006- const model = String ( this . model ?? "gemini-3.5-flash" ) ;
1007- const prompt = String (
1008- this . prompt ??
1009- "Transcribe the following audio accurately. Return only the transcription text without any additional commentary."
1010- ) ;
1008+ const audio = this . audio ;
1009+ const model = this . model ;
1010+ const prompt = this . prompt ;
10111011
10121012 if ( ! isRefSet ( audio ) )
10131013 throw new Error ( "Audio file is required for transcription" ) ;
0 commit comments