diff --git a/api-report/genai-node.api.md b/api-report/genai-node.api.md index d62bb9304..b48eab2bd 100644 --- a/api-report/genai-node.api.md +++ b/api-report/genai-node.api.md @@ -4,8 +4,11 @@ ```ts +/// + import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { GoogleAuthOptions } from 'google-auth-library'; +import { ReadableStream as ReadableStream_2 } from 'stream/web'; // @public export interface ActivityEnd { @@ -33,6 +36,19 @@ export enum AdapterSize { ADAPTER_SIZE_UNSPECIFIED = "ADAPTER_SIZE_UNSPECIFIED" } +// @public +interface AllowedTools { + mode?: ToolChoiceType; + tools?: Array; +} + +// @public +interface Annotation { + end_index?: number; + source?: string; + start_index?: number; +} + // @public export interface ApiAuth { apiKeyConfig?: ApiAuthApiKeyConfig; @@ -78,6 +94,19 @@ export interface AudioChunk { sourceMetadata?: LiveMusicSourceMetadata; } +// @public +interface AudioContent { + // (undocumented) + data?: string; + mime_type?: AudioMimeType; + type: 'audio'; + // (undocumented) + uri?: string; +} + +// @public +type AudioMimeType = 'audio/wav' | 'audio/mp3' | 'audio/aiff' | 'audio/aac' | 'audio/ogg' | 'audio/flac' | (string & {}); + // @public export interface AudioTranscriptionConfig { } @@ -147,6 +176,68 @@ export interface AutomaticFunctionCallingConfig { maximumRemoteCalls?: number; } +// @public (undocumented) +interface BaseCreateAgentInteractionParams { + agent: (string & {}) | 'deep-research-pro-preview-12-2025'; + agent_config?: DynamicAgentConfig | DeepResearchAgentConfig; + api_version?: string; + background?: boolean; + input: string | Array | Array | TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + previous_interaction_id?: string; + response_format?: unknown; + response_mime_type?: string; + response_modalities?: Array<'text' | 'image' | 'audio'>; + store?: boolean; + stream?: boolean; + system_instruction?: string; + tools?: Array; +} + +// @public (undocumented) +interface BaseCreateModelInteractionParams { + api_version?: string; + background?: boolean; + generation_config?: GenerationConfig_2; + input: string | Array | Array | TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + model: Model_2; + previous_interaction_id?: string; + response_format?: unknown; + response_mime_type?: string; + response_modalities?: Array<'text' | 'image' | 'audio'>; + store?: boolean; + stream?: boolean; + system_instruction?: string; + tools?: Array; +} + +// Warning: (ae-forgotten-export) The symbol "APIResource" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +class BaseInteractions extends APIResource { + cancel(id: string, params?: InteractionCancelParams | null | undefined, options?: RequestOptions): APIPromise; + // Warning: (ae-forgotten-export) The symbol "RequestOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "APIPromise" needs to be exported by the entry point index.d.ts + create(params: CreateModelInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + // Warning: (ae-forgotten-export) The symbol "Stream" needs to be exported by the entry point index.d.ts + // + // (undocumented) + create(params: CreateModelInteractionParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + create(params: CreateAgentInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + // (undocumented) + create(params: CreateAgentInteractionParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + create(params: BaseCreateModelInteractionParams | BaseCreateAgentInteractionParams, options?: RequestOptions): APIPromise | Interaction>; + delete(id: string, params?: InteractionDeleteParams | null | undefined, options?: RequestOptions): APIPromise; + get(id: string, params?: InteractionGetParamsNonStreaming, options?: RequestOptions): APIPromise; + // (undocumented) + get(id: string, params: InteractionGetParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + get(id: string, params?: InteractionGetParamsBase | undefined, options?: RequestOptions): APIPromise | Interaction>; + // (undocumented) + static readonly _key: readonly ['interactions']; +} + // @public export interface BaseUrlParameters { // (undocumented) @@ -368,12 +459,34 @@ export interface CitationMetadata { citations?: Citation[]; } +// @public +interface CodeExecutionCallArguments { + code?: string; + language?: 'python'; +} + +// @public +interface CodeExecutionCallContent { + arguments?: CodeExecutionCallArguments; + id?: string; + type: 'code_execution_call'; +} + // @public export interface CodeExecutionResult { outcome?: Outcome; output?: string; } +// @public +interface CodeExecutionResultContent { + call_id?: string; + is_error?: boolean; + result?: string; + signature?: string; + type: 'code_execution_result'; +} + // @public export interface CompletionStats { failedCount?: string; @@ -413,6 +526,202 @@ export interface Content { role?: string; } +// @public (undocumented) +interface ContentDelta { + // (undocumented) + delta?: ContentDelta.TextDelta | ContentDelta.ImageDelta | ContentDelta.AudioDelta | ContentDelta.DocumentDelta | ContentDelta.VideoDelta | ContentDelta.ThoughtSummaryDelta | ContentDelta.ThoughtSignatureDelta | ContentDelta.FunctionCallDelta | ContentDelta.FunctionResultDelta | ContentDelta.CodeExecutionCallDelta | ContentDelta.CodeExecutionResultDelta | ContentDelta.URLContextCallDelta | ContentDelta.URLContextResultDelta | ContentDelta.GoogleSearchCallDelta | ContentDelta.GoogleSearchResultDelta | ContentDelta.MCPServerToolCallDelta | ContentDelta.MCPServerToolResultDelta | ContentDelta.FileSearchResultDelta; + event_id?: string; + // (undocumented) + event_type?: 'content.delta'; + // (undocumented) + index?: number; +} + +// @public (undocumented) +namespace ContentDelta { + // (undocumented) + interface AudioDelta { + // (undocumented) + data?: string; + mime_type?: AudioMimeType; + type: 'audio'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface CodeExecutionCallDelta { + arguments?: CodeExecutionCallArguments; + id?: string; + type: 'code_execution_call'; + } + // (undocumented) + interface CodeExecutionResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: string; + // (undocumented) + signature?: string; + type: 'code_execution_result'; + } + // (undocumented) + interface DocumentDelta { + // (undocumented) + data?: string; + // (undocumented) + mime_type?: string; + type: 'document'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface FileSearchResultDelta { + // (undocumented) + result?: Array; + type: 'file_search_result'; + } + // (undocumented) + namespace FileSearchResultDelta { + interface Result { + file_search_store?: string; + text?: string; + title?: string; + } + } + // (undocumented) + interface FunctionCallDelta { + // (undocumented) + arguments?: { + [key: string]: unknown; + }; + id?: string; + // (undocumented) + name?: string; + type: 'function_call'; + } + // (undocumented) + interface FunctionResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + name?: string; + result?: FunctionResultDelta.Items | string; + type: 'function_result'; + } + // (undocumented) + namespace FunctionResultDelta { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } + } + // (undocumented) + interface GoogleSearchCallDelta { + arguments?: GoogleSearchCallArguments; + id?: string; + type: 'google_search_call'; + } + // (undocumented) + interface GoogleSearchResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: Array; + // (undocumented) + signature?: string; + type: 'google_search_result'; + } + // (undocumented) + interface ImageDelta { + // (undocumented) + data?: string; + mime_type?: ImageMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'image'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface MCPServerToolCallDelta { + // (undocumented) + arguments?: { + [key: string]: unknown; + }; + id?: string; + // (undocumented) + name?: string; + // (undocumented) + server_name?: string; + type: 'mcp_server_tool_call'; + } + // (undocumented) + interface MCPServerToolResultDelta { + call_id?: string; + // (undocumented) + name?: string; + result?: MCPServerToolResultDelta.Items | string; + // (undocumented) + server_name?: string; + type: 'mcp_server_tool_result'; + } + // (undocumented) + namespace MCPServerToolResultDelta { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } + } + // (undocumented) + interface TextDelta { + annotations?: Array; + // (undocumented) + text?: string; + type: 'text'; + } + // (undocumented) + interface ThoughtSignatureDelta { + signature?: string; + type: 'thought_signature'; + } + // (undocumented) + interface ThoughtSummaryDelta { + content?: TextContent | ImageContent; + type: 'thought_summary'; + } + // (undocumented) + interface URLContextCallDelta { + arguments?: URLContextCallArguments; + id?: string; + type: 'url_context_call'; + } + // (undocumented) + interface URLContextResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: Array; + // (undocumented) + signature?: string; + type: 'url_context_result'; + } + // (undocumented) + interface VideoDelta { + // (undocumented) + data?: string; + mime_type?: VideoMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'video'; + // (undocumented) + uri?: string; + } +} + // @public export interface ContentEmbedding { statistics?: ContentEmbeddingStatistics; @@ -437,6 +746,25 @@ export class ContentReferenceImage { toReferenceImageAPI(): ReferenceImageAPIInternal; } +// @public (undocumented) +interface ContentStart { + content?: TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + event_id?: string; + // (undocumented) + event_type?: 'content.start'; + // (undocumented) + index?: number; +} + +// @public (undocumented) +interface ContentStop { + event_id?: string; + // (undocumented) + event_type?: 'content.stop'; + // (undocumented) + index?: number; +} + // @public (undocumented) export type ContentUnion = Content | PartUnion[] | PartUnion; @@ -496,6 +824,16 @@ export class CountTokensResponse { totalTokens?: number; } +// @public (undocumented) +interface CreateAgentInteractionParamsNonStreaming extends BaseCreateAgentInteractionParams { + stream?: false; +} + +// @public (undocumented) +interface CreateAgentInteractionParamsStreaming extends BaseCreateAgentInteractionParams { + stream: true; +} + // @public export interface CreateAuthTokenConfig { abortSignal?: AbortSignal; @@ -606,6 +944,16 @@ export function createFunctionResponsePartFromUri(uri: string, mimeType: string) // @public export function createModelContent(partOrString: PartListUnion | string): Content; +// @public (undocumented) +interface CreateModelInteractionParamsNonStreaming extends BaseCreateModelInteractionParams { + stream?: false; +} + +// @public (undocumented) +interface CreateModelInteractionParamsStreaming extends BaseCreateModelInteractionParams { + stream: true; +} + // @public export function createPartFromBase64(data: string, mimeType: string, mediaResolution?: PartMediaResolutionLevel): Part; @@ -703,6 +1051,12 @@ export interface DatasetStats { userOutputTokenDistribution?: DatasetDistribution; } +// @public +interface DeepResearchAgentConfig { + thinking_summaries?: 'auto' | 'none'; + type?: 'deep-research'; +} + // @public export interface DeleteBatchJobConfig { abortSignal?: AbortSignal; @@ -822,6 +1176,17 @@ interface Document_2 { } export { Document_2 as Document } +// @public +interface DocumentContent { + // (undocumented) + data?: string; + // (undocumented) + mime_type?: string; + type: 'document'; + // (undocumented) + uri?: string; +} + // @public export enum DocumentState { // (undocumented) @@ -850,6 +1215,13 @@ export interface DownloadFileParameters { file: DownloadableFileUnion; } +// @public +interface DynamicAgentConfig { + // (undocumented) + [k: string]: unknown; + type?: 'dynamic'; +} + // @public export interface DynamicRetrievalConfig { dynamicThreshold?: number; @@ -997,6 +1369,22 @@ export enum Environment { ENVIRONMENT_UNSPECIFIED = "ENVIRONMENT_UNSPECIFIED" } +// @public (undocumented) +interface ErrorEvent_2 { + error?: ErrorEvent_2.Error; + event_id?: string; + // (undocumented) + event_type?: 'error'; +} + +// @public (undocumented) +namespace ErrorEvent_2 { + interface Error { + code?: string; + message?: string; + } +} + // @public export interface ExecutableCode { code?: string; @@ -1093,6 +1481,21 @@ export interface FileSearch { topK?: number; } +// @public +interface FileSearchResultContent { + result?: Array; + type: 'file_search_result'; +} + +// @public (undocumented) +namespace FileSearchResultContent { + interface Result { + file_search_store?: string; + text?: string; + title?: string; + } +} + // @public export interface FileSearchStore { activeDocumentsCount?: string; @@ -1153,6 +1556,15 @@ export enum FinishReason { UNEXPECTED_TOOL_CALL = "UNEXPECTED_TOOL_CALL" } +// @public +interface Function_2 { + description?: string; + name?: string; + parameters?: unknown; + // (undocumented) + type: 'function'; +} + // @public export interface FunctionCall { args?: Record; @@ -1162,6 +1574,16 @@ export interface FunctionCall { willContinue?: boolean; } +// @public +interface FunctionCallContent { + arguments: { + [key: string]: unknown; + }; + id: string; + name: string; + type: 'function_call'; +} + // @public export interface FunctionCallingConfig { allowedFunctionNames?: string[]; @@ -1227,6 +1649,24 @@ export enum FunctionResponseScheduling { WHEN_IDLE = "WHEN_IDLE" } +// @public +interface FunctionResultContent { + call_id: string; + is_error?: boolean; + name?: string; + result: FunctionResultContent.Items | unknown | string; + type: 'function_result'; +} + +// @public (undocumented) +namespace FunctionResultContent { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } +} + // @public export interface GeminiPreferenceExample { completions?: GeminiPreferenceExampleCompletion[]; @@ -1462,6 +1902,19 @@ export interface GenerationConfig { topP?: number; } +// @public +interface GenerationConfig_2 { + max_output_tokens?: number; + seed?: number; + speech_config?: Array; + stop_sequences?: Array; + temperature?: number; + thinking_level?: ThinkingLevel_2; + thinking_summaries?: 'auto' | 'none'; + tool_choice?: ToolChoice; + top_p?: number; +} + // @public export interface GenerationConfigRoutingConfig { autoMode?: GenerationConfigRoutingConfigAutoRoutingMode; @@ -1600,6 +2053,8 @@ export class GoogleGenAI { // (undocumented) readonly fileSearchStores: FileSearchStores; // (undocumented) + get interactions(): Interactions_2; + // (undocumented) readonly live: Live; // (undocumented) readonly models: Models; @@ -1644,6 +2099,34 @@ export interface GoogleSearch { timeRangeFilter?: Interval; } +// @public +interface GoogleSearchCallArguments { + queries?: Array; +} + +// @public +interface GoogleSearchCallContent { + arguments?: GoogleSearchCallArguments; + id?: string; + type: 'google_search_call'; +} + +// @public +interface GoogleSearchResult { + rendered_content?: string; + title?: string; + url?: string; +} + +// @public +interface GoogleSearchResultContent { + call_id?: string; + is_error?: boolean; + result?: Array; + signature?: string; + type: 'google_search_result'; +} + // @public export interface GoogleSearchRetrieval { dynamicRetrievalConfig?: DynamicRetrievalConfig; @@ -1832,6 +2315,20 @@ export interface ImageConfig { outputMimeType?: string; } +// @public +interface ImageContent { + // (undocumented) + data?: string; + mime_type?: ImageMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'image'; + // (undocumented) + uri?: string; +} + +// @public +type ImageMimeType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/heic' | 'image/heif' | (string & {}); + // @public export enum ImagePromptLanguage { auto = "auto", @@ -1898,6 +2395,166 @@ export class InlinedResponse { response?: GenerateContentResponse; } +// @public +interface Interaction { + agent?: (string & {}) | 'deep-research-pro-preview-12-2025'; + created?: string; + error?: Interaction.Error; + id: string; + model?: Model_2; + object?: 'interaction'; + outputs?: Array; + previous_interaction_id?: string; + role?: string; + status: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; + updated?: string; + usage?: Usage; +} + +// @public (undocumented) +namespace Interaction { + interface Error { + code?: string; + message?: string; + } +} + +// @public (undocumented) +interface InteractionCancelParams { + api_version?: string; +} + +// @public (undocumented) +type InteractionCreateParams = CreateModelInteractionParamsNonStreaming | CreateModelInteractionParamsStreaming | CreateAgentInteractionParamsNonStreaming | CreateAgentInteractionParamsStreaming; + +// @public (undocumented) +interface InteractionDeleteParams { + api_version?: string; +} + +// @public (undocumented) +type InteractionDeleteResponse = unknown; + +// @public (undocumented) +interface InteractionEvent { + event_id?: string; + // (undocumented) + event_type?: 'interaction.start' | 'interaction.complete'; + interaction?: Interaction; +} + +// @public (undocumented) +type InteractionGetParams = InteractionGetParamsNonStreaming | InteractionGetParamsStreaming; + +// @public (undocumented) +interface InteractionGetParamsBase { + api_version?: string; + last_event_id?: string; + stream?: boolean; +} + +// @public (undocumented) +interface InteractionGetParamsNonStreaming extends InteractionGetParamsBase { + stream?: false; +} + +// @public (undocumented) +interface InteractionGetParamsStreaming extends InteractionGetParamsBase { + stream: true; +} + +declare namespace Interactions { + export { + BaseInteractions, + Interactions_2 as Interactions, + AllowedTools, + Annotation, + AudioContent, + AudioMimeType, + CodeExecutionCallArguments, + CodeExecutionCallContent, + CodeExecutionResultContent, + ContentDelta, + ContentStart, + ContentStop, + DeepResearchAgentConfig, + DocumentContent, + DynamicAgentConfig, + ErrorEvent_2 as ErrorEvent, + FileSearchResultContent, + Function_2 as Function, + FunctionCallContent, + FunctionResultContent, + GenerationConfig_2 as GenerationConfig, + GoogleSearchCallArguments, + GoogleSearchCallContent, + GoogleSearchResult, + GoogleSearchResultContent, + ImageContent, + ImageMimeType, + Interaction, + InteractionEvent, + InteractionSSEEvent, + InteractionStatusUpdate, + MCPServerToolCallContent, + MCPServerToolResultContent, + Model_2 as Model, + SpeechConfig_2 as SpeechConfig, + TextContent, + ThinkingLevel_2 as ThinkingLevel, + ThoughtContent, + Tool_2 as Tool, + ToolChoice, + ToolChoiceConfig, + ToolChoiceType, + Turn, + URLContextCallArguments, + URLContextCallContent, + URLContextResult, + URLContextResultContent, + Usage, + VideoContent, + VideoMimeType, + InteractionDeleteResponse, + InteractionCreateParams, + BaseCreateModelInteractionParams, + BaseCreateAgentInteractionParams, + CreateModelInteractionParamsNonStreaming, + CreateModelInteractionParamsStreaming, + CreateAgentInteractionParamsNonStreaming, + CreateAgentInteractionParamsStreaming, + InteractionDeleteParams, + InteractionCancelParams, + InteractionGetParams, + InteractionGetParamsBase, + InteractionGetParamsNonStreaming, + InteractionGetParamsStreaming + } +} + +// @public (undocumented) +class Interactions_2 extends BaseInteractions { +} + +// @public (undocumented) +namespace Interactions_2 { + { type AllowedTools as AllowedTools, type Annotation as Annotation, type AudioContent as AudioContent, type AudioMimeType as AudioMimeType, type CodeExecutionCallArguments as CodeExecutionCallArguments, type CodeExecutionCallContent as CodeExecutionCallContent, type CodeExecutionResultContent as CodeExecutionResultContent, type ContentDelta as ContentDelta, type ContentStart as ContentStart, type ContentStop as ContentStop, type DeepResearchAgentConfig as DeepResearchAgentConfig, type DocumentContent as DocumentContent, type DynamicAgentConfig as DynamicAgentConfig, type ErrorEvent as ErrorEvent, type FileSearchResultContent as FileSearchResultContent, type Function as Function, type FunctionCallContent as FunctionCallContent, type FunctionResultContent as FunctionResultContent, type GenerationConfig as GenerationConfig, type GoogleSearchCallArguments as GoogleSearchCallArguments, type GoogleSearchCallContent as GoogleSearchCallContent, type GoogleSearchResult as GoogleSearchResult, type GoogleSearchResultContent as GoogleSearchResultContent, type ImageContent as ImageContent, type ImageMimeType as ImageMimeType, type Interaction as Interaction, type InteractionEvent as InteractionEvent, type InteractionSSEEvent as InteractionSSEEvent, type InteractionStatusUpdate as InteractionStatusUpdate, type MCPServerToolCallContent as MCPServerToolCallContent, type MCPServerToolResultContent as MCPServerToolResultContent, type Model as Model, type SpeechConfig as SpeechConfig, type TextContent as TextContent, type ThinkingLevel as ThinkingLevel, type ThoughtContent as ThoughtContent, type Tool as Tool, type ToolChoice as ToolChoice, type ToolChoiceConfig as ToolChoiceConfig, type ToolChoiceType as ToolChoiceType, type Turn as Turn, type URLContextCallArguments as URLContextCallArguments, type URLContextCallContent as URLContextCallContent, type URLContextResult as URLContextResult, type URLContextResultContent as URLContextResultContent, type Usage as Usage, type VideoContent as VideoContent, type VideoMimeType as VideoMimeType, type InteractionDeleteResponse as InteractionDeleteResponse, type InteractionCreateParams as InteractionCreateParams, type CreateModelInteractionParamsNonStreaming as CreateModelInteractionParamsNonStreaming, type CreateModelInteractionParamsStreaming as CreateModelInteractionParamsStreaming, type CreateAgentInteractionParamsNonStreaming as CreateAgentInteractionParamsNonStreaming, type CreateAgentInteractionParamsStreaming as CreateAgentInteractionParamsStreaming, type InteractionDeleteParams as InteractionDeleteParams, type InteractionCancelParams as InteractionCancelParams, type InteractionGetParams as InteractionGetParams, type InteractionGetParamsNonStreaming as InteractionGetParamsNonStreaming, type InteractionGetParamsStreaming as InteractionGetParamsStreaming, }; +} + +// @public (undocumented) +type InteractionSSEEvent = InteractionEvent | InteractionStatusUpdate | ContentStart | ContentDelta | ContentStop | ErrorEvent_2; + +// @public (undocumented) +interface InteractionStatusUpdate { + event_id?: string; + // (undocumented) + event_type?: 'interaction.status_update'; + // (undocumented) + interaction_id?: string; + // (undocumented) + status?: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; +} + // @public export interface Interval { endTime?: string; @@ -2451,6 +3108,35 @@ export enum MaskReferenceMode { MASK_MODE_USER_PROVIDED = "MASK_MODE_USER_PROVIDED" } +// @public +interface MCPServerToolCallContent { + arguments: { + [key: string]: unknown; + }; + id: string; + name: string; + server_name: string; + type: 'mcp_server_tool_call'; +} + +// @public +interface MCPServerToolResultContent { + call_id: string; + name?: string; + result: MCPServerToolResultContent.Items | unknown | string; + server_name?: string; + type: 'mcp_server_tool_result'; +} + +// @public (undocumented) +namespace MCPServerToolResultContent { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } +} + // @public export function mcpToTool(...args: [...Client[], CallableToolConfig | Client]): CallableTool; @@ -2513,6 +3199,9 @@ export interface Model { version?: string; } +// @public +type Model_2 = 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'gemini-2.5-flash-preview-09-2025' | 'gemini-2.5-flash-lite' | 'gemini-2.5-flash-lite-preview-09-2025' | 'gemini-2.5-flash-preview-native-audio-dialog' | 'gemini-2.5-flash-image-preview' | 'gemini-2.5-pro-preview-tts' | 'gemini-3-pro-preview' | (string & {}); + // @public (undocumented) export class Models extends BaseModule { constructor(apiClient: ApiClient); @@ -3114,6 +3803,13 @@ export interface SpeechConfig { voiceConfig?: VoiceConfig; } +// @public +interface SpeechConfig_2 { + language?: string; + speaker?: string; + voice?: string; +} + // @public (undocumented) export type SpeechConfigUnion = SpeechConfig | string; @@ -3249,6 +3945,13 @@ export interface TestTableItem { skipInApiMode?: string; } +// @public +interface TextContent { + annotations?: Array; + text?: string; + type: 'text'; +} + // @public export interface ThinkingConfig { includeThoughts?: boolean; @@ -3263,6 +3966,16 @@ export enum ThinkingLevel { THINKING_LEVEL_UNSPECIFIED = "THINKING_LEVEL_UNSPECIFIED" } +// @public (undocumented) +type ThinkingLevel_2 = 'low' | 'high'; + +// @public +interface ThoughtContent { + signature?: string; + summary?: Array; + type: 'thought'; +} + // @public (undocumented) export class Tokens extends BaseModule { constructor(apiClient: ApiClient); @@ -3290,6 +4003,59 @@ export interface Tool { urlContext?: UrlContext; } +// @public +type Tool_2 = Function_2 | Tool_2.GoogleSearch | Tool_2.CodeExecution | Tool_2.URLContext | Tool_2.ComputerUse | Tool_2.MCPServer | Tool_2.FileSearch; + +// @public (undocumented) +namespace Tool_2 { + interface CodeExecution { + // (undocumented) + type: 'code_execution'; + } + interface ComputerUse { + environment?: 'browser'; + excludedPredefinedFunctions?: Array; + // (undocumented) + type: 'computer_use'; + } + interface FileSearch { + file_search_store_names?: Array; + metadata_filter?: string; + top_k?: number; + // (undocumented) + type: 'file_search'; + } + interface GoogleSearch { + // (undocumented) + type: 'google_search'; + } + interface MCPServer { + allowed_tools?: Array; + headers?: { + [key: string]: string; + }; + name?: string; + // (undocumented) + type: 'mcp_server'; + url?: string; + } + interface URLContext { + // (undocumented) + type: 'url_context'; + } +} + +// @public +type ToolChoice = ToolChoiceType | ToolChoiceConfig; + +// @public (undocumented) +interface ToolChoiceConfig { + allowed_tools?: AllowedTools; +} + +// @public (undocumented) +type ToolChoiceType = 'auto' | 'any' | 'none' | 'validated'; + // @public export interface ToolCodeExecution { } @@ -3426,6 +4192,12 @@ export interface TuningValidationDataset { vertexDatasetResource?: string; } +// @public (undocumented) +interface Turn { + content?: string | Array; + role?: string; +} + // @public export enum TurnCompleteReason { MALFORMED_FUNCTION_CALL = "MALFORMED_FUNCTION_CALL", @@ -3582,11 +4354,38 @@ export class UpscaleImageResponse { export interface UrlContext { } +// @public +interface URLContextCallArguments { + urls?: Array; +} + +// @public +interface URLContextCallContent { + arguments?: URLContextCallArguments; + id?: string; + type: 'url_context_call'; +} + // @public export interface UrlContextMetadata { urlMetadata?: UrlMetadata[]; } +// @public +interface URLContextResult { + status?: 'success' | 'error' | 'paywall' | 'unsafe'; + url?: string; +} + +// @public +interface URLContextResultContent { + call_id?: string; + is_error?: boolean; + result?: Array; + signature?: string; + type: 'url_context_result'; +} + // @public export interface UrlMetadata { retrievedUrl?: string; @@ -3602,6 +4401,40 @@ export enum UrlRetrievalStatus { URL_RETRIEVAL_STATUS_UNSPECIFIED = "URL_RETRIEVAL_STATUS_UNSPECIFIED" } +// @public +interface Usage { + cached_tokens_by_modality?: Array; + input_tokens_by_modality?: Array; + output_tokens_by_modality?: Array; + tool_use_tokens_by_modality?: Array; + total_cached_tokens?: number; + total_input_tokens?: number; + total_output_tokens?: number; + total_reasoning_tokens?: number; + total_tokens?: number; + total_tool_use_tokens?: number; +} + +// @public (undocumented) +namespace Usage { + interface CachedTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface InputTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface OutputTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface ToolUseTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } +} + // @public export interface UsageMetadata { cachedContentTokenCount?: number; @@ -3682,6 +4515,17 @@ export enum VideoCompressionQuality { OPTIMIZED = "OPTIMIZED" } +// @public +interface VideoContent { + // (undocumented) + data?: string; + mime_type?: VideoMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'video'; + // (undocumented) + uri?: string; +} + // @public export interface VideoGenerationMask { image?: Image_2; @@ -3715,6 +4559,9 @@ export interface VideoMetadata { startOffset?: string; } +// @public +type VideoMimeType = 'video/mp4' | 'video/mpeg' | 'video/mov' | 'video/avi' | 'video/x-flv' | 'video/mpg' | 'video/webm' | 'video/wmv' | 'video/3gpp' | (string & {}); + // @public (undocumented) export interface VoiceActivityDetectionSignal { vadSignalType?: VadSignalType; diff --git a/api-report/genai-web.api.md b/api-report/genai-web.api.md index d62bb9304..b48eab2bd 100644 --- a/api-report/genai-web.api.md +++ b/api-report/genai-web.api.md @@ -4,8 +4,11 @@ ```ts +/// + import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { GoogleAuthOptions } from 'google-auth-library'; +import { ReadableStream as ReadableStream_2 } from 'stream/web'; // @public export interface ActivityEnd { @@ -33,6 +36,19 @@ export enum AdapterSize { ADAPTER_SIZE_UNSPECIFIED = "ADAPTER_SIZE_UNSPECIFIED" } +// @public +interface AllowedTools { + mode?: ToolChoiceType; + tools?: Array; +} + +// @public +interface Annotation { + end_index?: number; + source?: string; + start_index?: number; +} + // @public export interface ApiAuth { apiKeyConfig?: ApiAuthApiKeyConfig; @@ -78,6 +94,19 @@ export interface AudioChunk { sourceMetadata?: LiveMusicSourceMetadata; } +// @public +interface AudioContent { + // (undocumented) + data?: string; + mime_type?: AudioMimeType; + type: 'audio'; + // (undocumented) + uri?: string; +} + +// @public +type AudioMimeType = 'audio/wav' | 'audio/mp3' | 'audio/aiff' | 'audio/aac' | 'audio/ogg' | 'audio/flac' | (string & {}); + // @public export interface AudioTranscriptionConfig { } @@ -147,6 +176,68 @@ export interface AutomaticFunctionCallingConfig { maximumRemoteCalls?: number; } +// @public (undocumented) +interface BaseCreateAgentInteractionParams { + agent: (string & {}) | 'deep-research-pro-preview-12-2025'; + agent_config?: DynamicAgentConfig | DeepResearchAgentConfig; + api_version?: string; + background?: boolean; + input: string | Array | Array | TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + previous_interaction_id?: string; + response_format?: unknown; + response_mime_type?: string; + response_modalities?: Array<'text' | 'image' | 'audio'>; + store?: boolean; + stream?: boolean; + system_instruction?: string; + tools?: Array; +} + +// @public (undocumented) +interface BaseCreateModelInteractionParams { + api_version?: string; + background?: boolean; + generation_config?: GenerationConfig_2; + input: string | Array | Array | TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + model: Model_2; + previous_interaction_id?: string; + response_format?: unknown; + response_mime_type?: string; + response_modalities?: Array<'text' | 'image' | 'audio'>; + store?: boolean; + stream?: boolean; + system_instruction?: string; + tools?: Array; +} + +// Warning: (ae-forgotten-export) The symbol "APIResource" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +class BaseInteractions extends APIResource { + cancel(id: string, params?: InteractionCancelParams | null | undefined, options?: RequestOptions): APIPromise; + // Warning: (ae-forgotten-export) The symbol "RequestOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "APIPromise" needs to be exported by the entry point index.d.ts + create(params: CreateModelInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + // Warning: (ae-forgotten-export) The symbol "Stream" needs to be exported by the entry point index.d.ts + // + // (undocumented) + create(params: CreateModelInteractionParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + create(params: CreateAgentInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + // (undocumented) + create(params: CreateAgentInteractionParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + create(params: BaseCreateModelInteractionParams | BaseCreateAgentInteractionParams, options?: RequestOptions): APIPromise | Interaction>; + delete(id: string, params?: InteractionDeleteParams | null | undefined, options?: RequestOptions): APIPromise; + get(id: string, params?: InteractionGetParamsNonStreaming, options?: RequestOptions): APIPromise; + // (undocumented) + get(id: string, params: InteractionGetParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + get(id: string, params?: InteractionGetParamsBase | undefined, options?: RequestOptions): APIPromise | Interaction>; + // (undocumented) + static readonly _key: readonly ['interactions']; +} + // @public export interface BaseUrlParameters { // (undocumented) @@ -368,12 +459,34 @@ export interface CitationMetadata { citations?: Citation[]; } +// @public +interface CodeExecutionCallArguments { + code?: string; + language?: 'python'; +} + +// @public +interface CodeExecutionCallContent { + arguments?: CodeExecutionCallArguments; + id?: string; + type: 'code_execution_call'; +} + // @public export interface CodeExecutionResult { outcome?: Outcome; output?: string; } +// @public +interface CodeExecutionResultContent { + call_id?: string; + is_error?: boolean; + result?: string; + signature?: string; + type: 'code_execution_result'; +} + // @public export interface CompletionStats { failedCount?: string; @@ -413,6 +526,202 @@ export interface Content { role?: string; } +// @public (undocumented) +interface ContentDelta { + // (undocumented) + delta?: ContentDelta.TextDelta | ContentDelta.ImageDelta | ContentDelta.AudioDelta | ContentDelta.DocumentDelta | ContentDelta.VideoDelta | ContentDelta.ThoughtSummaryDelta | ContentDelta.ThoughtSignatureDelta | ContentDelta.FunctionCallDelta | ContentDelta.FunctionResultDelta | ContentDelta.CodeExecutionCallDelta | ContentDelta.CodeExecutionResultDelta | ContentDelta.URLContextCallDelta | ContentDelta.URLContextResultDelta | ContentDelta.GoogleSearchCallDelta | ContentDelta.GoogleSearchResultDelta | ContentDelta.MCPServerToolCallDelta | ContentDelta.MCPServerToolResultDelta | ContentDelta.FileSearchResultDelta; + event_id?: string; + // (undocumented) + event_type?: 'content.delta'; + // (undocumented) + index?: number; +} + +// @public (undocumented) +namespace ContentDelta { + // (undocumented) + interface AudioDelta { + // (undocumented) + data?: string; + mime_type?: AudioMimeType; + type: 'audio'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface CodeExecutionCallDelta { + arguments?: CodeExecutionCallArguments; + id?: string; + type: 'code_execution_call'; + } + // (undocumented) + interface CodeExecutionResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: string; + // (undocumented) + signature?: string; + type: 'code_execution_result'; + } + // (undocumented) + interface DocumentDelta { + // (undocumented) + data?: string; + // (undocumented) + mime_type?: string; + type: 'document'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface FileSearchResultDelta { + // (undocumented) + result?: Array; + type: 'file_search_result'; + } + // (undocumented) + namespace FileSearchResultDelta { + interface Result { + file_search_store?: string; + text?: string; + title?: string; + } + } + // (undocumented) + interface FunctionCallDelta { + // (undocumented) + arguments?: { + [key: string]: unknown; + }; + id?: string; + // (undocumented) + name?: string; + type: 'function_call'; + } + // (undocumented) + interface FunctionResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + name?: string; + result?: FunctionResultDelta.Items | string; + type: 'function_result'; + } + // (undocumented) + namespace FunctionResultDelta { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } + } + // (undocumented) + interface GoogleSearchCallDelta { + arguments?: GoogleSearchCallArguments; + id?: string; + type: 'google_search_call'; + } + // (undocumented) + interface GoogleSearchResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: Array; + // (undocumented) + signature?: string; + type: 'google_search_result'; + } + // (undocumented) + interface ImageDelta { + // (undocumented) + data?: string; + mime_type?: ImageMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'image'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface MCPServerToolCallDelta { + // (undocumented) + arguments?: { + [key: string]: unknown; + }; + id?: string; + // (undocumented) + name?: string; + // (undocumented) + server_name?: string; + type: 'mcp_server_tool_call'; + } + // (undocumented) + interface MCPServerToolResultDelta { + call_id?: string; + // (undocumented) + name?: string; + result?: MCPServerToolResultDelta.Items | string; + // (undocumented) + server_name?: string; + type: 'mcp_server_tool_result'; + } + // (undocumented) + namespace MCPServerToolResultDelta { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } + } + // (undocumented) + interface TextDelta { + annotations?: Array; + // (undocumented) + text?: string; + type: 'text'; + } + // (undocumented) + interface ThoughtSignatureDelta { + signature?: string; + type: 'thought_signature'; + } + // (undocumented) + interface ThoughtSummaryDelta { + content?: TextContent | ImageContent; + type: 'thought_summary'; + } + // (undocumented) + interface URLContextCallDelta { + arguments?: URLContextCallArguments; + id?: string; + type: 'url_context_call'; + } + // (undocumented) + interface URLContextResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: Array; + // (undocumented) + signature?: string; + type: 'url_context_result'; + } + // (undocumented) + interface VideoDelta { + // (undocumented) + data?: string; + mime_type?: VideoMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'video'; + // (undocumented) + uri?: string; + } +} + // @public export interface ContentEmbedding { statistics?: ContentEmbeddingStatistics; @@ -437,6 +746,25 @@ export class ContentReferenceImage { toReferenceImageAPI(): ReferenceImageAPIInternal; } +// @public (undocumented) +interface ContentStart { + content?: TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + event_id?: string; + // (undocumented) + event_type?: 'content.start'; + // (undocumented) + index?: number; +} + +// @public (undocumented) +interface ContentStop { + event_id?: string; + // (undocumented) + event_type?: 'content.stop'; + // (undocumented) + index?: number; +} + // @public (undocumented) export type ContentUnion = Content | PartUnion[] | PartUnion; @@ -496,6 +824,16 @@ export class CountTokensResponse { totalTokens?: number; } +// @public (undocumented) +interface CreateAgentInteractionParamsNonStreaming extends BaseCreateAgentInteractionParams { + stream?: false; +} + +// @public (undocumented) +interface CreateAgentInteractionParamsStreaming extends BaseCreateAgentInteractionParams { + stream: true; +} + // @public export interface CreateAuthTokenConfig { abortSignal?: AbortSignal; @@ -606,6 +944,16 @@ export function createFunctionResponsePartFromUri(uri: string, mimeType: string) // @public export function createModelContent(partOrString: PartListUnion | string): Content; +// @public (undocumented) +interface CreateModelInteractionParamsNonStreaming extends BaseCreateModelInteractionParams { + stream?: false; +} + +// @public (undocumented) +interface CreateModelInteractionParamsStreaming extends BaseCreateModelInteractionParams { + stream: true; +} + // @public export function createPartFromBase64(data: string, mimeType: string, mediaResolution?: PartMediaResolutionLevel): Part; @@ -703,6 +1051,12 @@ export interface DatasetStats { userOutputTokenDistribution?: DatasetDistribution; } +// @public +interface DeepResearchAgentConfig { + thinking_summaries?: 'auto' | 'none'; + type?: 'deep-research'; +} + // @public export interface DeleteBatchJobConfig { abortSignal?: AbortSignal; @@ -822,6 +1176,17 @@ interface Document_2 { } export { Document_2 as Document } +// @public +interface DocumentContent { + // (undocumented) + data?: string; + // (undocumented) + mime_type?: string; + type: 'document'; + // (undocumented) + uri?: string; +} + // @public export enum DocumentState { // (undocumented) @@ -850,6 +1215,13 @@ export interface DownloadFileParameters { file: DownloadableFileUnion; } +// @public +interface DynamicAgentConfig { + // (undocumented) + [k: string]: unknown; + type?: 'dynamic'; +} + // @public export interface DynamicRetrievalConfig { dynamicThreshold?: number; @@ -997,6 +1369,22 @@ export enum Environment { ENVIRONMENT_UNSPECIFIED = "ENVIRONMENT_UNSPECIFIED" } +// @public (undocumented) +interface ErrorEvent_2 { + error?: ErrorEvent_2.Error; + event_id?: string; + // (undocumented) + event_type?: 'error'; +} + +// @public (undocumented) +namespace ErrorEvent_2 { + interface Error { + code?: string; + message?: string; + } +} + // @public export interface ExecutableCode { code?: string; @@ -1093,6 +1481,21 @@ export interface FileSearch { topK?: number; } +// @public +interface FileSearchResultContent { + result?: Array; + type: 'file_search_result'; +} + +// @public (undocumented) +namespace FileSearchResultContent { + interface Result { + file_search_store?: string; + text?: string; + title?: string; + } +} + // @public export interface FileSearchStore { activeDocumentsCount?: string; @@ -1153,6 +1556,15 @@ export enum FinishReason { UNEXPECTED_TOOL_CALL = "UNEXPECTED_TOOL_CALL" } +// @public +interface Function_2 { + description?: string; + name?: string; + parameters?: unknown; + // (undocumented) + type: 'function'; +} + // @public export interface FunctionCall { args?: Record; @@ -1162,6 +1574,16 @@ export interface FunctionCall { willContinue?: boolean; } +// @public +interface FunctionCallContent { + arguments: { + [key: string]: unknown; + }; + id: string; + name: string; + type: 'function_call'; +} + // @public export interface FunctionCallingConfig { allowedFunctionNames?: string[]; @@ -1227,6 +1649,24 @@ export enum FunctionResponseScheduling { WHEN_IDLE = "WHEN_IDLE" } +// @public +interface FunctionResultContent { + call_id: string; + is_error?: boolean; + name?: string; + result: FunctionResultContent.Items | unknown | string; + type: 'function_result'; +} + +// @public (undocumented) +namespace FunctionResultContent { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } +} + // @public export interface GeminiPreferenceExample { completions?: GeminiPreferenceExampleCompletion[]; @@ -1462,6 +1902,19 @@ export interface GenerationConfig { topP?: number; } +// @public +interface GenerationConfig_2 { + max_output_tokens?: number; + seed?: number; + speech_config?: Array; + stop_sequences?: Array; + temperature?: number; + thinking_level?: ThinkingLevel_2; + thinking_summaries?: 'auto' | 'none'; + tool_choice?: ToolChoice; + top_p?: number; +} + // @public export interface GenerationConfigRoutingConfig { autoMode?: GenerationConfigRoutingConfigAutoRoutingMode; @@ -1600,6 +2053,8 @@ export class GoogleGenAI { // (undocumented) readonly fileSearchStores: FileSearchStores; // (undocumented) + get interactions(): Interactions_2; + // (undocumented) readonly live: Live; // (undocumented) readonly models: Models; @@ -1644,6 +2099,34 @@ export interface GoogleSearch { timeRangeFilter?: Interval; } +// @public +interface GoogleSearchCallArguments { + queries?: Array; +} + +// @public +interface GoogleSearchCallContent { + arguments?: GoogleSearchCallArguments; + id?: string; + type: 'google_search_call'; +} + +// @public +interface GoogleSearchResult { + rendered_content?: string; + title?: string; + url?: string; +} + +// @public +interface GoogleSearchResultContent { + call_id?: string; + is_error?: boolean; + result?: Array; + signature?: string; + type: 'google_search_result'; +} + // @public export interface GoogleSearchRetrieval { dynamicRetrievalConfig?: DynamicRetrievalConfig; @@ -1832,6 +2315,20 @@ export interface ImageConfig { outputMimeType?: string; } +// @public +interface ImageContent { + // (undocumented) + data?: string; + mime_type?: ImageMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'image'; + // (undocumented) + uri?: string; +} + +// @public +type ImageMimeType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/heic' | 'image/heif' | (string & {}); + // @public export enum ImagePromptLanguage { auto = "auto", @@ -1898,6 +2395,166 @@ export class InlinedResponse { response?: GenerateContentResponse; } +// @public +interface Interaction { + agent?: (string & {}) | 'deep-research-pro-preview-12-2025'; + created?: string; + error?: Interaction.Error; + id: string; + model?: Model_2; + object?: 'interaction'; + outputs?: Array; + previous_interaction_id?: string; + role?: string; + status: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; + updated?: string; + usage?: Usage; +} + +// @public (undocumented) +namespace Interaction { + interface Error { + code?: string; + message?: string; + } +} + +// @public (undocumented) +interface InteractionCancelParams { + api_version?: string; +} + +// @public (undocumented) +type InteractionCreateParams = CreateModelInteractionParamsNonStreaming | CreateModelInteractionParamsStreaming | CreateAgentInteractionParamsNonStreaming | CreateAgentInteractionParamsStreaming; + +// @public (undocumented) +interface InteractionDeleteParams { + api_version?: string; +} + +// @public (undocumented) +type InteractionDeleteResponse = unknown; + +// @public (undocumented) +interface InteractionEvent { + event_id?: string; + // (undocumented) + event_type?: 'interaction.start' | 'interaction.complete'; + interaction?: Interaction; +} + +// @public (undocumented) +type InteractionGetParams = InteractionGetParamsNonStreaming | InteractionGetParamsStreaming; + +// @public (undocumented) +interface InteractionGetParamsBase { + api_version?: string; + last_event_id?: string; + stream?: boolean; +} + +// @public (undocumented) +interface InteractionGetParamsNonStreaming extends InteractionGetParamsBase { + stream?: false; +} + +// @public (undocumented) +interface InteractionGetParamsStreaming extends InteractionGetParamsBase { + stream: true; +} + +declare namespace Interactions { + export { + BaseInteractions, + Interactions_2 as Interactions, + AllowedTools, + Annotation, + AudioContent, + AudioMimeType, + CodeExecutionCallArguments, + CodeExecutionCallContent, + CodeExecutionResultContent, + ContentDelta, + ContentStart, + ContentStop, + DeepResearchAgentConfig, + DocumentContent, + DynamicAgentConfig, + ErrorEvent_2 as ErrorEvent, + FileSearchResultContent, + Function_2 as Function, + FunctionCallContent, + FunctionResultContent, + GenerationConfig_2 as GenerationConfig, + GoogleSearchCallArguments, + GoogleSearchCallContent, + GoogleSearchResult, + GoogleSearchResultContent, + ImageContent, + ImageMimeType, + Interaction, + InteractionEvent, + InteractionSSEEvent, + InteractionStatusUpdate, + MCPServerToolCallContent, + MCPServerToolResultContent, + Model_2 as Model, + SpeechConfig_2 as SpeechConfig, + TextContent, + ThinkingLevel_2 as ThinkingLevel, + ThoughtContent, + Tool_2 as Tool, + ToolChoice, + ToolChoiceConfig, + ToolChoiceType, + Turn, + URLContextCallArguments, + URLContextCallContent, + URLContextResult, + URLContextResultContent, + Usage, + VideoContent, + VideoMimeType, + InteractionDeleteResponse, + InteractionCreateParams, + BaseCreateModelInteractionParams, + BaseCreateAgentInteractionParams, + CreateModelInteractionParamsNonStreaming, + CreateModelInteractionParamsStreaming, + CreateAgentInteractionParamsNonStreaming, + CreateAgentInteractionParamsStreaming, + InteractionDeleteParams, + InteractionCancelParams, + InteractionGetParams, + InteractionGetParamsBase, + InteractionGetParamsNonStreaming, + InteractionGetParamsStreaming + } +} + +// @public (undocumented) +class Interactions_2 extends BaseInteractions { +} + +// @public (undocumented) +namespace Interactions_2 { + { type AllowedTools as AllowedTools, type Annotation as Annotation, type AudioContent as AudioContent, type AudioMimeType as AudioMimeType, type CodeExecutionCallArguments as CodeExecutionCallArguments, type CodeExecutionCallContent as CodeExecutionCallContent, type CodeExecutionResultContent as CodeExecutionResultContent, type ContentDelta as ContentDelta, type ContentStart as ContentStart, type ContentStop as ContentStop, type DeepResearchAgentConfig as DeepResearchAgentConfig, type DocumentContent as DocumentContent, type DynamicAgentConfig as DynamicAgentConfig, type ErrorEvent as ErrorEvent, type FileSearchResultContent as FileSearchResultContent, type Function as Function, type FunctionCallContent as FunctionCallContent, type FunctionResultContent as FunctionResultContent, type GenerationConfig as GenerationConfig, type GoogleSearchCallArguments as GoogleSearchCallArguments, type GoogleSearchCallContent as GoogleSearchCallContent, type GoogleSearchResult as GoogleSearchResult, type GoogleSearchResultContent as GoogleSearchResultContent, type ImageContent as ImageContent, type ImageMimeType as ImageMimeType, type Interaction as Interaction, type InteractionEvent as InteractionEvent, type InteractionSSEEvent as InteractionSSEEvent, type InteractionStatusUpdate as InteractionStatusUpdate, type MCPServerToolCallContent as MCPServerToolCallContent, type MCPServerToolResultContent as MCPServerToolResultContent, type Model as Model, type SpeechConfig as SpeechConfig, type TextContent as TextContent, type ThinkingLevel as ThinkingLevel, type ThoughtContent as ThoughtContent, type Tool as Tool, type ToolChoice as ToolChoice, type ToolChoiceConfig as ToolChoiceConfig, type ToolChoiceType as ToolChoiceType, type Turn as Turn, type URLContextCallArguments as URLContextCallArguments, type URLContextCallContent as URLContextCallContent, type URLContextResult as URLContextResult, type URLContextResultContent as URLContextResultContent, type Usage as Usage, type VideoContent as VideoContent, type VideoMimeType as VideoMimeType, type InteractionDeleteResponse as InteractionDeleteResponse, type InteractionCreateParams as InteractionCreateParams, type CreateModelInteractionParamsNonStreaming as CreateModelInteractionParamsNonStreaming, type CreateModelInteractionParamsStreaming as CreateModelInteractionParamsStreaming, type CreateAgentInteractionParamsNonStreaming as CreateAgentInteractionParamsNonStreaming, type CreateAgentInteractionParamsStreaming as CreateAgentInteractionParamsStreaming, type InteractionDeleteParams as InteractionDeleteParams, type InteractionCancelParams as InteractionCancelParams, type InteractionGetParams as InteractionGetParams, type InteractionGetParamsNonStreaming as InteractionGetParamsNonStreaming, type InteractionGetParamsStreaming as InteractionGetParamsStreaming, }; +} + +// @public (undocumented) +type InteractionSSEEvent = InteractionEvent | InteractionStatusUpdate | ContentStart | ContentDelta | ContentStop | ErrorEvent_2; + +// @public (undocumented) +interface InteractionStatusUpdate { + event_id?: string; + // (undocumented) + event_type?: 'interaction.status_update'; + // (undocumented) + interaction_id?: string; + // (undocumented) + status?: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; +} + // @public export interface Interval { endTime?: string; @@ -2451,6 +3108,35 @@ export enum MaskReferenceMode { MASK_MODE_USER_PROVIDED = "MASK_MODE_USER_PROVIDED" } +// @public +interface MCPServerToolCallContent { + arguments: { + [key: string]: unknown; + }; + id: string; + name: string; + server_name: string; + type: 'mcp_server_tool_call'; +} + +// @public +interface MCPServerToolResultContent { + call_id: string; + name?: string; + result: MCPServerToolResultContent.Items | unknown | string; + server_name?: string; + type: 'mcp_server_tool_result'; +} + +// @public (undocumented) +namespace MCPServerToolResultContent { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } +} + // @public export function mcpToTool(...args: [...Client[], CallableToolConfig | Client]): CallableTool; @@ -2513,6 +3199,9 @@ export interface Model { version?: string; } +// @public +type Model_2 = 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'gemini-2.5-flash-preview-09-2025' | 'gemini-2.5-flash-lite' | 'gemini-2.5-flash-lite-preview-09-2025' | 'gemini-2.5-flash-preview-native-audio-dialog' | 'gemini-2.5-flash-image-preview' | 'gemini-2.5-pro-preview-tts' | 'gemini-3-pro-preview' | (string & {}); + // @public (undocumented) export class Models extends BaseModule { constructor(apiClient: ApiClient); @@ -3114,6 +3803,13 @@ export interface SpeechConfig { voiceConfig?: VoiceConfig; } +// @public +interface SpeechConfig_2 { + language?: string; + speaker?: string; + voice?: string; +} + // @public (undocumented) export type SpeechConfigUnion = SpeechConfig | string; @@ -3249,6 +3945,13 @@ export interface TestTableItem { skipInApiMode?: string; } +// @public +interface TextContent { + annotations?: Array; + text?: string; + type: 'text'; +} + // @public export interface ThinkingConfig { includeThoughts?: boolean; @@ -3263,6 +3966,16 @@ export enum ThinkingLevel { THINKING_LEVEL_UNSPECIFIED = "THINKING_LEVEL_UNSPECIFIED" } +// @public (undocumented) +type ThinkingLevel_2 = 'low' | 'high'; + +// @public +interface ThoughtContent { + signature?: string; + summary?: Array; + type: 'thought'; +} + // @public (undocumented) export class Tokens extends BaseModule { constructor(apiClient: ApiClient); @@ -3290,6 +4003,59 @@ export interface Tool { urlContext?: UrlContext; } +// @public +type Tool_2 = Function_2 | Tool_2.GoogleSearch | Tool_2.CodeExecution | Tool_2.URLContext | Tool_2.ComputerUse | Tool_2.MCPServer | Tool_2.FileSearch; + +// @public (undocumented) +namespace Tool_2 { + interface CodeExecution { + // (undocumented) + type: 'code_execution'; + } + interface ComputerUse { + environment?: 'browser'; + excludedPredefinedFunctions?: Array; + // (undocumented) + type: 'computer_use'; + } + interface FileSearch { + file_search_store_names?: Array; + metadata_filter?: string; + top_k?: number; + // (undocumented) + type: 'file_search'; + } + interface GoogleSearch { + // (undocumented) + type: 'google_search'; + } + interface MCPServer { + allowed_tools?: Array; + headers?: { + [key: string]: string; + }; + name?: string; + // (undocumented) + type: 'mcp_server'; + url?: string; + } + interface URLContext { + // (undocumented) + type: 'url_context'; + } +} + +// @public +type ToolChoice = ToolChoiceType | ToolChoiceConfig; + +// @public (undocumented) +interface ToolChoiceConfig { + allowed_tools?: AllowedTools; +} + +// @public (undocumented) +type ToolChoiceType = 'auto' | 'any' | 'none' | 'validated'; + // @public export interface ToolCodeExecution { } @@ -3426,6 +4192,12 @@ export interface TuningValidationDataset { vertexDatasetResource?: string; } +// @public (undocumented) +interface Turn { + content?: string | Array; + role?: string; +} + // @public export enum TurnCompleteReason { MALFORMED_FUNCTION_CALL = "MALFORMED_FUNCTION_CALL", @@ -3582,11 +4354,38 @@ export class UpscaleImageResponse { export interface UrlContext { } +// @public +interface URLContextCallArguments { + urls?: Array; +} + +// @public +interface URLContextCallContent { + arguments?: URLContextCallArguments; + id?: string; + type: 'url_context_call'; +} + // @public export interface UrlContextMetadata { urlMetadata?: UrlMetadata[]; } +// @public +interface URLContextResult { + status?: 'success' | 'error' | 'paywall' | 'unsafe'; + url?: string; +} + +// @public +interface URLContextResultContent { + call_id?: string; + is_error?: boolean; + result?: Array; + signature?: string; + type: 'url_context_result'; +} + // @public export interface UrlMetadata { retrievedUrl?: string; @@ -3602,6 +4401,40 @@ export enum UrlRetrievalStatus { URL_RETRIEVAL_STATUS_UNSPECIFIED = "URL_RETRIEVAL_STATUS_UNSPECIFIED" } +// @public +interface Usage { + cached_tokens_by_modality?: Array; + input_tokens_by_modality?: Array; + output_tokens_by_modality?: Array; + tool_use_tokens_by_modality?: Array; + total_cached_tokens?: number; + total_input_tokens?: number; + total_output_tokens?: number; + total_reasoning_tokens?: number; + total_tokens?: number; + total_tool_use_tokens?: number; +} + +// @public (undocumented) +namespace Usage { + interface CachedTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface InputTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface OutputTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface ToolUseTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } +} + // @public export interface UsageMetadata { cachedContentTokenCount?: number; @@ -3682,6 +4515,17 @@ export enum VideoCompressionQuality { OPTIMIZED = "OPTIMIZED" } +// @public +interface VideoContent { + // (undocumented) + data?: string; + mime_type?: VideoMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'video'; + // (undocumented) + uri?: string; +} + // @public export interface VideoGenerationMask { image?: Image_2; @@ -3715,6 +4559,9 @@ export interface VideoMetadata { startOffset?: string; } +// @public +type VideoMimeType = 'video/mp4' | 'video/mpeg' | 'video/mov' | 'video/avi' | 'video/x-flv' | 'video/mpg' | 'video/webm' | 'video/wmv' | 'video/3gpp' | (string & {}); + // @public (undocumented) export interface VoiceActivityDetectionSignal { vadSignalType?: VadSignalType; diff --git a/api-report/genai.api.md b/api-report/genai.api.md index d62bb9304..b48eab2bd 100644 --- a/api-report/genai.api.md +++ b/api-report/genai.api.md @@ -4,8 +4,11 @@ ```ts +/// + import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { GoogleAuthOptions } from 'google-auth-library'; +import { ReadableStream as ReadableStream_2 } from 'stream/web'; // @public export interface ActivityEnd { @@ -33,6 +36,19 @@ export enum AdapterSize { ADAPTER_SIZE_UNSPECIFIED = "ADAPTER_SIZE_UNSPECIFIED" } +// @public +interface AllowedTools { + mode?: ToolChoiceType; + tools?: Array; +} + +// @public +interface Annotation { + end_index?: number; + source?: string; + start_index?: number; +} + // @public export interface ApiAuth { apiKeyConfig?: ApiAuthApiKeyConfig; @@ -78,6 +94,19 @@ export interface AudioChunk { sourceMetadata?: LiveMusicSourceMetadata; } +// @public +interface AudioContent { + // (undocumented) + data?: string; + mime_type?: AudioMimeType; + type: 'audio'; + // (undocumented) + uri?: string; +} + +// @public +type AudioMimeType = 'audio/wav' | 'audio/mp3' | 'audio/aiff' | 'audio/aac' | 'audio/ogg' | 'audio/flac' | (string & {}); + // @public export interface AudioTranscriptionConfig { } @@ -147,6 +176,68 @@ export interface AutomaticFunctionCallingConfig { maximumRemoteCalls?: number; } +// @public (undocumented) +interface BaseCreateAgentInteractionParams { + agent: (string & {}) | 'deep-research-pro-preview-12-2025'; + agent_config?: DynamicAgentConfig | DeepResearchAgentConfig; + api_version?: string; + background?: boolean; + input: string | Array | Array | TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + previous_interaction_id?: string; + response_format?: unknown; + response_mime_type?: string; + response_modalities?: Array<'text' | 'image' | 'audio'>; + store?: boolean; + stream?: boolean; + system_instruction?: string; + tools?: Array; +} + +// @public (undocumented) +interface BaseCreateModelInteractionParams { + api_version?: string; + background?: boolean; + generation_config?: GenerationConfig_2; + input: string | Array | Array | TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + model: Model_2; + previous_interaction_id?: string; + response_format?: unknown; + response_mime_type?: string; + response_modalities?: Array<'text' | 'image' | 'audio'>; + store?: boolean; + stream?: boolean; + system_instruction?: string; + tools?: Array; +} + +// Warning: (ae-forgotten-export) The symbol "APIResource" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +class BaseInteractions extends APIResource { + cancel(id: string, params?: InteractionCancelParams | null | undefined, options?: RequestOptions): APIPromise; + // Warning: (ae-forgotten-export) The symbol "RequestOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "APIPromise" needs to be exported by the entry point index.d.ts + create(params: CreateModelInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + // Warning: (ae-forgotten-export) The symbol "Stream" needs to be exported by the entry point index.d.ts + // + // (undocumented) + create(params: CreateModelInteractionParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + create(params: CreateAgentInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + // (undocumented) + create(params: CreateAgentInteractionParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + create(params: BaseCreateModelInteractionParams | BaseCreateAgentInteractionParams, options?: RequestOptions): APIPromise | Interaction>; + delete(id: string, params?: InteractionDeleteParams | null | undefined, options?: RequestOptions): APIPromise; + get(id: string, params?: InteractionGetParamsNonStreaming, options?: RequestOptions): APIPromise; + // (undocumented) + get(id: string, params: InteractionGetParamsStreaming, options?: RequestOptions): APIPromise>; + // (undocumented) + get(id: string, params?: InteractionGetParamsBase | undefined, options?: RequestOptions): APIPromise | Interaction>; + // (undocumented) + static readonly _key: readonly ['interactions']; +} + // @public export interface BaseUrlParameters { // (undocumented) @@ -368,12 +459,34 @@ export interface CitationMetadata { citations?: Citation[]; } +// @public +interface CodeExecutionCallArguments { + code?: string; + language?: 'python'; +} + +// @public +interface CodeExecutionCallContent { + arguments?: CodeExecutionCallArguments; + id?: string; + type: 'code_execution_call'; +} + // @public export interface CodeExecutionResult { outcome?: Outcome; output?: string; } +// @public +interface CodeExecutionResultContent { + call_id?: string; + is_error?: boolean; + result?: string; + signature?: string; + type: 'code_execution_result'; +} + // @public export interface CompletionStats { failedCount?: string; @@ -413,6 +526,202 @@ export interface Content { role?: string; } +// @public (undocumented) +interface ContentDelta { + // (undocumented) + delta?: ContentDelta.TextDelta | ContentDelta.ImageDelta | ContentDelta.AudioDelta | ContentDelta.DocumentDelta | ContentDelta.VideoDelta | ContentDelta.ThoughtSummaryDelta | ContentDelta.ThoughtSignatureDelta | ContentDelta.FunctionCallDelta | ContentDelta.FunctionResultDelta | ContentDelta.CodeExecutionCallDelta | ContentDelta.CodeExecutionResultDelta | ContentDelta.URLContextCallDelta | ContentDelta.URLContextResultDelta | ContentDelta.GoogleSearchCallDelta | ContentDelta.GoogleSearchResultDelta | ContentDelta.MCPServerToolCallDelta | ContentDelta.MCPServerToolResultDelta | ContentDelta.FileSearchResultDelta; + event_id?: string; + // (undocumented) + event_type?: 'content.delta'; + // (undocumented) + index?: number; +} + +// @public (undocumented) +namespace ContentDelta { + // (undocumented) + interface AudioDelta { + // (undocumented) + data?: string; + mime_type?: AudioMimeType; + type: 'audio'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface CodeExecutionCallDelta { + arguments?: CodeExecutionCallArguments; + id?: string; + type: 'code_execution_call'; + } + // (undocumented) + interface CodeExecutionResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: string; + // (undocumented) + signature?: string; + type: 'code_execution_result'; + } + // (undocumented) + interface DocumentDelta { + // (undocumented) + data?: string; + // (undocumented) + mime_type?: string; + type: 'document'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface FileSearchResultDelta { + // (undocumented) + result?: Array; + type: 'file_search_result'; + } + // (undocumented) + namespace FileSearchResultDelta { + interface Result { + file_search_store?: string; + text?: string; + title?: string; + } + } + // (undocumented) + interface FunctionCallDelta { + // (undocumented) + arguments?: { + [key: string]: unknown; + }; + id?: string; + // (undocumented) + name?: string; + type: 'function_call'; + } + // (undocumented) + interface FunctionResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + name?: string; + result?: FunctionResultDelta.Items | string; + type: 'function_result'; + } + // (undocumented) + namespace FunctionResultDelta { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } + } + // (undocumented) + interface GoogleSearchCallDelta { + arguments?: GoogleSearchCallArguments; + id?: string; + type: 'google_search_call'; + } + // (undocumented) + interface GoogleSearchResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: Array; + // (undocumented) + signature?: string; + type: 'google_search_result'; + } + // (undocumented) + interface ImageDelta { + // (undocumented) + data?: string; + mime_type?: ImageMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'image'; + // (undocumented) + uri?: string; + } + // (undocumented) + interface MCPServerToolCallDelta { + // (undocumented) + arguments?: { + [key: string]: unknown; + }; + id?: string; + // (undocumented) + name?: string; + // (undocumented) + server_name?: string; + type: 'mcp_server_tool_call'; + } + // (undocumented) + interface MCPServerToolResultDelta { + call_id?: string; + // (undocumented) + name?: string; + result?: MCPServerToolResultDelta.Items | string; + // (undocumented) + server_name?: string; + type: 'mcp_server_tool_result'; + } + // (undocumented) + namespace MCPServerToolResultDelta { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } + } + // (undocumented) + interface TextDelta { + annotations?: Array; + // (undocumented) + text?: string; + type: 'text'; + } + // (undocumented) + interface ThoughtSignatureDelta { + signature?: string; + type: 'thought_signature'; + } + // (undocumented) + interface ThoughtSummaryDelta { + content?: TextContent | ImageContent; + type: 'thought_summary'; + } + // (undocumented) + interface URLContextCallDelta { + arguments?: URLContextCallArguments; + id?: string; + type: 'url_context_call'; + } + // (undocumented) + interface URLContextResultDelta { + call_id?: string; + // (undocumented) + is_error?: boolean; + // (undocumented) + result?: Array; + // (undocumented) + signature?: string; + type: 'url_context_result'; + } + // (undocumented) + interface VideoDelta { + // (undocumented) + data?: string; + mime_type?: VideoMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'video'; + // (undocumented) + uri?: string; + } +} + // @public export interface ContentEmbedding { statistics?: ContentEmbeddingStatistics; @@ -437,6 +746,25 @@ export class ContentReferenceImage { toReferenceImageAPI(): ReferenceImageAPIInternal; } +// @public (undocumented) +interface ContentStart { + content?: TextContent | ImageContent | AudioContent | DocumentContent | VideoContent | ThoughtContent | FunctionCallContent | FunctionResultContent | CodeExecutionCallContent | CodeExecutionResultContent | URLContextCallContent | URLContextResultContent | GoogleSearchCallContent | GoogleSearchResultContent | MCPServerToolCallContent | MCPServerToolResultContent | FileSearchResultContent; + event_id?: string; + // (undocumented) + event_type?: 'content.start'; + // (undocumented) + index?: number; +} + +// @public (undocumented) +interface ContentStop { + event_id?: string; + // (undocumented) + event_type?: 'content.stop'; + // (undocumented) + index?: number; +} + // @public (undocumented) export type ContentUnion = Content | PartUnion[] | PartUnion; @@ -496,6 +824,16 @@ export class CountTokensResponse { totalTokens?: number; } +// @public (undocumented) +interface CreateAgentInteractionParamsNonStreaming extends BaseCreateAgentInteractionParams { + stream?: false; +} + +// @public (undocumented) +interface CreateAgentInteractionParamsStreaming extends BaseCreateAgentInteractionParams { + stream: true; +} + // @public export interface CreateAuthTokenConfig { abortSignal?: AbortSignal; @@ -606,6 +944,16 @@ export function createFunctionResponsePartFromUri(uri: string, mimeType: string) // @public export function createModelContent(partOrString: PartListUnion | string): Content; +// @public (undocumented) +interface CreateModelInteractionParamsNonStreaming extends BaseCreateModelInteractionParams { + stream?: false; +} + +// @public (undocumented) +interface CreateModelInteractionParamsStreaming extends BaseCreateModelInteractionParams { + stream: true; +} + // @public export function createPartFromBase64(data: string, mimeType: string, mediaResolution?: PartMediaResolutionLevel): Part; @@ -703,6 +1051,12 @@ export interface DatasetStats { userOutputTokenDistribution?: DatasetDistribution; } +// @public +interface DeepResearchAgentConfig { + thinking_summaries?: 'auto' | 'none'; + type?: 'deep-research'; +} + // @public export interface DeleteBatchJobConfig { abortSignal?: AbortSignal; @@ -822,6 +1176,17 @@ interface Document_2 { } export { Document_2 as Document } +// @public +interface DocumentContent { + // (undocumented) + data?: string; + // (undocumented) + mime_type?: string; + type: 'document'; + // (undocumented) + uri?: string; +} + // @public export enum DocumentState { // (undocumented) @@ -850,6 +1215,13 @@ export interface DownloadFileParameters { file: DownloadableFileUnion; } +// @public +interface DynamicAgentConfig { + // (undocumented) + [k: string]: unknown; + type?: 'dynamic'; +} + // @public export interface DynamicRetrievalConfig { dynamicThreshold?: number; @@ -997,6 +1369,22 @@ export enum Environment { ENVIRONMENT_UNSPECIFIED = "ENVIRONMENT_UNSPECIFIED" } +// @public (undocumented) +interface ErrorEvent_2 { + error?: ErrorEvent_2.Error; + event_id?: string; + // (undocumented) + event_type?: 'error'; +} + +// @public (undocumented) +namespace ErrorEvent_2 { + interface Error { + code?: string; + message?: string; + } +} + // @public export interface ExecutableCode { code?: string; @@ -1093,6 +1481,21 @@ export interface FileSearch { topK?: number; } +// @public +interface FileSearchResultContent { + result?: Array; + type: 'file_search_result'; +} + +// @public (undocumented) +namespace FileSearchResultContent { + interface Result { + file_search_store?: string; + text?: string; + title?: string; + } +} + // @public export interface FileSearchStore { activeDocumentsCount?: string; @@ -1153,6 +1556,15 @@ export enum FinishReason { UNEXPECTED_TOOL_CALL = "UNEXPECTED_TOOL_CALL" } +// @public +interface Function_2 { + description?: string; + name?: string; + parameters?: unknown; + // (undocumented) + type: 'function'; +} + // @public export interface FunctionCall { args?: Record; @@ -1162,6 +1574,16 @@ export interface FunctionCall { willContinue?: boolean; } +// @public +interface FunctionCallContent { + arguments: { + [key: string]: unknown; + }; + id: string; + name: string; + type: 'function_call'; +} + // @public export interface FunctionCallingConfig { allowedFunctionNames?: string[]; @@ -1227,6 +1649,24 @@ export enum FunctionResponseScheduling { WHEN_IDLE = "WHEN_IDLE" } +// @public +interface FunctionResultContent { + call_id: string; + is_error?: boolean; + name?: string; + result: FunctionResultContent.Items | unknown | string; + type: 'function_result'; +} + +// @public (undocumented) +namespace FunctionResultContent { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } +} + // @public export interface GeminiPreferenceExample { completions?: GeminiPreferenceExampleCompletion[]; @@ -1462,6 +1902,19 @@ export interface GenerationConfig { topP?: number; } +// @public +interface GenerationConfig_2 { + max_output_tokens?: number; + seed?: number; + speech_config?: Array; + stop_sequences?: Array; + temperature?: number; + thinking_level?: ThinkingLevel_2; + thinking_summaries?: 'auto' | 'none'; + tool_choice?: ToolChoice; + top_p?: number; +} + // @public export interface GenerationConfigRoutingConfig { autoMode?: GenerationConfigRoutingConfigAutoRoutingMode; @@ -1600,6 +2053,8 @@ export class GoogleGenAI { // (undocumented) readonly fileSearchStores: FileSearchStores; // (undocumented) + get interactions(): Interactions_2; + // (undocumented) readonly live: Live; // (undocumented) readonly models: Models; @@ -1644,6 +2099,34 @@ export interface GoogleSearch { timeRangeFilter?: Interval; } +// @public +interface GoogleSearchCallArguments { + queries?: Array; +} + +// @public +interface GoogleSearchCallContent { + arguments?: GoogleSearchCallArguments; + id?: string; + type: 'google_search_call'; +} + +// @public +interface GoogleSearchResult { + rendered_content?: string; + title?: string; + url?: string; +} + +// @public +interface GoogleSearchResultContent { + call_id?: string; + is_error?: boolean; + result?: Array; + signature?: string; + type: 'google_search_result'; +} + // @public export interface GoogleSearchRetrieval { dynamicRetrievalConfig?: DynamicRetrievalConfig; @@ -1832,6 +2315,20 @@ export interface ImageConfig { outputMimeType?: string; } +// @public +interface ImageContent { + // (undocumented) + data?: string; + mime_type?: ImageMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'image'; + // (undocumented) + uri?: string; +} + +// @public +type ImageMimeType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/heic' | 'image/heif' | (string & {}); + // @public export enum ImagePromptLanguage { auto = "auto", @@ -1898,6 +2395,166 @@ export class InlinedResponse { response?: GenerateContentResponse; } +// @public +interface Interaction { + agent?: (string & {}) | 'deep-research-pro-preview-12-2025'; + created?: string; + error?: Interaction.Error; + id: string; + model?: Model_2; + object?: 'interaction'; + outputs?: Array; + previous_interaction_id?: string; + role?: string; + status: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; + updated?: string; + usage?: Usage; +} + +// @public (undocumented) +namespace Interaction { + interface Error { + code?: string; + message?: string; + } +} + +// @public (undocumented) +interface InteractionCancelParams { + api_version?: string; +} + +// @public (undocumented) +type InteractionCreateParams = CreateModelInteractionParamsNonStreaming | CreateModelInteractionParamsStreaming | CreateAgentInteractionParamsNonStreaming | CreateAgentInteractionParamsStreaming; + +// @public (undocumented) +interface InteractionDeleteParams { + api_version?: string; +} + +// @public (undocumented) +type InteractionDeleteResponse = unknown; + +// @public (undocumented) +interface InteractionEvent { + event_id?: string; + // (undocumented) + event_type?: 'interaction.start' | 'interaction.complete'; + interaction?: Interaction; +} + +// @public (undocumented) +type InteractionGetParams = InteractionGetParamsNonStreaming | InteractionGetParamsStreaming; + +// @public (undocumented) +interface InteractionGetParamsBase { + api_version?: string; + last_event_id?: string; + stream?: boolean; +} + +// @public (undocumented) +interface InteractionGetParamsNonStreaming extends InteractionGetParamsBase { + stream?: false; +} + +// @public (undocumented) +interface InteractionGetParamsStreaming extends InteractionGetParamsBase { + stream: true; +} + +declare namespace Interactions { + export { + BaseInteractions, + Interactions_2 as Interactions, + AllowedTools, + Annotation, + AudioContent, + AudioMimeType, + CodeExecutionCallArguments, + CodeExecutionCallContent, + CodeExecutionResultContent, + ContentDelta, + ContentStart, + ContentStop, + DeepResearchAgentConfig, + DocumentContent, + DynamicAgentConfig, + ErrorEvent_2 as ErrorEvent, + FileSearchResultContent, + Function_2 as Function, + FunctionCallContent, + FunctionResultContent, + GenerationConfig_2 as GenerationConfig, + GoogleSearchCallArguments, + GoogleSearchCallContent, + GoogleSearchResult, + GoogleSearchResultContent, + ImageContent, + ImageMimeType, + Interaction, + InteractionEvent, + InteractionSSEEvent, + InteractionStatusUpdate, + MCPServerToolCallContent, + MCPServerToolResultContent, + Model_2 as Model, + SpeechConfig_2 as SpeechConfig, + TextContent, + ThinkingLevel_2 as ThinkingLevel, + ThoughtContent, + Tool_2 as Tool, + ToolChoice, + ToolChoiceConfig, + ToolChoiceType, + Turn, + URLContextCallArguments, + URLContextCallContent, + URLContextResult, + URLContextResultContent, + Usage, + VideoContent, + VideoMimeType, + InteractionDeleteResponse, + InteractionCreateParams, + BaseCreateModelInteractionParams, + BaseCreateAgentInteractionParams, + CreateModelInteractionParamsNonStreaming, + CreateModelInteractionParamsStreaming, + CreateAgentInteractionParamsNonStreaming, + CreateAgentInteractionParamsStreaming, + InteractionDeleteParams, + InteractionCancelParams, + InteractionGetParams, + InteractionGetParamsBase, + InteractionGetParamsNonStreaming, + InteractionGetParamsStreaming + } +} + +// @public (undocumented) +class Interactions_2 extends BaseInteractions { +} + +// @public (undocumented) +namespace Interactions_2 { + { type AllowedTools as AllowedTools, type Annotation as Annotation, type AudioContent as AudioContent, type AudioMimeType as AudioMimeType, type CodeExecutionCallArguments as CodeExecutionCallArguments, type CodeExecutionCallContent as CodeExecutionCallContent, type CodeExecutionResultContent as CodeExecutionResultContent, type ContentDelta as ContentDelta, type ContentStart as ContentStart, type ContentStop as ContentStop, type DeepResearchAgentConfig as DeepResearchAgentConfig, type DocumentContent as DocumentContent, type DynamicAgentConfig as DynamicAgentConfig, type ErrorEvent as ErrorEvent, type FileSearchResultContent as FileSearchResultContent, type Function as Function, type FunctionCallContent as FunctionCallContent, type FunctionResultContent as FunctionResultContent, type GenerationConfig as GenerationConfig, type GoogleSearchCallArguments as GoogleSearchCallArguments, type GoogleSearchCallContent as GoogleSearchCallContent, type GoogleSearchResult as GoogleSearchResult, type GoogleSearchResultContent as GoogleSearchResultContent, type ImageContent as ImageContent, type ImageMimeType as ImageMimeType, type Interaction as Interaction, type InteractionEvent as InteractionEvent, type InteractionSSEEvent as InteractionSSEEvent, type InteractionStatusUpdate as InteractionStatusUpdate, type MCPServerToolCallContent as MCPServerToolCallContent, type MCPServerToolResultContent as MCPServerToolResultContent, type Model as Model, type SpeechConfig as SpeechConfig, type TextContent as TextContent, type ThinkingLevel as ThinkingLevel, type ThoughtContent as ThoughtContent, type Tool as Tool, type ToolChoice as ToolChoice, type ToolChoiceConfig as ToolChoiceConfig, type ToolChoiceType as ToolChoiceType, type Turn as Turn, type URLContextCallArguments as URLContextCallArguments, type URLContextCallContent as URLContextCallContent, type URLContextResult as URLContextResult, type URLContextResultContent as URLContextResultContent, type Usage as Usage, type VideoContent as VideoContent, type VideoMimeType as VideoMimeType, type InteractionDeleteResponse as InteractionDeleteResponse, type InteractionCreateParams as InteractionCreateParams, type CreateModelInteractionParamsNonStreaming as CreateModelInteractionParamsNonStreaming, type CreateModelInteractionParamsStreaming as CreateModelInteractionParamsStreaming, type CreateAgentInteractionParamsNonStreaming as CreateAgentInteractionParamsNonStreaming, type CreateAgentInteractionParamsStreaming as CreateAgentInteractionParamsStreaming, type InteractionDeleteParams as InteractionDeleteParams, type InteractionCancelParams as InteractionCancelParams, type InteractionGetParams as InteractionGetParams, type InteractionGetParamsNonStreaming as InteractionGetParamsNonStreaming, type InteractionGetParamsStreaming as InteractionGetParamsStreaming, }; +} + +// @public (undocumented) +type InteractionSSEEvent = InteractionEvent | InteractionStatusUpdate | ContentStart | ContentDelta | ContentStop | ErrorEvent_2; + +// @public (undocumented) +interface InteractionStatusUpdate { + event_id?: string; + // (undocumented) + event_type?: 'interaction.status_update'; + // (undocumented) + interaction_id?: string; + // (undocumented) + status?: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; +} + // @public export interface Interval { endTime?: string; @@ -2451,6 +3108,35 @@ export enum MaskReferenceMode { MASK_MODE_USER_PROVIDED = "MASK_MODE_USER_PROVIDED" } +// @public +interface MCPServerToolCallContent { + arguments: { + [key: string]: unknown; + }; + id: string; + name: string; + server_name: string; + type: 'mcp_server_tool_call'; +} + +// @public +interface MCPServerToolResultContent { + call_id: string; + name?: string; + result: MCPServerToolResultContent.Items | unknown | string; + server_name?: string; + type: 'mcp_server_tool_result'; +} + +// @public (undocumented) +namespace MCPServerToolResultContent { + // (undocumented) + interface Items { + // (undocumented) + items?: Array; + } +} + // @public export function mcpToTool(...args: [...Client[], CallableToolConfig | Client]): CallableTool; @@ -2513,6 +3199,9 @@ export interface Model { version?: string; } +// @public +type Model_2 = 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'gemini-2.5-flash-preview-09-2025' | 'gemini-2.5-flash-lite' | 'gemini-2.5-flash-lite-preview-09-2025' | 'gemini-2.5-flash-preview-native-audio-dialog' | 'gemini-2.5-flash-image-preview' | 'gemini-2.5-pro-preview-tts' | 'gemini-3-pro-preview' | (string & {}); + // @public (undocumented) export class Models extends BaseModule { constructor(apiClient: ApiClient); @@ -3114,6 +3803,13 @@ export interface SpeechConfig { voiceConfig?: VoiceConfig; } +// @public +interface SpeechConfig_2 { + language?: string; + speaker?: string; + voice?: string; +} + // @public (undocumented) export type SpeechConfigUnion = SpeechConfig | string; @@ -3249,6 +3945,13 @@ export interface TestTableItem { skipInApiMode?: string; } +// @public +interface TextContent { + annotations?: Array; + text?: string; + type: 'text'; +} + // @public export interface ThinkingConfig { includeThoughts?: boolean; @@ -3263,6 +3966,16 @@ export enum ThinkingLevel { THINKING_LEVEL_UNSPECIFIED = "THINKING_LEVEL_UNSPECIFIED" } +// @public (undocumented) +type ThinkingLevel_2 = 'low' | 'high'; + +// @public +interface ThoughtContent { + signature?: string; + summary?: Array; + type: 'thought'; +} + // @public (undocumented) export class Tokens extends BaseModule { constructor(apiClient: ApiClient); @@ -3290,6 +4003,59 @@ export interface Tool { urlContext?: UrlContext; } +// @public +type Tool_2 = Function_2 | Tool_2.GoogleSearch | Tool_2.CodeExecution | Tool_2.URLContext | Tool_2.ComputerUse | Tool_2.MCPServer | Tool_2.FileSearch; + +// @public (undocumented) +namespace Tool_2 { + interface CodeExecution { + // (undocumented) + type: 'code_execution'; + } + interface ComputerUse { + environment?: 'browser'; + excludedPredefinedFunctions?: Array; + // (undocumented) + type: 'computer_use'; + } + interface FileSearch { + file_search_store_names?: Array; + metadata_filter?: string; + top_k?: number; + // (undocumented) + type: 'file_search'; + } + interface GoogleSearch { + // (undocumented) + type: 'google_search'; + } + interface MCPServer { + allowed_tools?: Array; + headers?: { + [key: string]: string; + }; + name?: string; + // (undocumented) + type: 'mcp_server'; + url?: string; + } + interface URLContext { + // (undocumented) + type: 'url_context'; + } +} + +// @public +type ToolChoice = ToolChoiceType | ToolChoiceConfig; + +// @public (undocumented) +interface ToolChoiceConfig { + allowed_tools?: AllowedTools; +} + +// @public (undocumented) +type ToolChoiceType = 'auto' | 'any' | 'none' | 'validated'; + // @public export interface ToolCodeExecution { } @@ -3426,6 +4192,12 @@ export interface TuningValidationDataset { vertexDatasetResource?: string; } +// @public (undocumented) +interface Turn { + content?: string | Array; + role?: string; +} + // @public export enum TurnCompleteReason { MALFORMED_FUNCTION_CALL = "MALFORMED_FUNCTION_CALL", @@ -3582,11 +4354,38 @@ export class UpscaleImageResponse { export interface UrlContext { } +// @public +interface URLContextCallArguments { + urls?: Array; +} + +// @public +interface URLContextCallContent { + arguments?: URLContextCallArguments; + id?: string; + type: 'url_context_call'; +} + // @public export interface UrlContextMetadata { urlMetadata?: UrlMetadata[]; } +// @public +interface URLContextResult { + status?: 'success' | 'error' | 'paywall' | 'unsafe'; + url?: string; +} + +// @public +interface URLContextResultContent { + call_id?: string; + is_error?: boolean; + result?: Array; + signature?: string; + type: 'url_context_result'; +} + // @public export interface UrlMetadata { retrievedUrl?: string; @@ -3602,6 +4401,40 @@ export enum UrlRetrievalStatus { URL_RETRIEVAL_STATUS_UNSPECIFIED = "URL_RETRIEVAL_STATUS_UNSPECIFIED" } +// @public +interface Usage { + cached_tokens_by_modality?: Array; + input_tokens_by_modality?: Array; + output_tokens_by_modality?: Array; + tool_use_tokens_by_modality?: Array; + total_cached_tokens?: number; + total_input_tokens?: number; + total_output_tokens?: number; + total_reasoning_tokens?: number; + total_tokens?: number; + total_tool_use_tokens?: number; +} + +// @public (undocumented) +namespace Usage { + interface CachedTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface InputTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface OutputTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } + interface ToolUseTokensByModality { + modality?: 'text' | 'image' | 'audio'; + tokens?: number; + } +} + // @public export interface UsageMetadata { cachedContentTokenCount?: number; @@ -3682,6 +4515,17 @@ export enum VideoCompressionQuality { OPTIMIZED = "OPTIMIZED" } +// @public +interface VideoContent { + // (undocumented) + data?: string; + mime_type?: VideoMimeType; + resolution?: 'low' | 'medium' | 'high'; + type: 'video'; + // (undocumented) + uri?: string; +} + // @public export interface VideoGenerationMask { image?: Image_2; @@ -3715,6 +4559,9 @@ export interface VideoMetadata { startOffset?: string; } +// @public +type VideoMimeType = 'video/mp4' | 'video/mpeg' | 'video/mov' | 'video/avi' | 'video/x-flv' | 'video/mpg' | 'video/webm' | 'video/wmv' | 'video/3gpp' | (string & {}); + // @public (undocumented) export interface VoiceActivityDetectionSignal { vadSignalType?: VadSignalType; diff --git a/package-lock.json b/package-lock.json index d0688fbd6..e1505d064 100644 --- a/package-lock.json +++ b/package-lock.json @@ -90,7 +90,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -359,8 +358,7 @@ "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -1482,7 +1480,8 @@ "optional": true, "os": [ "android" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.53.3", @@ -1496,7 +1495,8 @@ "optional": true, "os": [ "android" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.53.3", @@ -1510,7 +1510,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.53.3", @@ -1524,7 +1525,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.53.3", @@ -1538,7 +1540,8 @@ "optional": true, "os": [ "freebsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.53.3", @@ -1552,7 +1555,8 @@ "optional": true, "os": [ "freebsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.53.3", @@ -1566,7 +1570,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.53.3", @@ -1580,7 +1585,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.53.3", @@ -1594,7 +1600,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.53.3", @@ -1608,7 +1615,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.53.3", @@ -1622,7 +1630,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.53.3", @@ -1636,7 +1645,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.53.3", @@ -1650,7 +1660,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.53.3", @@ -1664,7 +1675,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.53.3", @@ -1678,7 +1690,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.53.3", @@ -1692,7 +1705,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.53.3", @@ -1706,7 +1720,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.53.3", @@ -1720,7 +1735,8 @@ "optional": true, "os": [ "openharmony" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.53.3", @@ -1734,7 +1750,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.53.3", @@ -1748,7 +1765,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.53.3", @@ -1762,7 +1780,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.53.3", @@ -1776,7 +1795,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rushstack/node-core-library": { "version": "5.19.1", @@ -1994,7 +2014,6 @@ "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2100,7 +2119,6 @@ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -2324,7 +2342,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2736,7 +2753,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3799,7 +3815,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3856,7 +3871,6 @@ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4241,7 +4255,6 @@ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -8108,7 +8121,6 @@ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -10063,7 +10075,6 @@ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10131,7 +10142,6 @@ "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/types": "8.24.1", @@ -10870,7 +10880,6 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/sdk-samples/interactions_basic.ts b/sdk-samples/interactions_basic.ts new file mode 100644 index 000000000..07f6e1e51 --- /dev/null +++ b/sdk-samples/interactions_basic.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'why is the sky blue?', + }); + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_function_calling_client_state.ts b/sdk-samples/interactions_function_calling_client_state.ts new file mode 100644 index 000000000..74a394ab6 --- /dev/null +++ b/sdk-samples/interactions_function_calling_client_state.ts @@ -0,0 +1,104 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI, Interactions} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + const fcConversationHistory: Interactions.InteractionCreateParams['input'] = [ + { + type: 'text', + text: 'Schedule a meeting for 2025-11-01 at 10 am with Peter and Amir about the Next Gen API.', + }, + ]; + + // 1. Model decides to call the function + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: fcConversationHistory, + tools: [ + { + name: 'schedule_meeting', + description: + 'Schedules a meeting with specified attendees at a given time and date.', + parameters: { + type: 'object', + properties: { + attendees: { + type: 'array', + items: {type: 'string'}, + description: 'List of people attending the meeting.', + }, + date: { + type: 'string', + description: 'Date of the meeting (e.g., 2024-07-29)', + }, + time: { + type: 'string', + description: 'Time of the meeting (e.g., 15:00)', + }, + topic: { + type: 'string', + description: 'The subject or topic of the meeting.', + }, + }, + required: ['attendees', 'date', 'time', 'topic'], + }, + type: 'function', + }, + ], + }); + + // add model response back to history + if (response.outputs) { + fcConversationHistory.push(...response.outputs); + } + + for (const output of response.outputs ?? []) { + if (output.type == 'function_call') { + console.log( + `Function call: ${output.name} with arguments ${output.arguments}`, + ); + + // 2. Execute the function and get a result + // In a real app, you would call your function here. + // const call_result = schedule_meeting(output.arguments); + + // 3. Send the result back to the model + fcConversationHistory.push({ + type: 'function_result', + name: output.name!, + call_id: output.id!, + result: 'Meeting scheduled successfully.', + }); + + const response2 = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: fcConversationHistory, + }); + console.log(`Final response: ${response2}`); + } else { + console.log(`Output: ${output}`); + } + } +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_function_calling_server_state.ts b/sdk-samples/interactions_function_calling_server_state.ts new file mode 100644 index 000000000..9045f0e61 --- /dev/null +++ b/sdk-samples/interactions_function_calling_server_state.ts @@ -0,0 +1,93 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + // 1. Model decides to call the function + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: + 'Schedule a meeting for 2025-11-01 at 10 am with Peter and Amir about the Next Gen API', + tools: [ + { + name: 'schedule_meeting', + description: + 'Schedules a meeting with specified attendees at a given time and date.', + parameters: { + type: 'object', + properties: { + attendees: { + type: 'array', + items: {type: 'string'}, + description: 'List of people attending the meeting.', + }, + date: { + type: 'string', + description: 'Date of the meeting (e.g., 2024-07-29)', + }, + time: { + type: 'string', + description: 'Time of the meeting (e.g., 15:00)', + }, + topic: { + type: 'string', + description: 'The subject or topic of the meeting.', + }, + }, + required: ['attendees', 'date', 'time', 'topic'], + }, + type: 'function', + }, + ], + }); + + for (const output of response.outputs ?? []) { + if (output.type == 'function_call') { + console.log( + `Function call: ${output.name} with arguments ${output.arguments}`, + ); + + // 2. Execute the function and get a result + // In a real app, you would call your function here. + // const call_result = schedule_meeting(output.arguments); + + // 3. Send the result back to the model + const response2 = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: [ + { + type: 'function_result', + name: output.name, + call_id: output.id, + result: 'Meeting scheduled successfully.', + }, + ], + }); + console.log(`Final response: ${response2}`); + } else { + console.log(`Output: ${output}`); + } + } +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_get.ts b/sdk-samples/interactions_get.ts new file mode 100644 index 000000000..401b78060 --- /dev/null +++ b/sdk-samples/interactions_get.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'why is the sky blue?', + }); + console.debug(response); + + const getResponse = await ai.interactions.get(response.id); + console.debug(getResponse); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_input_text_and_audio.ts b/sdk-samples/interactions_multimodal_input_text_and_audio.ts new file mode 100644 index 000000000..69dfa386c --- /dev/null +++ b/sdk-samples/interactions_multimodal_input_text_and_audio.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; +import fs from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + // Load and encode the audio + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const audioPath = path.join(__dirname, '../sample_audio.mp3'); + const audioBuffer = fs.readFileSync(audioPath); + const base64Audio = audioBuffer.toString('base64'); + + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: [ + { + type: 'text', + text: 'What does this audio say?', + }, + { + type: 'audio', + data: base64Audio, + mime_type: 'audio/mpeg', + }, + ], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_input_text_and_audio_with_generate_content.ts b/sdk-samples/interactions_multimodal_input_text_and_audio_with_generate_content.ts new file mode 100644 index 000000000..97eee4a6e --- /dev/null +++ b/sdk-samples/interactions_multimodal_input_text_and_audio_with_generate_content.ts @@ -0,0 +1,82 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; +import fs from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + // Load and encode the audio + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const audioPath = path.join(__dirname, '../sample_audio.mp3'); + const audioBuffer = fs.readFileSync(audioPath); + const base64Audio = audioBuffer.toString('base64'); + + console.log( + '[Interactions] Start interactions multimodal input text and audio', + ); + + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: [ + { + type: 'text', + text: 'What does this audio say?', + }, + { + type: 'audio', + data: base64Audio, + mime_type: 'audio/mpeg', + }, + ], + }); + + console.debug(response); + + console.log('[Generate Content] Start generate content'); + + const generateContentResponse = await ai.models.generateContent({ + model: 'gemini-2.5-flash', + contents: [ + { + role: 'user', + parts: [ + { + text: 'What does this audio say?', + }, + { + inlineData: { + mimeType: 'audio/mpeg', + data: base64Audio, + }, + }, + ], + }, + ], + }); + + console.debug(generateContentResponse.text); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_input_text_and_image.ts b/sdk-samples/interactions_multimodal_input_text_and_image.ts new file mode 100644 index 000000000..d48edd9f0 --- /dev/null +++ b/sdk-samples/interactions_multimodal_input_text_and_image.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; +import fs from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + // Load and encode the image + const imagePath = path.join(__dirname, '../car.png'); + const imageBuffer = fs.readFileSync(imagePath); + const base64Image = imageBuffer.toString('base64'); + + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: [ + { + type: 'text', + text: 'What is this landmark?', + }, + { + type: 'image', + data: base64Image, + mime_type: 'image/png', + }, + ], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_input_text_and_image_with_generate_content.ts b/sdk-samples/interactions_multimodal_input_text_and_image_with_generate_content.ts new file mode 100644 index 000000000..6f0eb88a2 --- /dev/null +++ b/sdk-samples/interactions_multimodal_input_text_and_image_with_generate_content.ts @@ -0,0 +1,81 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; +import fs from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + console.log( + '[Interactions] Start interactions multimodal input text and image', + ); + + // Load and encode the image + const imagePath = path.join(__dirname, '../car.png'); + const imageBuffer = fs.readFileSync(imagePath); + const base64Image = imageBuffer.toString('base64'); + + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: [ + { + type: 'text', + text: 'What is this landmark?', + }, + { + type: 'image', + data: base64Image, + mime_type: 'image/png', + }, + ], + }); + console.debug(response); + + console.log('[Generate Content] Start generate content'); + const generateContentResponse = await ai.models.generateContent({ + model: 'gemini-2.5-flash', + contents: [ + { + role: 'user', + parts: [ + { + text: 'What is this landmark?', + }, + { + inlineData: { + mimeType: 'image/png', + data: base64Image, + }, + }, + ], + }, + ], + }); + + console.debug(generateContentResponse.text); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_response_audio.ts b/sdk-samples/interactions_multimodal_response_audio.ts new file mode 100644 index 000000000..c3b252391 --- /dev/null +++ b/sdk-samples/interactions_multimodal_response_audio.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const interaction = await ai.interactions.create({ + model: 'gemini-2.5-flash-preview-tts', + input: 'Say cheerfully: Have a wonderful day!', + response_modalities: ['audio'], + generation_config: { + speech_config: [{voice: 'achernar', language: 'en'}], + }, + }); + + interaction.outputs?.forEach((output, index) => { + if (output.type === 'audio') { + console.log(`Audio output ${index + 1}:`, output); + } else { + console.log(`Output ${index + 1}: ${output}`); + } + }); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_response_audio_with_generate_content.ts b/sdk-samples/interactions_multimodal_response_audio_with_generate_content.ts new file mode 100644 index 000000000..d61682844 --- /dev/null +++ b/sdk-samples/interactions_multimodal_response_audio_with_generate_content.ts @@ -0,0 +1,71 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + console.log('[Interactions] Start interactions multimodal response audio'); + + const interaction = await ai.interactions.create({ + model: 'gemini-2.5-flash-preview-tts', + input: 'Say cheerfully: Have a wonderful day!', + response_modalities: ['audio'], + generation_config: { + speech_config: [{voice: 'achernar', language: 'en'}], + }, + }); + + interaction.outputs?.forEach((output, index) => { + if (output.type === 'audio') { + console.log(`Audio output ${index + 1}:`, output); + } else { + console.log(`Output ${index + 1}: ${output}`); + } + }); + + console.log('[Generate Content] Start generate content'); + const generateContentResponse = await ai.models.generateContent({ + model: 'gemini-2.5-flash-preview-tts', + config: { + responseModalities: ['AUDIO'], + }, + contents: [ + { + role: 'user', + parts: [ + { + text: 'Say cheerfully: Have a wonderful day!', + }, + ], + }, + ], + }); + console.debug( + 'Generate Content response text: ', + generateContentResponse.text, + ); + console.debug( + 'Generate Content response data: ', + generateContentResponse.data, + ); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_response_image.ts b/sdk-samples/interactions_multimodal_response_image.ts new file mode 100644 index 000000000..a0a14b996 --- /dev/null +++ b/sdk-samples/interactions_multimodal_response_image.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const interaction = await ai.interactions.create({ + model: 'gemini-2.5-flash-image-preview', + response_modalities: ['image'], + input: 'Generate an image of a futuristic cityscape at sunset.', + }); + + interaction.outputs?.forEach((output, index) => { + if (output.type === 'image') { + console.log(`Image output ${index + 1}:`, output); + } else { + console.log(`Output ${index + 1}: ${output}`); + } + }); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_multimodal_response_image_with_generate_content.ts b/sdk-samples/interactions_multimodal_response_image_with_generate_content.ts new file mode 100644 index 000000000..34f46848d --- /dev/null +++ b/sdk-samples/interactions_multimodal_response_image_with_generate_content.ts @@ -0,0 +1,68 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + console.log('[Interactions] Start interactions multimodal response image'); + + const interaction = await ai.interactions.create({ + model: 'gemini-2.5-flash-image-preview', + response_modalities: ['image'], + input: 'Generate an image of a futuristic cityscape at sunset.', + }); + + interaction.outputs?.forEach((output, index) => { + if (output.type === 'image') { + console.log(`Image output ${index + 1}:`, output); + } else { + console.log(`Output ${index + 1}: ${output}`); + } + }); + + console.log('[Generate Content] Start generate content'); + const generateContentResponse = await ai.models.generateContent({ + model: 'gemini-2.5-flash-image', + config: { + responseModalities: ['TEXT', 'IMAGE'], + }, + contents: [ + { + role: 'user', + parts: [ + { + text: 'Generate an image of a futuristic cityscape at sunset.', + }, + ], + }, + ], + }); + console.debug( + 'Generate Content response text: ', + generateContentResponse.text, + ); + console.debug( + 'Generate Content response data: ', + generateContentResponse.data, + ); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_stateful.ts b/sdk-samples/interactions_stateful.ts new file mode 100644 index 000000000..0da7a12a7 --- /dev/null +++ b/sdk-samples/interactions_stateful.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + // Start the conversation + console.log('User: What are the three largest cities in Spain?'); + const response1 = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'What are the three largest cities in Spain?', + }); + console.log('Model: ', response1); + + // Continue the conversation using the previous interaction ID + console.log('\nUser: What is the most famous landmark in the second one?'); + const response2 = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'What is the most famous landmark in the second one?', + }); + console.log('Model: ', response2); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_stateless.ts b/sdk-samples/interactions_stateless.ts new file mode 100644 index 000000000..f02337732 --- /dev/null +++ b/sdk-samples/interactions_stateless.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI, Interactions} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + const conversationHistory: Interactions.InteractionCreateParams['input'] = [ + { + type: 'text', + text: 'What are the three largest cities in Spain?', + }, + ]; + + console.log('User: What are the three largest cities in Spain?'); + const response1 = await ai.interactions.create({ + model: 'gemini-2.5-flash', + store: false, + input: conversationHistory, + }); + console.log('Model: ', response1); + + if (response1.outputs) { + conversationHistory.push(...response1.outputs); + } + conversationHistory.push({ + type: 'text', + text: 'What is the most famous landmark in the second one?', + }); + + console.log('\nUser: What is the most famous landmark in the second one?'); + const response2 = await ai.interactions.create({ + model: 'gemini-2.5-flash', + store: false, + input: conversationHistory, + }); + console.log('Model: ', response2); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_streaming.ts b/sdk-samples/interactions_streaming.ts new file mode 100644 index 000000000..20e4daf26 --- /dev/null +++ b/sdk-samples/interactions_streaming.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const stream = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'Tell me a story', + stream: true, + }); + + for await (const event of stream) { + switch (event.event_type) { + case 'content.delta': + switch (event.delta?.type) { + case 'text': + process.stdout.write(event.delta.text ?? ''); + break; + } + } + } + + process.stdout.write('\n'); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_structured_output_json.ts b/sdk-samples/interactions_structured_output_json.ts new file mode 100644 index 000000000..14ad2fd5a --- /dev/null +++ b/sdk-samples/interactions_structured_output_json.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'Which are the colors of a rainbow', + response_mime_type: 'application/json', + response_format: {type: 'array', description: 'A list of colors'}, + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_code_execution.ts b/sdk-samples/interactions_tool_call_with_code_execution.ts new file mode 100644 index 000000000..c9aaa9915 --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_code_execution.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'What is the sum of the first 100 integers?', + tools: [{type: 'code_execution'}], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_computer_use.ts b/sdk-samples/interactions_tool_call_with_computer_use.ts new file mode 100644 index 000000000..7d72343b6 --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_computer_use.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: + 'Search for highly rated smart fridges with touchscreen, 2 doors, around 25 cu ft, priced below 4000 dollars on Google Shopping. Create a bulleted list of the 3 cheapest options in the format of name, description, price in an easy-to-read layout.', + tools: [{type: 'computer_use', environment: 'browser'}], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_functions.ts b/sdk-samples/interactions_tool_call_with_functions.ts new file mode 100644 index 000000000..7e092e55b --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_functions.ts @@ -0,0 +1,65 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: + 'Schedule a meeting for 10/06/2028 at 10 am with Peter and Amir about the Next Gen API', + tools: [ + { + name: 'schedule_meeting', + description: + 'Schedules a meeting with specified attendees at a given time and date.', + parameters: { + type: 'object', + properties: { + attendees: { + type: 'array', + items: {type: 'string'}, + description: 'List of people attending the meeting.', + }, + date: { + type: 'string', + description: 'Date of the meeting (e.g., 2024-07-29)', + }, + time: { + type: 'string', + description: 'Time of the meeting (e.g., 15:00)', + }, + topic: { + type: 'string', + description: 'The subject or topic of the meeting.', + }, + }, + required: ['attendees', 'date', 'time', 'topic'], + }, + type: 'function', + }, + ], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_google_search.ts b/sdk-samples/interactions_tool_call_with_google_search.ts new file mode 100644 index 000000000..dac412118 --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_google_search.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'Why is the sky blue', + tools: [{type: 'google_search'}], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_mcp_server.ts b/sdk-samples/interactions_tool_call_with_mcp_server.ts new file mode 100644 index 000000000..661175a09 --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_mcp_server.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'What is the temperature today in London?', + system_instruction: + 'Today is 9-23-2025. Any dates before this are in the past, and any dates after this are in the future.', + tools: [ + { + type: 'mcp_server', + name: 'weather_service', + url: 'https://gemini-api-demos.uc.r.appspot.com/mcp', + }, + ], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_mime_type.ts b/sdk-samples/interactions_tool_call_with_mime_type.ts new file mode 100644 index 000000000..0327bd24c --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_mime_type.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + apiVersion: 'v1alpha', + }); + const response = await ai.interactions.create({ + model: 'gemini-3-pro-preview', + input: 'What is the weather in New York?', + tools: [ + { + type: 'function', + name: 'get_weather', + description: 'Get the current weather in a given location', + parameters: { + type: 'object', + properties: { + location: { + type: 'string', + description: 'The city and state, e.g. San Francisco, CA', + }, + }, + required: ['location'], + }, + }, + ], + response_format: { + type: 'object', + properties: { + location: {type: 'string'}, + temperature: {type: 'number'}, + condition: {type: 'string'}, + recommendation: {type: 'string'}, + }, + required: ['location', 'temperature', 'condition', 'recommendation'], + }, + response_mime_type: 'application/json', + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error( + 'This version of the GenAI SDK does not support Vertex AI API for interactions.', + ); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_tool_call_with_url_context.ts b/sdk-samples/interactions_tool_call_with_url_context.ts new file mode 100644 index 000000000..64e819e81 --- /dev/null +++ b/sdk-samples/interactions_tool_call_with_url_context.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: + 'Compare the ingredients and cooking times from the recipes at https://www.foodnetwork.com/recipes/ina-garten/perfect-roast-chicken-recipe-1940592 and https://www.allrecipes.com/recipe/21151/simple-whole-roast-chicken/', + tools: [{type: 'url_context'}], + }); + + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/interactions_with_config.ts b/sdk-samples/interactions_with_config.ts new file mode 100644 index 000000000..951130a04 --- /dev/null +++ b/sdk-samples/interactions_with_config.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import {GoogleGenAI} from '@google/genai'; + +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GOOGLE_GENAI_USE_VERTEXAI = process.env.GOOGLE_GENAI_USE_VERTEXAI; + +async function createInteractionsFromMLDev() { + const ai = new GoogleGenAI({ + apiKey: GEMINI_API_KEY, + }); + + const response = await ai.interactions.create({ + model: 'gemini-2.5-flash', + input: 'Tell me a story', + system_instruction: 'You are a helpful assistant', + }); + console.debug(response); +} + +async function main() { + if (GOOGLE_GENAI_USE_VERTEXAI) { + throw new Error('Interactions API is not yet supported on Vertex'); + } else { + await createInteractionsFromMLDev().catch((e) => + console.error('got error', e), + ); + } +} + +main(); diff --git a/sdk-samples/package-lock.json b/sdk-samples/package-lock.json index 99b833e33..c2113cc12 100644 --- a/sdk-samples/package-lock.json +++ b/sdk-samples/package-lock.json @@ -22,7 +22,7 @@ }, "..": { "name": "@google/genai", - "version": "1.30.0", + "version": "1.32.0", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^10.3.0", @@ -60,8 +60,8 @@ "typescript-eslint": "8.24.1", "undici": "^7.16.0", "undici-types": "^7.16.0", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.4" + "zod": "^3.25.0", + "zod-to-json-schema": "^3.25.0" }, "engines": { "node": ">=20.0.0" diff --git a/src/client.ts b/src/client.ts index 7d33252eb..acb5cb6fa 100644 --- a/src/client.ts +++ b/src/client.ts @@ -16,6 +16,8 @@ import {CrossUploader} from './cross/_cross_uploader.js'; import {CrossWebSocketFactory} from './cross/_cross_websocket.js'; import {Files} from './files.js'; import {FileSearchStores} from './filesearchstores.js'; +import GeminiNextGenAPI from './interactions/index.js'; +import {Interactions as GeminiNextGenInteractions} from './interactions/resources/interactions.js'; import {Live} from './live.js'; import {Models} from './models.js'; import {Operations} from './operations.js'; @@ -136,6 +138,42 @@ export class GoogleGenAI { readonly authTokens: Tokens; readonly tunings: Tunings; readonly fileSearchStores: FileSearchStores; + private _interactions: GeminiNextGenInteractions | undefined; + + get interactions(): GeminiNextGenInteractions { + if (this._interactions !== undefined) { + return this._interactions; + } + + console.warn( + 'GoogleGenAI.interactions: Interactions usage is experimental and may change in future versions.', + ); + + if (this.vertexai) { + throw new Error( + 'This version of the GenAI SDK does not support Vertex AI API for interactions.', + ); + } + + const httpOpts = this.httpOptions; + + // Unsupported Options Warnings + if (httpOpts?.extraBody) { + console.warn( + 'GoogleGenAI.interactions: Client level httpOptions.extraBody is not supported by the interactions client and will be ignored.', + ); + } + + const nextGenClient = new GeminiNextGenAPI({ + baseURL: this.apiClient.getBaseUrl(), + apiKey: this.apiKey, + defaultHeaders: this.apiClient.getDefaultHeaders(), + timeout: httpOpts?.timeout, + }); + this._interactions = nextGenClient.interactions; + + return this._interactions; + } constructor(options: GoogleGenAIOptions) { if (options.apiKey == null) { diff --git a/src/index.ts b/src/index.ts index 6853db510..a82ac4b23 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export * from './chats'; export {GoogleGenAI, GoogleGenAIOptions} from './client'; export * from './errors'; export {Files} from './files'; +export type * as Interactions from './interactions/resources/interactions.js'; export * from './live'; export {mcpToTool} from './mcp/_mcp'; export {Models} from './models'; diff --git a/src/interactions/OWNERS b/src/interactions/OWNERS new file mode 100644 index 000000000..da35fbc61 --- /dev/null +++ b/src/interactions/OWNERS @@ -0,0 +1,4 @@ +file://depot/google3/third_party/javascript/google_genai/OWNERS + +# Interactions API developers +file://depot/google3/labs/language/genai/interactions/OWNERS \ No newline at end of file diff --git a/src/interactions/api-promise.ts b/src/interactions/api-promise.ts new file mode 100644 index 000000000..68ee3bf7e --- /dev/null +++ b/src/interactions/api-promise.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @deprecated Import from ./core/api-promise instead */ +export * from './core/api-promise.js'; diff --git a/src/interactions/client.ts b/src/interactions/client.ts new file mode 100644 index 000000000..dd5f58900 --- /dev/null +++ b/src/interactions/client.ts @@ -0,0 +1,874 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import type { RequestInit, RequestInfo, BodyInit } from './internal/builtin-types.js'; +import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestInit } from './internal/types.js'; +import { uuid4 } from './internal/utils/uuid.js'; +import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values.js'; +import { sleep } from './internal/utils/sleep.js'; +export type { Logger, LogLevel } from './internal/utils/log.js'; +import { castToError, isAbortError } from './internal/errors.js'; +import type { APIResponseProps } from './internal/parse.js'; +import { getPlatformHeaders } from './internal/detect-platform.js'; +import * as Shims from './internal/shims.js'; +import * as Opts from './internal/request-options.js'; +import { VERSION } from './version.js'; +import * as Errors from './core/error.js'; +import * as Uploads from './core/uploads.js'; +import * as API from './resources/index.js'; +import { APIPromise } from './core/api-promise.js'; +import { + AllowedTools, + Annotation, + AudioContent, + AudioMimeType, + CodeExecutionCallArguments, + CodeExecutionCallContent, + CodeExecutionResultContent, + ContentDelta, + ContentStart, + ContentStop, + CreateAgentInteractionParamsNonStreaming, + CreateAgentInteractionParamsStreaming, + CreateModelInteractionParamsNonStreaming, + CreateModelInteractionParamsStreaming, + DeepResearchAgentConfig, + DocumentContent, + DynamicAgentConfig, + ErrorEvent, + FileSearchResultContent, + Function, + FunctionCallContent, + FunctionResultContent, + GenerationConfig, + GoogleSearchCallArguments, + GoogleSearchCallContent, + GoogleSearchResult, + GoogleSearchResultContent, + ImageContent, + ImageMimeType, + Interaction, + InteractionCancelParams, + InteractionCreateParams, + InteractionDeleteParams, + InteractionDeleteResponse, + InteractionEvent, + InteractionGetParams, + InteractionGetParamsNonStreaming, + InteractionGetParamsStreaming, + InteractionSSEEvent, + InteractionStatusUpdate, + Interactions, + MCPServerToolCallContent, + MCPServerToolResultContent, + Model, + SpeechConfig, + TextContent, + ThinkingLevel, + ThoughtContent, + Tool, + ToolChoice, + ToolChoiceConfig, + ToolChoiceType, + Turn, + URLContextCallArguments, + URLContextCallContent, + URLContextResult, + URLContextResultContent, + Usage, + VideoContent, + VideoMimeType, +} from './resources/interactions.js'; +import { type Fetch } from './internal/builtin-types.js'; +import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers.js'; +import { FinalRequestOptions, RequestOptions } from './internal/request-options.js'; +import { readEnv } from './internal/utils/env.js'; +import { + type LogLevel, + type Logger, + formatRequestDetails, + loggerFor, + parseLogLevel, +} from './internal/utils/log.js'; +import { isEmptyObj } from './internal/utils/values.js'; + +export interface ClientOptions { + /** + * Defaults to process.env['GEMINI_API_KEY']. + */ + apiKey?: string | null | undefined; + + apiVersion?: string | undefined; + + /** + * Override the default base URL for the API, e.g., "https://api.example.com/v2/" + * + * Defaults to process.env['GEMINI_NEXT_GEN_API_BASE_URL']. + */ + baseURL?: string | null | undefined; + + /** + * The maximum amount of time (in milliseconds) that the client should wait for a response + * from the server before timing out a single request. + * + * Note that request timeouts are retried by default, so in a worst-case scenario you may wait + * much longer than this timeout before the promise succeeds or fails. + * + * @unit milliseconds + */ + timeout?: number | undefined; + /** + * Additional `RequestInit` options to be passed to `fetch` calls. + * Properties will be overridden by per-request `fetchOptions`. + */ + fetchOptions?: MergedRequestInit | undefined; + + /** + * Specify a custom `fetch` function implementation. + * + * If not provided, we expect that `fetch` is defined globally. + */ + fetch?: Fetch | undefined; + + /** + * The maximum number of times that the client will retry a request in case of a + * temporary failure, like a network error or a 5XX error from the server. + * + * @default 2 + */ + maxRetries?: number | undefined; + + /** + * Default headers to include with every request to the API. + * + * These can be removed in individual requests by explicitly setting the + * header to `null` in request options. + */ + defaultHeaders?: HeadersLike | undefined; + + /** + * Default query parameters to include with every request to the API. + * + * These can be removed in individual requests by explicitly setting the + * param to `undefined` in request options. + */ + defaultQuery?: Record | undefined; + + /** + * Set the log level. + * + * Defaults to process.env['GEMINI_NEXT_GEN_API_LOG'] or 'warn' if it isn't set. + */ + logLevel?: LogLevel | undefined; + + /** + * Set the logger. + * + * Defaults to globalThis.console. + */ + logger?: Logger | undefined; +} + +/** + * Base class for Gemini Next Gen API API clients. + */ +export class BaseGeminiNextGenAPIClient { + apiKey: string | null; + apiVersion: string; + + baseURL: string; + maxRetries: number; + timeout: number; + logger: Logger; + logLevel: LogLevel | undefined; + fetchOptions: MergedRequestInit | undefined; + + private fetch: Fetch; + private encoder: Opts.RequestEncoder; + protected idempotencyHeader?: string; + private _options: ClientOptions; + + /** + * API Client for interfacing with the Gemini Next Gen API API. + * + * @param {string | null | undefined} [opts.apiKey=process.env['GEMINI_API_KEY'] ?? null] + * @param {string | undefined} [opts.apiVersion=v1beta] + * @param {string} [opts.baseURL=process.env['GEMINI_NEXT_GEN_API_BASE_URL'] ?? https://generativelanguage.googleapis.com] - Override the default base URL for the API. + * @param {number} [opts.timeout=1 minute] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out. + * @param {MergedRequestInit} [opts.fetchOptions] - Additional `RequestInit` options to be passed to `fetch` calls. + * @param {Fetch} [opts.fetch] - Specify a custom `fetch` function implementation. + * @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request. + * @param {HeadersLike} opts.defaultHeaders - Default headers to include with every request to the API. + * @param {Record} opts.defaultQuery - Default query parameters to include with every request to the API. + */ + constructor({ + baseURL = readEnv('GEMINI_NEXT_GEN_API_BASE_URL'), + apiKey = readEnv('GEMINI_API_KEY') ?? null, + apiVersion = 'v1beta', + ...opts + }: ClientOptions = {}) { + const options: ClientOptions = { + apiKey, + apiVersion, + ...opts, + baseURL: baseURL || `https://generativelanguage.googleapis.com`, + }; + + this.baseURL = options.baseURL!; + this.timeout = options.timeout ?? BaseGeminiNextGenAPIClient.DEFAULT_TIMEOUT /* 1 minute */; + this.logger = options.logger ?? console; + const defaultLogLevel = 'warn'; + // Set default logLevel early so that we can log a warning in parseLogLevel. + this.logLevel = defaultLogLevel; + this.logLevel = + parseLogLevel(options.logLevel, 'ClientOptions.logLevel', this) ?? + parseLogLevel(readEnv('GEMINI_NEXT_GEN_API_LOG'), "process.env['GEMINI_NEXT_GEN_API_LOG']", this) ?? + defaultLogLevel; + this.fetchOptions = options.fetchOptions; + this.maxRetries = options.maxRetries ?? 2; + this.fetch = options.fetch ?? Shims.getDefaultFetch(); + this.encoder = Opts.FallbackEncoder; + + this._options = options; + + this.apiKey = apiKey; + this.apiVersion = apiVersion; + } + + /** + * Create a new client instance re-using the same options given to the current client with optional overriding. + */ + withOptions(options: Partial): this { + const client = new (this.constructor as any as new (props: ClientOptions) => typeof this)({ + ...this._options, + baseURL: this.baseURL, + maxRetries: this.maxRetries, + timeout: this.timeout, + logger: this.logger, + logLevel: this.logLevel, + fetch: this.fetch, + fetchOptions: this.fetchOptions, + apiKey: this.apiKey, + apiVersion: this.apiVersion, + ...options, + }); + return client; + } + + /** + * Check whether the base URL is set to its default. + */ + private baseURLOverridden() { + return this.baseURL !== 'https://generativelanguage.googleapis.com'; + } + + protected defaultQuery(): Record | undefined { + return this._options.defaultQuery; + } + + protected validateHeaders({ values, nulls }: NullableHeaders) { + if (this.apiKey && values.get('x-goog-api-key')) { + return; + } + if (nulls.has('x-goog-api-key')) { + return; + } + + throw new Error( + 'Could not resolve authentication method. Expected the apiKey to be set. Or for the "x-goog-api-key" headers to be explicitly omitted', + ); + } + + protected async authHeaders(opts: FinalRequestOptions): Promise { + if (this.apiKey == null) { + return undefined; + } + return buildHeaders([{ 'x-goog-api-key': this.apiKey }]); + } + + /** + * Basic re-implementation of `qs.stringify` for primitive types. + */ + protected stringifyQuery(query: Record): string { + return Object.entries(query) + .filter(([_, value]) => typeof value !== 'undefined') + .map(([key, value]) => { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + } + if (value === null) { + return `${encodeURIComponent(key)}=`; + } + throw new Errors.GeminiNextGenAPIClientError( + `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`, + ); + }) + .join('&'); + } + + private getUserAgent(): string { + return `${this.constructor.name}/JS ${VERSION}`; + } + + protected defaultIdempotencyKey(): string { + return `stainless-node-retry-${uuid4()}`; + } + + protected makeStatusError( + status: number, + error: Object, + message: string | undefined, + headers: Headers, + ): Errors.APIError { + return Errors.APIError.generate(status, error, message, headers); + } + + buildURL( + path: string, + query: Record | null | undefined, + defaultBaseURL?: string | undefined, + ): string { + const baseURL = (!this.baseURLOverridden() && defaultBaseURL) || this.baseURL; + const url = + isAbsoluteURL(path) ? + new URL(path) + : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); + + const defaultQuery = this.defaultQuery(); + if (!isEmptyObj(defaultQuery)) { + query = { ...defaultQuery, ...query }; + } + + if (typeof query === 'object' && query && !Array.isArray(query)) { + url.search = this.stringifyQuery(query as Record); + } + + return url.toString(); + } + + /** + * Used as a callback for mutating the given `FinalRequestOptions` object. + */ + protected async prepareOptions(options: FinalRequestOptions): Promise {} + + /** + * Used as a callback for mutating the given `RequestInit` object. + * + * This is useful for cases where you want to add certain headers based off of + * the request properties, e.g. `method` or `url`. + */ + protected async prepareRequest( + request: RequestInit, + { url, options }: { url: string; options: FinalRequestOptions }, + ): Promise {} + + get(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('get', path, opts); + } + + post(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('post', path, opts); + } + + patch(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('patch', path, opts); + } + + put(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('put', path, opts); + } + + delete(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('delete', path, opts); + } + + private methodRequest( + method: HTTPMethod, + path: string, + opts?: PromiseOrValue, + ): APIPromise { + return this.request( + Promise.resolve(opts).then((opts) => { + return { method, path, ...opts }; + }), + ); + } + + request( + options: PromiseOrValue, + remainingRetries: number | null = null, + ): APIPromise { + return new APIPromise(this, this.makeRequest(options, remainingRetries, undefined)); + } + + private async makeRequest( + optionsInput: PromiseOrValue, + retriesRemaining: number | null, + retryOfRequestLogID: string | undefined, + ): Promise { + const options = await optionsInput; + const maxRetries = options.maxRetries ?? this.maxRetries; + if (retriesRemaining == null) { + retriesRemaining = maxRetries; + } + + await this.prepareOptions(options); + + const { req, url, timeout } = await this.buildRequest(options, { + retryCount: maxRetries - retriesRemaining, + }); + + await this.prepareRequest(req, { url, options }); + + /** Not an API request ID, just for correlating local log entries. */ + const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0'); + const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`; + const startTime = Date.now(); + + loggerFor(this).debug( + `[${requestLogID}] sending request`, + formatRequestDetails({ + retryOfRequestLogID, + method: options.method, + url, + options, + headers: req.headers, + }), + ); + + if (options.signal?.aborted) { + throw new Errors.APIUserAbortError(); + } + + const controller = new AbortController(); + const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError); + const headersTime = Date.now(); + + if (response instanceof globalThis.Error) { + const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; + if (options.signal?.aborted) { + throw new Errors.APIUserAbortError(); + } + // detect native connection timeout errors + // deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)" + // undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)" + // others do not provide enough information to distinguish timeouts from other connection errors + const isTimeout = + isAbortError(response) || + /timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : '')); + if (retriesRemaining) { + loggerFor(this).info( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - ${retryMessage}`, + ); + loggerFor(this).debug( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url, + durationMs: headersTime - startTime, + message: response.message, + }), + ); + return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID); + } + loggerFor(this).info( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - error; no more retries left`, + ); + loggerFor(this).debug( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (error; no more retries left)`, + formatRequestDetails({ + retryOfRequestLogID, + url, + durationMs: headersTime - startTime, + message: response.message, + }), + ); + if (isTimeout) { + throw new Errors.APIConnectionTimeoutError(); + } + throw new Errors.APIConnectionError({ cause: response }); + } + + const responseInfo = `[${requestLogID}${retryLogStr}] ${req.method} ${url} ${ + response.ok ? 'succeeded' : 'failed' + } with status ${response.status} in ${headersTime - startTime}ms`; + + if (!response.ok) { + const shouldRetry = await this.shouldRetry(response); + if (retriesRemaining && shouldRetry) { + const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; + + // We don't need the body of this response. + await Shims.CancelReadableStream(response.body); + loggerFor(this).info(`${responseInfo} - ${retryMessage}`); + loggerFor(this).debug( + `[${requestLogID}] response error (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + durationMs: headersTime - startTime, + }), + ); + return this.retryRequest( + options, + retriesRemaining, + retryOfRequestLogID ?? requestLogID, + response.headers, + ); + } + + const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`; + + loggerFor(this).info(`${responseInfo} - ${retryMessage}`); + + const errText = await response.text().catch((err: any) => castToError(err).message); + const errJSON = safeJSON(errText); + const errMessage = errJSON ? undefined : errText; + + loggerFor(this).debug( + `[${requestLogID}] response error (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + message: errMessage, + durationMs: Date.now() - startTime, + }), + ); + // @ts-ignore + const err = this.makeStatusError(response.status, errJSON, errMessage, response.headers); + throw err; + } + + loggerFor(this).info(responseInfo); + loggerFor(this).debug( + `[${requestLogID}] response start`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + durationMs: headersTime - startTime, + }), + ); + + return { response, options, controller, requestLogID, retryOfRequestLogID, startTime }; + } + + async fetchWithTimeout( + url: RequestInfo, + init: RequestInit | undefined, + ms: number, + controller: AbortController, + ): Promise { + const { signal, method, ...options } = init || {}; + if (signal) signal.addEventListener('abort', () => controller.abort()); + + const timeout = setTimeout(() => controller.abort(), ms); + + const isReadableBody = + ((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) || + (typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body); + + const fetchOptions: RequestInit = { + signal: controller.signal as any, + ...(isReadableBody ? { duplex: 'half' } : {}), + method: 'GET', + ...options, + }; + if (method) { + // Custom methods like 'patch' need to be uppercased + // See https://github.com/nodejs/undici/issues/2294 + fetchOptions.method = method.toUpperCase(); + } + + try { + // use undefined this binding; fetch errors if bound to something else in browser/cloudflare + return await this.fetch.call(undefined, url, fetchOptions); + } finally { + clearTimeout(timeout); + } + } + + private async shouldRetry(response: Response): Promise { + // Note this is not a standard header. + const shouldRetryHeader = response.headers.get('x-should-retry'); + + // If the server explicitly says whether or not to retry, obey. + if (shouldRetryHeader === 'true') return true; + if (shouldRetryHeader === 'false') return false; + + // Retry on request timeouts. + if (response.status === 408) return true; + + // Retry on lock timeouts. + if (response.status === 409) return true; + + // Retry on rate limits. + if (response.status === 429) return true; + + // Retry internal errors. + if (response.status >= 500) return true; + + return false; + } + + private async retryRequest( + options: FinalRequestOptions, + retriesRemaining: number, + requestLogID: string, + responseHeaders?: Headers | undefined, + ): Promise { + let timeoutMillis: number | undefined; + + // Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it. + const retryAfterMillisHeader = responseHeaders?.get('retry-after-ms'); + if (retryAfterMillisHeader) { + const timeoutMs = parseFloat(retryAfterMillisHeader); + if (!Number.isNaN(timeoutMs)) { + timeoutMillis = timeoutMs; + } + } + + // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + const retryAfterHeader = responseHeaders?.get('retry-after'); + if (retryAfterHeader && !timeoutMillis) { + const timeoutSeconds = parseFloat(retryAfterHeader); + if (!Number.isNaN(timeoutSeconds)) { + timeoutMillis = timeoutSeconds * 1000; + } else { + timeoutMillis = Date.parse(retryAfterHeader) - Date.now(); + } + } + + // If the API asks us to wait a certain amount of time (and it's a reasonable amount), + // just do what it says, but otherwise calculate a default + if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) { + const maxRetries = options.maxRetries ?? this.maxRetries; + timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries); + } + await sleep(timeoutMillis); + + return this.makeRequest(options, retriesRemaining - 1, requestLogID); + } + + private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number { + const initialRetryDelay = 0.5; + const maxRetryDelay = 8.0; + + const numRetries = maxRetries - retriesRemaining; + + // Apply exponential backoff, but not more than the max. + const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay); + + // Apply some jitter, take up to at most 25 percent of the retry time. + const jitter = 1 - Math.random() * 0.25; + + return sleepSeconds * jitter * 1000; + } + + async buildRequest( + inputOptions: FinalRequestOptions, + { retryCount = 0 }: { retryCount?: number } = {}, + ): Promise<{ req: FinalizedRequestInit; url: string; timeout: number }> { + const options = { ...inputOptions }; + const { method, path, query, defaultBaseURL } = options; + + const url = this.buildURL(path!, query as Record, defaultBaseURL); + if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); + options.timeout = options.timeout ?? this.timeout; + const { bodyHeaders, body } = this.buildBody({ options }); + const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount }); + + const req: FinalizedRequestInit = { + method, + headers: reqHeaders, + ...(options.signal && { signal: options.signal }), + ...((globalThis as any).ReadableStream && + body instanceof (globalThis as any).ReadableStream && { duplex: 'half' }), + ...(body && { body }), + ...((this.fetchOptions as any) ?? {}), + ...((options.fetchOptions as any) ?? {}), + }; + + return { req, url, timeout: options.timeout }; + } + + private async buildHeaders({ + options, + method, + bodyHeaders, + retryCount, + }: { + options: FinalRequestOptions; + method: HTTPMethod; + bodyHeaders: HeadersLike; + retryCount: number; + }): Promise { + let idempotencyHeaders: HeadersLike = {}; + if (this.idempotencyHeader && method !== 'get') { + if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey(); + idempotencyHeaders[this.idempotencyHeader] = options.idempotencyKey; + } + + const headers = buildHeaders([ + idempotencyHeaders, + { + Accept: 'application/json', + 'User-Agent': this.getUserAgent(), + 'X-Stainless-Retry-Count': String(retryCount), + ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}), + ...getPlatformHeaders(), + }, + await this.authHeaders(options), + this._options.defaultHeaders, + bodyHeaders, + options.headers, + ]); + + this.validateHeaders(headers); + + return headers.values; + } + + private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): { + bodyHeaders: HeadersLike; + body: BodyInit | undefined; + } { + if (!body) { + return { bodyHeaders: undefined, body: undefined }; + } + const headers = buildHeaders([rawHeaders]); + if ( + // Pass raw type verbatim + ArrayBuffer.isView(body) || + body instanceof ArrayBuffer || + body instanceof DataView || + (typeof body === 'string' && + // Preserve legacy string encoding behavior for now + headers.values.has('content-type')) || + // `Blob` is superset of `File` + ((globalThis as any).Blob && body instanceof (globalThis as any).Blob) || + // `FormData` -> `multipart/form-data` + body instanceof FormData || + // `URLSearchParams` -> `application/x-www-form-urlencoded` + body instanceof URLSearchParams || + // Send chunked stream (each chunk has own `length`) + ((globalThis as any).ReadableStream && body instanceof (globalThis as any).ReadableStream) + ) { + return { bodyHeaders: undefined, body: body as BodyInit }; + } else if ( + typeof body === 'object' && + (Symbol.asyncIterator in body || + (Symbol.iterator in body && 'next' in body && typeof body.next === 'function')) + ) { + return { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body as AsyncIterable) }; + } else { + return this.encoder({ body, headers }); + } + } + + static DEFAULT_TIMEOUT = 60000; // 1 minute +} + +/** + * API Client for interfacing with the Gemini Next Gen API API. + */ +export class GeminiNextGenAPIClient extends BaseGeminiNextGenAPIClient { + static GeminiNextGenAPIClient = this; + + static GeminiNextGenAPIClientError = Errors.GeminiNextGenAPIClientError; + static APIError = Errors.APIError; + static APIConnectionError = Errors.APIConnectionError; + static APIConnectionTimeoutError = Errors.APIConnectionTimeoutError; + static APIUserAbortError = Errors.APIUserAbortError; + static NotFoundError = Errors.NotFoundError; + static ConflictError = Errors.ConflictError; + static RateLimitError = Errors.RateLimitError; + static BadRequestError = Errors.BadRequestError; + static AuthenticationError = Errors.AuthenticationError; + static InternalServerError = Errors.InternalServerError; + static PermissionDeniedError = Errors.PermissionDeniedError; + static UnprocessableEntityError = Errors.UnprocessableEntityError; + + static toFile = Uploads.toFile; + + interactions: API.Interactions = new API.Interactions(this); +} + +GeminiNextGenAPIClient.Interactions = Interactions; + +export declare namespace GeminiNextGenAPIClient { + export type RequestOptions = Opts.RequestOptions; + + export { + Interactions as Interactions, + type AllowedTools as AllowedTools, + type Annotation as Annotation, + type AudioContent as AudioContent, + type AudioMimeType as AudioMimeType, + type CodeExecutionCallArguments as CodeExecutionCallArguments, + type CodeExecutionCallContent as CodeExecutionCallContent, + type CodeExecutionResultContent as CodeExecutionResultContent, + type ContentDelta as ContentDelta, + type ContentStart as ContentStart, + type ContentStop as ContentStop, + type DeepResearchAgentConfig as DeepResearchAgentConfig, + type DocumentContent as DocumentContent, + type DynamicAgentConfig as DynamicAgentConfig, + type ErrorEvent as ErrorEvent, + type FileSearchResultContent as FileSearchResultContent, + type Function as Function, + type FunctionCallContent as FunctionCallContent, + type FunctionResultContent as FunctionResultContent, + type GenerationConfig as GenerationConfig, + type GoogleSearchCallArguments as GoogleSearchCallArguments, + type GoogleSearchCallContent as GoogleSearchCallContent, + type GoogleSearchResult as GoogleSearchResult, + type GoogleSearchResultContent as GoogleSearchResultContent, + type ImageContent as ImageContent, + type ImageMimeType as ImageMimeType, + type Interaction as Interaction, + type InteractionEvent as InteractionEvent, + type InteractionSSEEvent as InteractionSSEEvent, + type InteractionStatusUpdate as InteractionStatusUpdate, + type MCPServerToolCallContent as MCPServerToolCallContent, + type MCPServerToolResultContent as MCPServerToolResultContent, + type Model as Model, + type SpeechConfig as SpeechConfig, + type TextContent as TextContent, + type ThinkingLevel as ThinkingLevel, + type ThoughtContent as ThoughtContent, + type Tool as Tool, + type ToolChoice as ToolChoice, + type ToolChoiceConfig as ToolChoiceConfig, + type ToolChoiceType as ToolChoiceType, + type Turn as Turn, + type URLContextCallArguments as URLContextCallArguments, + type URLContextCallContent as URLContextCallContent, + type URLContextResult as URLContextResult, + type URLContextResultContent as URLContextResultContent, + type Usage as Usage, + type VideoContent as VideoContent, + type VideoMimeType as VideoMimeType, + type InteractionDeleteResponse as InteractionDeleteResponse, + type InteractionCreateParams as InteractionCreateParams, + type CreateModelInteractionParamsNonStreaming as CreateModelInteractionParamsNonStreaming, + type CreateModelInteractionParamsStreaming as CreateModelInteractionParamsStreaming, + type CreateAgentInteractionParamsNonStreaming as CreateAgentInteractionParamsNonStreaming, + type CreateAgentInteractionParamsStreaming as CreateAgentInteractionParamsStreaming, + type InteractionDeleteParams as InteractionDeleteParams, + type InteractionCancelParams as InteractionCancelParams, + type InteractionGetParams as InteractionGetParams, + type InteractionGetParamsNonStreaming as InteractionGetParamsNonStreaming, + type InteractionGetParamsStreaming as InteractionGetParamsStreaming, + }; +} diff --git a/src/interactions/core/README.md b/src/interactions/core/README.md new file mode 100644 index 000000000..485fce861 --- /dev/null +++ b/src/interactions/core/README.md @@ -0,0 +1,3 @@ +# `core` + +This directory holds public modules implementing non-resource-specific SDK functionality. diff --git a/src/interactions/core/api-promise.ts b/src/interactions/core/api-promise.ts new file mode 100644 index 000000000..2cf0b0487 --- /dev/null +++ b/src/interactions/core/api-promise.ts @@ -0,0 +1,98 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { type BaseGeminiNextGenAPIClient } from '../client.js'; + +import { type PromiseOrValue } from '../internal/types.js'; +import { APIResponseProps, defaultParseResponse } from '../internal/parse.js'; + +/** + * A subclass of `Promise` providing additional helper methods + * for interacting with the SDK. + */ +export class APIPromise extends Promise { + private parsedPromise: Promise | undefined; + private client: BaseGeminiNextGenAPIClient; + + constructor( + client: BaseGeminiNextGenAPIClient, + private responsePromise: Promise, + private parseResponse: ( + client: BaseGeminiNextGenAPIClient, + props: APIResponseProps, + ) => PromiseOrValue = defaultParseResponse, + ) { + super((resolve) => { + // this is maybe a bit weird but this has to be a no-op to not implicitly + // parse the response body; instead .then, .catch, .finally are overridden + // to parse the response + resolve(null as any); + }); + this.client = client; + } + + _thenUnwrap(transform: (data: T, props: APIResponseProps) => U): APIPromise { + return new APIPromise(this.client, this.responsePromise, async (client, props) => + transform(await this.parseResponse(client, props), props), + ); + } + + /** + * Gets the raw `Response` instance instead of parsing the response + * data. + * + * If you want to parse the response body but still get the `Response` + * instance, you can use {@link withResponse()}. + * + * 👋 Getting the wrong TypeScript type for `Response`? + * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]` + * to your `tsconfig.json`. + */ + asResponse(): Promise { + return this.responsePromise.then((p) => p.response); + } + + /** + * Gets the parsed response data and the raw `Response` instance. + * + * If you just want to get the raw `Response` instance without parsing it, + * you can use {@link asResponse()}. + * + * 👋 Getting the wrong TypeScript type for `Response`? + * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]` + * to your `tsconfig.json`. + */ + async withResponse(): Promise<{ data: T; response: Response }> { + const [data, response] = await Promise.all([this.parse(), this.asResponse()]); + return { data, response }; + } + + private parse(): Promise { + if (!this.parsedPromise) { + this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.client, data)); + } + return this.parsedPromise; + } + + override then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null, + ): Promise { + return this.parse().then(onfulfilled, onrejected); + } + + override catch( + onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null, + ): Promise { + return this.parse().catch(onrejected); + } + + override finally(onfinally?: (() => void) | undefined | null): Promise { + return this.parse().finally(onfinally); + } +} diff --git a/src/interactions/core/error.ts b/src/interactions/core/error.ts new file mode 100644 index 000000000..1a163bd80 --- /dev/null +++ b/src/interactions/core/error.ts @@ -0,0 +1,136 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { castToError } from '../internal/errors.js'; + +export class GeminiNextGenAPIClientError extends Error {} + +export class APIError< + TStatus extends number | undefined = number | undefined, + THeaders extends Headers | undefined = Headers | undefined, + TError extends Object | undefined = Object | undefined, +> extends GeminiNextGenAPIClientError { + /** HTTP status for the response that caused the error */ + readonly status: TStatus; + /** HTTP headers for the response that caused the error */ + readonly headers: THeaders; + /** JSON body of the response that caused the error */ + readonly error: TError; + + constructor(status: TStatus, error: TError, message: string | undefined, headers: THeaders) { + super(`${APIError.makeMessage(status, error, message)}`); + this.status = status; + this.headers = headers; + this.error = error; + } + + private static makeMessage(status: number | undefined, error: any, message: string | undefined) { + const msg = + error?.message ? + typeof error.message === 'string' ? + error.message + : JSON.stringify(error.message) + : error ? JSON.stringify(error) + : message; + + if (status && msg) { + return `${status} ${msg}`; + } + if (status) { + return `${status} status code (no body)`; + } + if (msg) { + return msg; + } + return '(no status code or body)'; + } + + static generate( + status: number | undefined, + errorResponse: Object | undefined, + message: string | undefined, + headers: Headers | undefined, + ): APIError { + if (!status || !headers) { + return new APIConnectionError({ message, cause: castToError(errorResponse) }); + } + + const error = errorResponse as Record; + + if (status === 400) { + return new BadRequestError(status, error, message, headers); + } + + if (status === 401) { + return new AuthenticationError(status, error, message, headers); + } + + if (status === 403) { + return new PermissionDeniedError(status, error, message, headers); + } + + if (status === 404) { + return new NotFoundError(status, error, message, headers); + } + + if (status === 409) { + return new ConflictError(status, error, message, headers); + } + + if (status === 422) { + return new UnprocessableEntityError(status, error, message, headers); + } + + if (status === 429) { + return new RateLimitError(status, error, message, headers); + } + + if (status >= 500) { + return new InternalServerError(status, error, message, headers); + } + + return new APIError(status, error, message, headers); + } +} + +export class APIUserAbortError extends APIError { + constructor({ message }: { message?: string } = {}) { + super(undefined, undefined, message || 'Request was aborted.', undefined); + } +} + +export class APIConnectionError extends APIError { + constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) { + super(undefined, undefined, message || 'Connection error.', undefined); + // in some environments the 'cause' property is already declared + // @ts-ignore + if (cause) this.cause = cause; + } +} + +export class APIConnectionTimeoutError extends APIConnectionError { + constructor({ message }: { message?: string } = {}) { + super({ message: message ?? 'Request timed out.' }); + } +} + +export class BadRequestError extends APIError<400, Headers> {} + +export class AuthenticationError extends APIError<401, Headers> {} + +export class PermissionDeniedError extends APIError<403, Headers> {} + +export class NotFoundError extends APIError<404, Headers> {} + +export class ConflictError extends APIError<409, Headers> {} + +export class UnprocessableEntityError extends APIError<422, Headers> {} + +export class RateLimitError extends APIError<429, Headers> {} + +export class InternalServerError extends APIError {} diff --git a/src/interactions/core/resource.ts b/src/interactions/core/resource.ts new file mode 100644 index 000000000..4b3fc77ef --- /dev/null +++ b/src/interactions/core/resource.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { BaseGeminiNextGenAPIClient } from '../client.js'; + +export abstract class APIResource { + /** + * The key path from the client. For example, a resource accessible as `client.resource.subresource` would + * have a property `static override readonly _key = Object.freeze(['resource', 'subresource'] as const);`. + */ + static readonly _key: readonly string[] = []; + protected _client: BaseGeminiNextGenAPIClient; + + constructor(client: BaseGeminiNextGenAPIClient) { + this._client = client; + } +} diff --git a/src/interactions/core/streaming.ts b/src/interactions/core/streaming.ts new file mode 100644 index 000000000..6496fc8fa --- /dev/null +++ b/src/interactions/core/streaming.ts @@ -0,0 +1,334 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { GeminiNextGenAPIClientError } from './error.js'; +import { type ReadableStream } from '../internal/shim-types.js'; +import { makeReadableStream } from '../internal/shims.js'; +import { findDoubleNewlineIndex, LineDecoder } from '../internal/decoders/line.js'; +import { ReadableStreamToAsyncIterable } from '../internal/shims.js'; +import { isAbortError } from '../internal/errors.js'; +import { encodeUTF8 } from '../internal/utils/bytes.js'; +import { loggerFor } from '../internal/utils/log.js'; +import type { BaseGeminiNextGenAPIClient } from '../client.js'; + +type Bytes = string | ArrayBuffer | Uint8Array | null | undefined; + +export type ServerSentEvent = { + event: string | null; + data: string; + raw: string[]; +}; + +export class Stream implements AsyncIterable { + controller: AbortController; + private client: BaseGeminiNextGenAPIClient | undefined; + + constructor( + private iterator: () => AsyncIterator, + controller: AbortController, + client?: BaseGeminiNextGenAPIClient, + ) { + this.controller = controller; + this.client = client; + } + + static fromSSEResponse( + response: Response, + controller: AbortController, + client?: BaseGeminiNextGenAPIClient, + ): Stream { + let consumed = false; + const logger = client ? loggerFor(client) : console; + + async function* iterator(): AsyncIterator { + if (consumed) { + throw new GeminiNextGenAPIClientError( + 'Cannot iterate over a consumed stream, use `.tee()` to split the stream.', + ); + } + consumed = true; + let done = false; + try { + for await (const sse of _iterSSEMessages(response, controller)) { + if (done) continue; + + if (sse.data.startsWith('[DONE]')) { + done = true; + continue; + } else { + try { + // @ts-ignore + yield JSON.parse(sse.data); + } catch (e) { + logger.error(`Could not parse message into JSON:`, sse.data); + logger.error(`From chunk:`, sse.raw); + throw e; + } + } + } + done = true; + } catch (e) { + // If the user calls `stream.controller.abort()`, we should exit without throwing. + if (isAbortError(e)) return; + throw e; + } finally { + // If the user `break`s, abort the ongoing request. + if (!done) controller.abort(); + } + } + + return new Stream(iterator, controller, client); + } + + /** + * Generates a Stream from a newline-separated ReadableStream + * where each item is a JSON value. + */ + static fromReadableStream( + readableStream: ReadableStream, + controller: AbortController, + client?: BaseGeminiNextGenAPIClient, + ): Stream { + let consumed = false; + + async function* iterLines(): AsyncGenerator { + const lineDecoder = new LineDecoder(); + + const iter = ReadableStreamToAsyncIterable(readableStream); + for await (const chunk of iter) { + for (const line of lineDecoder.decode(chunk)) { + yield line; + } + } + + for (const line of lineDecoder.flush()) { + yield line; + } + } + + async function* iterator(): AsyncIterator { + if (consumed) { + throw new GeminiNextGenAPIClientError( + 'Cannot iterate over a consumed stream, use `.tee()` to split the stream.', + ); + } + consumed = true; + let done = false; + try { + for await (const line of iterLines()) { + if (done) continue; + // @ts-ignore + if (line) yield JSON.parse(line); + } + done = true; + } catch (e) { + // If the user calls `stream.controller.abort()`, we should exit without throwing. + if (isAbortError(e)) return; + throw e; + } finally { + // If the user `break`s, abort the ongoing request. + if (!done) controller.abort(); + } + } + + return new Stream(iterator, controller, client); + } + + [Symbol.asyncIterator](): AsyncIterator { + return this.iterator(); + } + + /** + * Splits the stream into two streams which can be + * independently read from at different speeds. + */ + tee(): [Stream, Stream] { + const left: Array>> = []; + const right: Array>> = []; + const iterator = this.iterator(); + + const teeIterator = (queue: Array>>): AsyncIterator => { + return { + next: () => { + if (queue.length === 0) { + const result = iterator.next(); + left.push(result); + right.push(result); + } + return queue.shift()!; + }, + }; + }; + + return [ + new Stream(() => teeIterator(left), this.controller, this.client), + new Stream(() => teeIterator(right), this.controller, this.client), + ]; + } + + /** + * Converts this stream to a newline-separated ReadableStream of + * JSON stringified values in the stream + * which can be turned back into a Stream with `Stream.fromReadableStream()`. + */ + toReadableStream(): ReadableStream { + const self = this; + let iter: AsyncIterator; + + return makeReadableStream({ + async start() { + iter = self[Symbol.asyncIterator](); + }, + async pull(ctrl: any) { + try { + const { value, done } = await iter.next(); + if (done) return ctrl.close(); + + const bytes = encodeUTF8(JSON.stringify(value) + '\n'); + + ctrl.enqueue(bytes); + } catch (err) { + ctrl.error(err); + } + }, + async cancel() { + await iter.return?.(); + }, + }); + } +} + +export async function* _iterSSEMessages( + response: Response, + controller: AbortController, +): AsyncGenerator { + if (!response.body) { + controller.abort(); + if ( + typeof (globalThis as any).navigator !== 'undefined' && + (globalThis as any).navigator.product === 'ReactNative' + ) { + throw new GeminiNextGenAPIClientError( + `The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`, + ); + } + throw new GeminiNextGenAPIClientError(`Attempted to iterate over a response with no body`); + } + + const sseDecoder = new SSEDecoder(); + const lineDecoder = new LineDecoder(); + + const iter = ReadableStreamToAsyncIterable(response.body); + for await (const sseChunk of iterSSEChunks(iter)) { + for (const line of lineDecoder.decode(sseChunk)) { + const sse = sseDecoder.decode(line); + if (sse) yield sse; + } + } + + for (const line of lineDecoder.flush()) { + const sse = sseDecoder.decode(line); + if (sse) yield sse; + } +} + +/** + * Given an async iterable iterator, iterates over it and yields full + * SSE chunks, i.e. yields when a double new-line is encountered. + */ +async function* iterSSEChunks(iterator: AsyncIterableIterator): AsyncGenerator { + let data = new Uint8Array(); + + for await (const chunk of iterator) { + if (chunk == null) { + continue; + } + + const binaryChunk = + chunk instanceof ArrayBuffer ? new Uint8Array(chunk) + : typeof chunk === 'string' ? encodeUTF8(chunk) + : chunk; + + let newData = new Uint8Array(data.length + binaryChunk.length); + newData.set(data); + newData.set(binaryChunk, data.length); + data = newData; + + let patternIndex; + while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) { + yield data.slice(0, patternIndex); + data = data.slice(patternIndex); + } + } + + if (data.length > 0) { + yield data; + } +} + +class SSEDecoder { + private data: string[]; + private event: string | null; + private chunks: string[]; + + constructor() { + this.event = null; + this.data = []; + this.chunks = []; + } + + decode(line: string) { + if (line.endsWith('\r')) { + line = line.substring(0, line.length - 1); + } + + if (!line) { + // empty line and we didn't previously encounter any messages + if (!this.event && !this.data.length) return null; + + const sse: ServerSentEvent = { + event: this.event, + data: this.data.join('\n'), + raw: this.chunks, + }; + + this.event = null; + this.data = []; + this.chunks = []; + + return sse; + } + + this.chunks.push(line); + + if (line.startsWith(':')) { + return null; + } + + let [fieldname, _, value] = partition(line, ':'); + + if (value.startsWith(' ')) { + value = value.substring(1); + } + + if (fieldname === 'event') { + this.event = value; + } else if (fieldname === 'data') { + this.data.push(value); + } + + return null; + } +} + +function partition(str: string, delimiter: string): [string, string, string] { + const index = str.indexOf(delimiter); + if (index !== -1) { + return [str.substring(0, index), delimiter, str.substring(index + delimiter.length)]; + } + + return [str, '', '']; +} diff --git a/src/interactions/core/uploads.ts b/src/interactions/core/uploads.ts new file mode 100644 index 000000000..27a020a47 --- /dev/null +++ b/src/interactions/core/uploads.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export { type Uploadable } from '../internal/uploads.js'; +export { toFile, type ToFileInput, type BlobLikePart } from '../internal/to-file.js'; diff --git a/src/interactions/error.ts b/src/interactions/error.ts new file mode 100644 index 000000000..cfecafa72 --- /dev/null +++ b/src/interactions/error.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @deprecated Import from ./core/error instead */ +export * from './core/error.js'; diff --git a/src/interactions/index.ts b/src/interactions/index.ts new file mode 100644 index 000000000..f09f1f7bd --- /dev/null +++ b/src/interactions/index.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { GeminiNextGenAPIClient as default } from './client.js'; + +export { type Uploadable, type BlobLikePart, toFile } from './core/uploads.js'; +export { APIPromise } from './core/api-promise.js'; +export { BaseGeminiNextGenAPIClient, GeminiNextGenAPIClient, type ClientOptions } from './client.js'; +export { + GeminiNextGenAPIClientError, + APIError, + APIConnectionError, + APIConnectionTimeoutError, + APIUserAbortError, + NotFoundError, + ConflictError, + RateLimitError, + BadRequestError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, +} from './core/error.js'; diff --git a/src/interactions/internal/README.md b/src/interactions/internal/README.md new file mode 100644 index 000000000..3ef5a25ba --- /dev/null +++ b/src/interactions/internal/README.md @@ -0,0 +1,3 @@ +# `internal` + +The modules in this directory are not importable outside this package and will change between releases. diff --git a/src/interactions/internal/builtin-types.ts b/src/interactions/internal/builtin-types.ts new file mode 100644 index 000000000..c73a68858 --- /dev/null +++ b/src/interactions/internal/builtin-types.ts @@ -0,0 +1,99 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise; + +/** + * An alias to the builtin `RequestInit` type so we can + * easily alias it in import statements if there are name clashes. + * + * https://developer.mozilla.org/docs/Web/API/RequestInit + */ +type _RequestInit = RequestInit; + +/** + * An alias to the builtin `Response` type so we can + * easily alias it in import statements if there are name clashes. + * + * https://developer.mozilla.org/docs/Web/API/Response + */ +type _Response = Response; + +/** + * The type for the first argument to `fetch`. + * + * https://developer.mozilla.org/docs/Web/API/Window/fetch#resource + */ +type _RequestInfo = Request | URL | string; + +/** + * The type for constructing `RequestInit` Headers. + * + * https://developer.mozilla.org/docs/Web/API/RequestInit#setting_headers + */ +type _HeadersInit = RequestInit['headers']; + +/** + * The type for constructing `RequestInit` body. + * + * https://developer.mozilla.org/docs/Web/API/RequestInit#body + */ +type _BodyInit = RequestInit['body']; + +/** + * An alias to the builtin `Array` type so we can + * easily alias it in import statements if there are name clashes. + */ +type _Array = Array; + +/** + * An alias to the builtin `Record` type so we can + * easily alias it in import statements if there are name clashes. + */ +type _Record = Record; + +export type { + _Array as Array, + _BodyInit as BodyInit, + _HeadersInit as HeadersInit, + _Record as Record, + _RequestInfo as RequestInfo, + _RequestInit as RequestInit, + _Response as Response, +}; + +/** + * A copy of the builtin `EndingType` type as it isn't fully supported in certain + * environments and attempting to reference the global version will error. + * + * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L27941 + */ +type EndingType = 'native' | 'transparent'; + +/** + * A copy of the builtin `BlobPropertyBag` type as it isn't fully supported in certain + * environments and attempting to reference the global version will error. + * + * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L154 + * https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#options + */ +export interface BlobPropertyBag { + endings?: EndingType; + type?: string; +} + +/** + * A copy of the builtin `FilePropertyBag` type as it isn't fully supported in certain + * environments and attempting to reference the global version will error. + * + * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L503 + * https://developer.mozilla.org/en-US/docs/Web/API/File/File#options + */ +export interface FilePropertyBag extends BlobPropertyBag { + lastModified?: number; +} diff --git a/src/interactions/internal/decoders/line.ts b/src/interactions/internal/decoders/line.ts new file mode 100644 index 000000000..4206b92f0 --- /dev/null +++ b/src/interactions/internal/decoders/line.ts @@ -0,0 +1,141 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes.js'; + +export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined; + +/** + * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally + * reading lines from text. + * + * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258 + */ +export class LineDecoder { + // prettier-ignore + static NEWLINE_CHARS = new Set(['\n', '\r']); + static NEWLINE_REGEXP = /\r\n|[\n\r]/g; + + private buffer: Uint8Array; + private carriageReturnIndex: number | null; + + constructor() { + this.buffer = new Uint8Array(); + this.carriageReturnIndex = null; + } + + decode(chunk: Bytes): string[] { + if (chunk == null) { + return []; + } + + const binaryChunk = + chunk instanceof ArrayBuffer ? new Uint8Array(chunk) + : typeof chunk === 'string' ? encodeUTF8(chunk) + : chunk; + + this.buffer = concatBytes([this.buffer, binaryChunk]); + + const lines: string[] = []; + let patternIndex; + while ((patternIndex = findNewlineIndex(this.buffer, this.carriageReturnIndex)) != null) { + if (patternIndex.carriage && this.carriageReturnIndex == null) { + // skip until we either get a corresponding `\n`, a new `\r` or nothing + this.carriageReturnIndex = patternIndex.index; + continue; + } + + // we got double \r or \rtext\n + if ( + this.carriageReturnIndex != null && + (patternIndex.index !== this.carriageReturnIndex + 1 || patternIndex.carriage) + ) { + lines.push(decodeUTF8(this.buffer.subarray(0, this.carriageReturnIndex - 1))); + this.buffer = this.buffer.subarray(this.carriageReturnIndex); + this.carriageReturnIndex = null; + continue; + } + + const endIndex = + this.carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding; + + const line = decodeUTF8(this.buffer.subarray(0, endIndex)); + lines.push(line); + + this.buffer = this.buffer.subarray(patternIndex.index); + this.carriageReturnIndex = null; + } + + return lines; + } + + flush(): string[] { + if (!this.buffer.length) { + return []; + } + return this.decode('\n'); + } +} + +/** + * This function searches the buffer for the end patterns, (\r or \n) + * and returns an object with the index preceding the matched newline and the + * index after the newline char. `null` is returned if no new line is found. + * + * ```ts + * findNewLineIndex('abc\ndef') -> { preceding: 2, index: 3 } + * ``` + */ +function findNewlineIndex( + buffer: Uint8Array, + startIndex: number | null, +): { preceding: number; index: number; carriage: boolean } | null { + const newline = 0x0a; // \n + const carriage = 0x0d; // \r + + for (let i = startIndex ?? 0; i < buffer.length; i++) { + if (buffer[i] === newline) { + return { preceding: i, index: i + 1, carriage: false }; + } + + if (buffer[i] === carriage) { + return { preceding: i, index: i + 1, carriage: true }; + } + } + + return null; +} + +export function findDoubleNewlineIndex(buffer: Uint8Array): number { + // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n) + // and returns the index right after the first occurrence of any pattern, + // or -1 if none of the patterns are found. + const newline = 0x0a; // \n + const carriage = 0x0d; // \r + + for (let i = 0; i < buffer.length - 1; i++) { + if (buffer[i] === newline && buffer[i + 1] === newline) { + // \n\n + return i + 2; + } + if (buffer[i] === carriage && buffer[i + 1] === carriage) { + // \r\r + return i + 2; + } + if ( + buffer[i] === carriage && + buffer[i + 1] === newline && + i + 3 < buffer.length && + buffer[i + 2] === carriage && + buffer[i + 3] === newline + ) { + // \r\n\r\n + return i + 4; + } + } + + return -1; +} diff --git a/src/interactions/internal/detect-platform.ts b/src/interactions/internal/detect-platform.ts new file mode 100644 index 000000000..3152e85d2 --- /dev/null +++ b/src/interactions/internal/detect-platform.ts @@ -0,0 +1,200 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { VERSION } from '../version.js'; + +export const isRunningInBrowser = () => { + return ( + // @ts-ignore + (typeof window !== 'undefined' && + // @ts-ignore + typeof window.document !== 'undefined' && typeof navigator !== 'undefined') + ); +}; + +type DetectedPlatform = 'deno' | 'node' | 'edge' | 'unknown'; + +/** + * Note this does not detect 'browser'; for that, use getBrowserInfo(). + */ +function getDetectedPlatform(): DetectedPlatform { + if (typeof Deno !== 'undefined' && Deno.build != null) { + return 'deno'; + } + if (typeof EdgeRuntime !== 'undefined') { + return 'edge'; + } + if ( + Object.prototype.toString.call( + typeof (globalThis as any).process !== 'undefined' ? (globalThis as any).process : 0, + ) === '[object process]' + ) { + return 'node'; + } + return 'unknown'; +} + +declare const Deno: any; +declare const EdgeRuntime: any; +type Arch = 'x32' | 'x64' | 'arm' | 'arm64' | `other:${string}` | 'unknown'; +type PlatformName = + | 'MacOS' + | 'Linux' + | 'Windows' + | 'FreeBSD' + | 'OpenBSD' + | 'iOS' + | 'Android' + | `Other:${string}` + | 'Unknown'; +type Browser = 'ie' | 'edge' | 'chrome' | 'firefox' | 'safari'; +type PlatformProperties = { + 'X-Stainless-Lang': 'js'; + 'X-Stainless-Package-Version': string; + 'X-Stainless-OS': PlatformName; + 'X-Stainless-Arch': Arch; + 'X-Stainless-Runtime': 'node' | 'deno' | 'edge' | `browser:${Browser}` | 'unknown'; + 'X-Stainless-Runtime-Version': string; +}; +const getPlatformProperties = (): PlatformProperties => { + const detectedPlatform = getDetectedPlatform(); + if (detectedPlatform === 'deno') { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': normalizePlatform(Deno.build.os), + 'X-Stainless-Arch': normalizeArch(Deno.build.arch), + 'X-Stainless-Runtime': 'deno', + 'X-Stainless-Runtime-Version': + typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown', + }; + } + if (typeof EdgeRuntime !== 'undefined') { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': 'Unknown', + 'X-Stainless-Arch': `other:${EdgeRuntime}`, + 'X-Stainless-Runtime': 'edge', + 'X-Stainless-Runtime-Version': (globalThis as any).process.version, + }; + } + // Check if Node.js + if (detectedPlatform === 'node') { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': normalizePlatform((globalThis as any).process.platform ?? 'unknown'), + 'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch ?? 'unknown'), + 'X-Stainless-Runtime': 'node', + 'X-Stainless-Runtime-Version': (globalThis as any).process.version ?? 'unknown', + }; + } + + const browserInfo = getBrowserInfo(); + if (browserInfo) { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': 'Unknown', + 'X-Stainless-Arch': 'unknown', + 'X-Stainless-Runtime': `browser:${browserInfo.browser}`, + 'X-Stainless-Runtime-Version': browserInfo.version, + }; + } + + // TODO add support for Cloudflare workers, etc. + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': 'Unknown', + 'X-Stainless-Arch': 'unknown', + 'X-Stainless-Runtime': 'unknown', + 'X-Stainless-Runtime-Version': 'unknown', + }; +}; + +type BrowserInfo = { + browser: Browser; + version: string; +}; + +declare const navigator: { userAgent: string } | undefined; + +// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts +function getBrowserInfo(): BrowserInfo | null { + if (typeof navigator === 'undefined' || !navigator) { + return null; + } + + // NOTE: The order matters here! + const browserPatterns = [ + { key: 'edge' as const, pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'ie' as const, pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'ie' as const, pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'chrome' as const, pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'firefox' as const, pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'safari' as const, pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ }, + ]; + + // Find the FIRST matching browser + for (const { key, pattern } of browserPatterns) { + const match = pattern.exec(navigator.userAgent); + if (match) { + const major = match[1] || 0; + const minor = match[2] || 0; + const patch = match[3] || 0; + + return { browser: key, version: `${major}.${minor}.${patch}` }; + } + } + + return null; +} + +const normalizeArch = (arch: string): Arch => { + // Node docs: + // - https://nodejs.org/api/process.html#processarch + // Deno docs: + // - https://doc.deno.land/deno/stable/~/Deno.build + if (arch === 'x32') return 'x32'; + if (arch === 'x86_64' || arch === 'x64') return 'x64'; + if (arch === 'arm') return 'arm'; + if (arch === 'aarch64' || arch === 'arm64') return 'arm64'; + if (arch) return `other:${arch}`; + return 'unknown'; +}; + +const normalizePlatform = (platform: string): PlatformName => { + // Node platforms: + // - https://nodejs.org/api/process.html#processplatform + // Deno platforms: + // - https://doc.deno.land/deno/stable/~/Deno.build + // - https://github.com/denoland/deno/issues/14799 + + platform = platform.toLowerCase(); + + // NOTE: this iOS check is untested and may not work + // Node does not work natively on IOS, there is a fork at + // https://github.com/nodejs-mobile/nodejs-mobile + // however it is unknown at the time of writing how to detect if it is running + if (platform.includes('ios')) return 'iOS'; + if (platform === 'android') return 'Android'; + if (platform === 'darwin') return 'MacOS'; + if (platform === 'win32') return 'Windows'; + if (platform === 'freebsd') return 'FreeBSD'; + if (platform === 'openbsd') return 'OpenBSD'; + if (platform === 'linux') return 'Linux'; + if (platform) return `Other:${platform}`; + return 'Unknown'; +}; + +let _platformHeaders: PlatformProperties; +export const getPlatformHeaders = () => { + return (_platformHeaders ??= getPlatformProperties()); +}; diff --git a/src/interactions/internal/errors.ts b/src/interactions/internal/errors.ts new file mode 100644 index 000000000..6c43ba768 --- /dev/null +++ b/src/interactions/internal/errors.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export function isAbortError(err: unknown) { + return ( + typeof err === 'object' && + err !== null && + // Spec-compliant fetch implementations + (('name' in err && (err as any).name === 'AbortError') || + // Expo fetch + ('message' in err && String((err as any).message).includes('FetchRequestCanceledException'))) + ); +} + +export const castToError = (err: any): Error => { + if (err instanceof Error) return err; + if (typeof err === 'object' && err !== null) { + try { + if (Object.prototype.toString.call(err) === '[object Error]') { + // @ts-ignore - not all envs have native support for cause yet + const error = new Error(err.message, err.cause ? { cause: err.cause } : {}); + if (err.stack) error.stack = err.stack; + // @ts-ignore - not all envs have native support for cause yet + if (err.cause && !error.cause) error.cause = err.cause; + if (err.name) error.name = err.name; + return error; + } + } catch {} + try { + return new Error(JSON.stringify(err)); + } catch {} + } + return new Error(err); +}; diff --git a/src/interactions/internal/headers.ts b/src/interactions/internal/headers.ts new file mode 100644 index 000000000..f3980425b --- /dev/null +++ b/src/interactions/internal/headers.ts @@ -0,0 +1,103 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { isReadonlyArray } from './utils/values.js'; + +type HeaderValue = string | undefined | null; +export type HeadersLike = + | Headers + | readonly HeaderValue[][] + | Record + | undefined + | null + | NullableHeaders; + +const brand_privateNullableHeaders = /* @__PURE__ */ Symbol('brand.privateNullableHeaders'); + +/** + * @internal + * Users can pass explicit nulls to unset default headers. When we parse them + * into a standard headers type we need to preserve that information. + */ +export type NullableHeaders = { + /** Brand check, prevent users from creating a NullableHeaders. */ + [brand_privateNullableHeaders]: true; + /** Parsed headers. */ + values: Headers; + /** Set of lowercase header names explicitly set to null. */ + nulls: Set; +}; + +function* iterateHeaders(headers: HeadersLike): IterableIterator { + if (!headers) return; + + if (brand_privateNullableHeaders in headers) { + const { values, nulls } = headers; + yield* values.entries(); + for (const name of nulls) { + yield [name, null]; + } + return; + } + + let shouldClear = false; + let iter: Iterable; + if (headers instanceof Headers) { + iter = headers.entries(); + } else if (isReadonlyArray(headers)) { + iter = headers; + } else { + shouldClear = true; + iter = Object.entries(headers ?? {}); + } + for (let row of iter) { + const name = row[0]; + if (typeof name !== 'string') throw new TypeError('expected header name to be a string'); + const values = isReadonlyArray(row[1]) ? row[1] : [row[1]]; + let didClear = false; + for (const value of values) { + if (value === undefined) continue; + + // Objects keys always overwrite older headers, they never append. + // Yield a null to clear the header before adding the new values. + if (shouldClear && !didClear) { + didClear = true; + yield [name, null]; + } + yield [name, value]; + } + } +} + +export const buildHeaders = (newHeaders: HeadersLike[]): NullableHeaders => { + const targetHeaders = new Headers(); + const nullHeaders = new Set(); + for (const headers of newHeaders) { + const seenHeaders = new Set(); + for (const [name, value] of iterateHeaders(headers)) { + const lowerName = name.toLowerCase(); + if (!seenHeaders.has(lowerName)) { + targetHeaders.delete(name); + seenHeaders.add(lowerName); + } + if (value === null) { + targetHeaders.delete(name); + nullHeaders.add(lowerName); + } else { + targetHeaders.append(name, value); + nullHeaders.delete(lowerName); + } + } + } + return { [brand_privateNullableHeaders]: true, values: targetHeaders, nulls: nullHeaders }; +}; + +export const isEmptyHeaders = (headers: HeadersLike) => { + for (const _ of iterateHeaders(headers)) return false; + return true; +}; diff --git a/src/interactions/internal/parse.ts b/src/interactions/internal/parse.ts new file mode 100644 index 000000000..5642d1a71 --- /dev/null +++ b/src/interactions/internal/parse.ts @@ -0,0 +1,73 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import type { FinalRequestOptions } from './request-options.js'; +import { Stream } from '../core/streaming.js'; +import { type BaseGeminiNextGenAPIClient } from '../client.js'; +import { formatRequestDetails, loggerFor } from './utils/log.js'; + +export type APIResponseProps = { + response: Response; + options: FinalRequestOptions; + controller: AbortController; + requestLogID: string; + retryOfRequestLogID: string | undefined; + startTime: number; +}; + +export async function defaultParseResponse( + client: BaseGeminiNextGenAPIClient, + props: APIResponseProps, +): Promise { + const { response, requestLogID, retryOfRequestLogID, startTime } = props; + const body = await (async () => { + if (props.options.stream) { + loggerFor(client).debug('response', response.status, response.url, response.headers, response.body); + + // Note: there is an invariant here that isn't represented in the type system + // that if you set `stream: true` the response type must also be `Stream` + + if (props.options.__streamClass) { + return props.options.__streamClass.fromSSEResponse(response, props.controller, client) as any; + } + + return Stream.fromSSEResponse(response, props.controller, client) as any; + } + + // fetch refuses to read the body when the status code is 204. + if (response.status === 204) { + return null as T; + } + + if (props.options.__binaryResponse) { + return response as unknown as T; + } + + const contentType = response.headers.get('content-type'); + const mediaType = contentType?.split(';')[0]?.trim(); + const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json'); + if (isJSON) { + const json = await response.json(); + return json as T; + } + + const text = await response.text(); + return text as unknown as T; + })(); + loggerFor(client).debug( + `[${requestLogID}] response parsed`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + body, + durationMs: Date.now() - startTime, + }), + ); + return body; +} diff --git a/src/interactions/internal/request-options.ts b/src/interactions/internal/request-options.ts new file mode 100644 index 000000000..0416ecd50 --- /dev/null +++ b/src/interactions/internal/request-options.ts @@ -0,0 +1,99 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { NullableHeaders } from './headers.js'; + +import type { BodyInit } from './builtin-types.js'; +import { Stream } from '../core/streaming.js'; +import type { HTTPMethod, MergedRequestInit } from './types.js'; +import { type HeadersLike } from './headers.js'; + +export type FinalRequestOptions = RequestOptions & { method: HTTPMethod; path: string }; + +export type RequestOptions = { + /** + * The HTTP method for the request (e.g., 'get', 'post', 'put', 'delete'). + */ + method?: HTTPMethod; + + /** + * The URL path for the request. + * + * @example "/v1/foo" + */ + path?: string; + + /** + * Query parameters to include in the request URL. + */ + query?: object | undefined | null; + + /** + * The request body. Can be a string, JSON object, FormData, or other supported types. + */ + body?: unknown; + + /** + * HTTP headers to include with the request. Can be a Headers object, plain object, or array of tuples. + */ + headers?: HeadersLike; + + /** + * The maximum number of times that the client will retry a request in case of a + * temporary failure, like a network error or a 5XX error from the server. + * + * @default 2 + */ + maxRetries?: number; + + stream?: boolean | undefined; + + /** + * The maximum amount of time (in milliseconds) that the client should wait for a response + * from the server before timing out a single request. + * + * @unit milliseconds + */ + timeout?: number; + + /** + * Additional `RequestInit` options to be passed to the underlying `fetch` call. + * These options will be merged with the client's default fetch options. + */ + fetchOptions?: MergedRequestInit; + + /** + * An AbortSignal that can be used to cancel the request. + */ + signal?: AbortSignal | undefined | null; + + /** + * A unique key for this request to enable idempotency. + */ + idempotencyKey?: string; + + /** + * Override the default base URL for this specific request. + */ + defaultBaseURL?: string | undefined; + + __binaryResponse?: boolean | undefined; + __streamClass?: typeof Stream; +}; + +export type EncodedContent = { bodyHeaders: HeadersLike; body: BodyInit }; +export type RequestEncoder = (request: { headers: NullableHeaders; body: unknown }) => EncodedContent; + +export const FallbackEncoder: RequestEncoder = ({ headers, body }) => { + return { + bodyHeaders: { + 'content-type': 'application/json', + }, + body: JSON.stringify(body), + }; +}; diff --git a/src/interactions/internal/shim-types.ts b/src/interactions/internal/shim-types.ts new file mode 100644 index 000000000..ecb614bfe --- /dev/null +++ b/src/interactions/internal/shim-types.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * Shims for types that we can't always rely on being available globally. + * + * Note: these only exist at the type-level, there is no corresponding runtime + * version for any of these symbols. + */ + +type NeverToAny = T extends never ? any : T; + +/** @ts-ignore */ +type _DOMReadableStream = globalThis.ReadableStream; + +/** @ts-ignore */ +type _NodeReadableStream = import('stream/web').ReadableStream; + +type _ConditionalNodeReadableStream = + typeof globalThis extends { ReadableStream: any } ? never : _NodeReadableStream; + +type _ReadableStream = NeverToAny< + | ([0] extends [1 & _DOMReadableStream] ? never : _DOMReadableStream) + | ([0] extends [1 & _ConditionalNodeReadableStream] ? never : _ConditionalNodeReadableStream) +>; + +export type { _ReadableStream as ReadableStream }; diff --git a/src/interactions/internal/shims.ts b/src/interactions/internal/shims.ts new file mode 100644 index 000000000..4f305833f --- /dev/null +++ b/src/interactions/internal/shims.ts @@ -0,0 +1,113 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * This module provides internal shims and utility functions for environments where certain Node.js or global types may not be available. + * + * These are used to ensure we can provide a consistent behaviour between different JavaScript environments and good error + * messages in cases where an environment isn't fully supported. + */ + +import type { Fetch } from './builtin-types.js'; +import type { ReadableStream } from './shim-types.js'; + +export function getDefaultFetch(): Fetch { + if (typeof fetch !== 'undefined') { + return fetch as any; + } + + throw new Error( + '`fetch` is not defined as a global; Either pass `fetch` to the client, `new GeminiNextGenAPIClient({ fetch })` or polyfill the global, `globalThis.fetch = fetch`', + ); +} + +type ReadableStreamArgs = ConstructorParameters; + +export function makeReadableStream(...args: ReadableStreamArgs): ReadableStream { + const ReadableStream = (globalThis as any).ReadableStream; + if (typeof ReadableStream === 'undefined') { + // Note: All of the platforms / runtimes we officially support already define + // `ReadableStream` as a global, so this should only ever be hit on unsupported runtimes. + throw new Error( + '`ReadableStream` is not defined as a global; You will need to polyfill it, `globalThis.ReadableStream = ReadableStream`', + ); + } + + return new ReadableStream(...args); +} + +export function ReadableStreamFrom(iterable: Iterable | AsyncIterable): ReadableStream { + let iter: AsyncIterator | Iterator = + Symbol.asyncIterator in iterable ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator](); + + return makeReadableStream({ + start() {}, + async pull(controller: any) { + const { done, value } = await iter.next(); + if (done) { + controller.close(); + } else { + controller.enqueue(value); + } + }, + async cancel() { + await iter.return?.(); + }, + }); +} + +/** + * Most browsers don't yet have async iterable support for ReadableStream, + * and Node has a very different way of reading bytes from its "ReadableStream". + * + * This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490 + */ +export function ReadableStreamToAsyncIterable(stream: any): AsyncIterableIterator { + if (stream[Symbol.asyncIterator]) return stream; + + const reader = stream.getReader(); + return { + async next() { + try { + const result = await reader.read(); + if (result?.done) reader.releaseLock(); // release lock when stream becomes closed + return result; + } catch (e) { + reader.releaseLock(); // release lock when stream becomes errored + throw e; + } + }, + async return() { + const cancelPromise = reader.cancel(); + reader.releaseLock(); + await cancelPromise; + return { done: true, value: undefined }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; +} + +/** + * Cancels a ReadableStream we don't need to consume. + * See https://undici.nodejs.org/#/?id=garbage-collection + */ +export async function CancelReadableStream(stream: any): Promise { + if (stream === null || typeof stream !== 'object') return; + + if (stream[Symbol.asyncIterator]) { + await stream[Symbol.asyncIterator]().return?.(); + return; + } + + const reader = stream.getReader(); + const cancelPromise = reader.cancel(); + reader.releaseLock(); + await cancelPromise; +} diff --git a/src/interactions/internal/to-file.ts b/src/interactions/internal/to-file.ts new file mode 100644 index 000000000..3218f6a17 --- /dev/null +++ b/src/interactions/internal/to-file.ts @@ -0,0 +1,160 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BlobPart, getName, makeFile, isAsyncIterable } from './uploads.js'; +import type { FilePropertyBag } from './builtin-types.js'; +import { checkFileSupport } from './uploads.js'; + +export type BlobLikePart = string | ArrayBuffer | ArrayBufferView | BlobLike | DataView; + +/** + * Intended to match DOM Blob, node-fetch Blob, node:buffer Blob, etc. + * Don't add arrayBuffer here, node-fetch doesn't have it + */ +export interface BlobLike { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + readonly size: number; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + readonly type: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number): BlobLike; +} + +/** + * This check adds the arrayBuffer() method type because it is available and used at runtime + */ +const isBlobLike = (value: any): value is BlobLike & { arrayBuffer(): Promise } => + value != null && + typeof value === 'object' && + typeof value.size === 'number' && + typeof value.type === 'string' && + typeof value.text === 'function' && + typeof value.slice === 'function' && + typeof value.arrayBuffer === 'function'; + +/** + * Intended to match DOM File, node:buffer File, undici File, etc. + */ +interface FileLike extends BlobLike { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + readonly lastModified: number; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + readonly name?: string | undefined; +} + +/** + * This check adds the arrayBuffer() method type because it is available and used at runtime + */ +const isFileLike = (value: any): value is FileLike & { arrayBuffer(): Promise } => + value != null && + typeof value === 'object' && + typeof value.name === 'string' && + typeof value.lastModified === 'number' && + isBlobLike(value); + +/** + * Intended to match DOM Response, node-fetch Response, undici Response, etc. + */ +export interface ResponseLike { + url: string; + blob(): Promise; +} + +const isResponseLike = (value: any): value is ResponseLike => + value != null && + typeof value === 'object' && + typeof value.url === 'string' && + typeof value.blob === 'function'; + +export type ToFileInput = + | FileLike + | ResponseLike + | Exclude + | AsyncIterable; + +/** + * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats + * @param value the raw content of the file. Can be an {@link Uploadable}, BlobLikePart, or AsyncIterable of BlobLikeParts + * @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible + * @param {Object=} options additional properties + * @param {string=} options.type the MIME type of the content + * @param {number=} options.lastModified the last modified timestamp + * @returns a {@link File} with the given properties + */ +export async function toFile( + value: ToFileInput | PromiseLike, + name?: string | null | undefined, + options?: FilePropertyBag | undefined, +): Promise { + checkFileSupport(); + + // If it's a promise, resolve it. + value = await value; + + // If we've been given a `File` we don't need to do anything + if (isFileLike(value)) { + if (value instanceof File) { + return value; + } + return makeFile([await value.arrayBuffer()], value.name); + } + + if (isResponseLike(value)) { + const blob = await value.blob(); + name ||= new URL(value.url).pathname.split(/[\\/]/).pop(); + + return makeFile(await getBytes(blob), name, options); + } + + const parts = await getBytes(value); + + name ||= getName(value); + + if (!options?.type) { + const type = parts.find((part) => typeof part === 'object' && 'type' in part && part.type); + if (typeof type === 'string') { + options = { ...options, type }; + } + } + + return makeFile(parts, name, options); +} + +async function getBytes(value: BlobLikePart | AsyncIterable): Promise> { + let parts: Array = []; + if ( + typeof value === 'string' || + ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc. + value instanceof ArrayBuffer + ) { + parts.push(value); + } else if (isBlobLike(value)) { + parts.push(value instanceof Blob ? value : await value.arrayBuffer()); + } else if ( + isAsyncIterable(value) // includes Readable, ReadableStream, etc. + ) { + for await (const chunk of value) { + parts.push(...(await getBytes(chunk as BlobLikePart))); // TODO, consider validating? + } + } else { + const constructor = value?.constructor?.name; + throw new Error( + `Unexpected data type: ${typeof value}${ + constructor ? `; constructor: ${constructor}` : '' + }${propsForError(value)}`, + ); + } + + return parts; +} + +function propsForError(value: unknown): string { + if (typeof value !== 'object' || value === null) return ''; + const props = Object.getOwnPropertyNames(value); + return `; props: [${props.map((p) => `"${p}"`).join(', ')}]`; +} diff --git a/src/interactions/internal/types.ts b/src/interactions/internal/types.ts new file mode 100644 index 000000000..11eff9b67 --- /dev/null +++ b/src/interactions/internal/types.ts @@ -0,0 +1,101 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export type PromiseOrValue = T | Promise; +export type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'; + +export type KeysEnum = { [P in keyof Required]: true }; + +export type FinalizedRequestInit = RequestInit & { headers: Headers }; + +type NotAny = [0] extends [1 & T] ? never : T; + +/** + * Some environments overload the global fetch function, and Parameters only gets the last signature. + */ +type OverloadedParameters = + T extends ( + { + (...args: infer A): unknown; + (...args: infer B): unknown; + (...args: infer C): unknown; + (...args: infer D): unknown; + } + ) ? + A | B | C | D + : T extends ( + { + (...args: infer A): unknown; + (...args: infer B): unknown; + (...args: infer C): unknown; + } + ) ? + A | B | C + : T extends ( + { + (...args: infer A): unknown; + (...args: infer B): unknown; + } + ) ? + A | B + : T extends (...args: infer A) => unknown ? A + : never; + +/* eslint-disable */ +/** + * These imports attempt to get types from a parent package's dependencies. + * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which + * would cause typescript to show types not present at runtime. To avoid this, we import + * directly from parent node_modules folders. + * + * We need to check multiple levels because we don't know what directory structure we'll be in. + * For example, pnpm generates directories like this: + * ``` + * node_modules + * ├── .pnpm + * │ └── pkg@1.0.0 + * │ └── node_modules + * │ └── pkg + * │ └── internal + * │ └── types.d.ts + * ├── pkg -> .pnpm/pkg@1.0.0/node_modules/pkg + * └── undici + * ``` + * + * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition + */ +/** @ts-ignore For users with \@types/node */ +type UndiciTypesRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users with undici */ +type UndiciRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users with \@types/bun */ +type BunRequestInit = globalThis.FetchRequestInit; +/** @ts-ignore For users with node-fetch@2 */ +type NodeFetch2RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ +type NodeFetch3RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users who use Deno */ +type FetchRequestInit = NonNullable[1]>; +/* eslint-enable */ + +type RequestInits = + | NotAny + | NotAny + | NotAny + | NotAny + | NotAny + | NotAny + | NotAny; + +/** + * This type contains `RequestInit` options that may be available on the current runtime, + * including per-platform extensions like `dispatcher`, `agent`, `client`, etc. + */ +export type MergedRequestInit = RequestInits & + /** We don't include these in the types as they'll be overridden for every request. */ + Partial>; diff --git a/src/interactions/internal/uploads.ts b/src/interactions/internal/uploads.ts new file mode 100644 index 000000000..da1be78a7 --- /dev/null +++ b/src/interactions/internal/uploads.ts @@ -0,0 +1,191 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { type RequestOptions } from './request-options.js'; +import type { FilePropertyBag, Fetch } from './builtin-types.js'; +import type { BaseGeminiNextGenAPIClient } from '../client.js'; +import { ReadableStreamFrom } from './shims.js'; + +export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob | DataView; +type FsReadStream = AsyncIterable & { path: string | { toString(): string } }; + +// https://github.com/oven-sh/bun/issues/5980 +interface BunFile extends Blob { + readonly name?: string | undefined; +} + +export const checkFileSupport = () => { + if (typeof File === 'undefined') { + const { process } = globalThis as any; + const isOldNode = + typeof process?.versions?.node === 'string' && parseInt(process.versions.node.split('.')) < 20; + throw new Error( + '`File` is not defined as a global, which is required for file uploads.' + + (isOldNode ? + " Update to Node 20 LTS or newer, or set `globalThis.File` to `import('node:buffer').File`." + : ''), + ); + } +}; + +/** + * Typically, this is a native "File" class. + * + * We provide the {@link toFile} utility to convert a variety of objects + * into the File class. + * + * For convenience, you can also pass a fetch Response, or in Node, + * the result of fs.createReadStream(). + */ +export type Uploadable = File | Response | FsReadStream | BunFile; + +/** + * Construct a `File` instance. This is used to ensure a helpful error is thrown + * for environments that don't define a global `File` yet. + */ +export function makeFile( + fileBits: BlobPart[], + fileName: string | undefined, + options?: FilePropertyBag, +): File { + checkFileSupport(); + return new File(fileBits as any, fileName ?? 'unknown_file', options); +} + +export function getName(value: any): string | undefined { + return (( + (typeof value === 'object' && + value !== null && + (('name' in value && value.name && String(value.name)) || + ('url' in value && value.url && String(value.url)) || + ('filename' in value && value.filename && String(value.filename)) || + ('path' in value && value.path && String(value.path)))) || + '' + ) + .split(/[\\/]/) + .pop() || undefined); +} + +export const isAsyncIterable = (value: any): value is AsyncIterable => + value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function'; + +/** + * Returns a multipart/form-data request if any part of the given request body contains a File / Blob value. + * Otherwise returns the request as is. + */ +export const maybeMultipartFormRequestOptions = async ( + opts: RequestOptions, + fetch: BaseGeminiNextGenAPIClient | Fetch, +): Promise => { + if (!hasUploadableValue(opts.body)) return opts; + + return { ...opts, body: await createForm(opts.body, fetch) }; +}; + +type MultipartFormRequestOptions = Omit & { body: unknown }; + +export const multipartFormRequestOptions = async ( + opts: MultipartFormRequestOptions, + fetch: BaseGeminiNextGenAPIClient | Fetch, +): Promise => { + return { ...opts, body: await createForm(opts.body, fetch) }; +}; + +const supportsFormDataMap = /* @__PURE__ */ new WeakMap>(); + +/** + * node-fetch doesn't support the global FormData object in recent node versions. Instead of sending + * properly-encoded form data, it just stringifies the object, resulting in a request body of "[object FormData]". + * This function detects if the fetch function provided supports the global FormData object to avoid + * confusing error messages later on. + */ +function supportsFormData(fetchObject: BaseGeminiNextGenAPIClient | Fetch): Promise { + const fetch: Fetch = typeof fetchObject === 'function' ? fetchObject : (fetchObject as any).fetch; + const cached = supportsFormDataMap.get(fetch); + if (cached) return cached; + const promise = (async () => { + try { + const FetchResponse = ( + 'Response' in fetch ? + fetch.Response + : (await fetch('data:,')).constructor) as typeof Response; + const data = new FormData(); + if (data.toString() === (await new FetchResponse(data).text())) { + return false; + } + return true; + } catch { + // avoid false negatives + return true; + } + })(); + supportsFormDataMap.set(fetch, promise); + return promise; +} + +export const createForm = async >( + body: T | undefined, + fetch: BaseGeminiNextGenAPIClient | Fetch, +): Promise => { + if (!(await supportsFormData(fetch))) { + throw new TypeError( + 'The provided fetch function does not support file uploads with the current global FormData class.', + ); + } + const form = new FormData(); + await Promise.all(Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value))); + return form; +}; + +// We check for Blob not File because Bun.File doesn't inherit from File, +// but they both inherit from Blob and have a `name` property at runtime. +const isNamedBlob = (value: unknown): value is Blob => value instanceof Blob && 'name' in value; + +const isUploadable = (value: unknown) => + typeof value === 'object' && + value !== null && + (value instanceof Response || isAsyncIterable(value) || isNamedBlob(value)); + +const hasUploadableValue = (value: unknown): boolean => { + if (isUploadable(value)) return true; + if (Array.isArray(value)) return value.some(hasUploadableValue); + if (value && typeof value === 'object') { + for (const k in value) { + if (hasUploadableValue((value as any)[k])) return true; + } + } + return false; +}; + +const addFormValue = async (form: FormData, key: string, value: unknown): Promise => { + if (value === undefined) return; + if (value == null) { + throw new TypeError( + `Received null for "${key}"; to pass null in FormData, you must use the string 'null'`, + ); + } + + // TODO: make nested formats configurable + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + form.append(key, String(value)); + } else if (value instanceof Response) { + form.append(key, makeFile([await value.blob()], getName(value))); + } else if (isAsyncIterable(value)) { + form.append(key, makeFile([await new Response(ReadableStreamFrom(value)).blob()], getName(value))); + } else if (isNamedBlob(value)) { + form.append(key, value, getName(value)); + } else if (Array.isArray(value)) { + await Promise.all(value.map((entry) => addFormValue(form, key + '[]', entry))); + } else if (typeof value === 'object') { + await Promise.all( + Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop)), + ); + } else { + throw new TypeError( + `Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`, + ); + } +}; diff --git a/src/interactions/internal/utils.ts b/src/interactions/internal/utils.ts new file mode 100644 index 000000000..3d6642c48 --- /dev/null +++ b/src/interactions/internal/utils.ts @@ -0,0 +1,14 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './utils/values.js'; +export * from './utils/base64.js'; +export * from './utils/env.js'; +export * from './utils/log.js'; +export * from './utils/uuid.js'; +export * from './utils/sleep.js'; diff --git a/src/interactions/internal/utils/base64.ts b/src/interactions/internal/utils/base64.ts new file mode 100644 index 000000000..414c4a5c2 --- /dev/null +++ b/src/interactions/internal/utils/base64.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { GeminiNextGenAPIClientError } from '../../core/error.js'; +import { encodeUTF8 } from './bytes.js'; + +export const toBase64 = (data: string | Uint8Array | null | undefined): string => { + if (!data) return ''; + + if (typeof (globalThis as any).Buffer !== 'undefined') { + return (globalThis as any).Buffer.from(data).toString('base64'); + } + + if (typeof data === 'string') { + data = encodeUTF8(data); + } + + if (typeof btoa !== 'undefined') { + return btoa(String.fromCharCode.apply(null, data as any)); + } + + throw new GeminiNextGenAPIClientError( + 'Cannot generate base64 string; Expected `Buffer` or `btoa` to be defined', + ); +}; + +export const fromBase64 = (str: string): Uint8Array => { + if (typeof (globalThis as any).Buffer !== 'undefined') { + const buf = (globalThis as any).Buffer.from(str, 'base64'); + return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); + } + + if (typeof atob !== 'undefined') { + const bstr = atob(str); + const buf = new Uint8Array(bstr.length); + for (let i = 0; i < bstr.length; i++) { + buf[i] = bstr.charCodeAt(i); + } + return buf; + } + + throw new GeminiNextGenAPIClientError( + 'Cannot decode base64 string; Expected `Buffer` or `atob` to be defined', + ); +}; diff --git a/src/interactions/internal/utils/bytes.ts b/src/interactions/internal/utils/bytes.ts new file mode 100644 index 000000000..687c969d5 --- /dev/null +++ b/src/interactions/internal/utils/bytes.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export function concatBytes(buffers: Uint8Array[]): Uint8Array { + let length = 0; + for (const buffer of buffers) { + length += buffer.length; + } + const output = new Uint8Array(length); + let index = 0; + for (const buffer of buffers) { + output.set(buffer, index); + index += buffer.length; + } + + return output; +} + +let encodeUTF8_: (str: string) => Uint8Array; +export function encodeUTF8(str: string) { + let encoder; + return ( + encodeUTF8_ ?? + ((encoder = new (globalThis as any).TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder))) + )(str); +} + +let decodeUTF8_: (bytes: Uint8Array) => string; +export function decodeUTF8(bytes: Uint8Array) { + let decoder; + return ( + decodeUTF8_ ?? + ((decoder = new (globalThis as any).TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder))) + )(bytes); +} diff --git a/src/interactions/internal/utils/env.ts b/src/interactions/internal/utils/env.ts new file mode 100644 index 000000000..0f2351e6a --- /dev/null +++ b/src/interactions/internal/utils/env.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * Read an environment variable. + * + * Trims beginning and trailing whitespace. + * + * Will return undefined if the environment variable doesn't exist or cannot be accessed. + */ +export const readEnv = (env: string): string | undefined => { + if (typeof (globalThis as any).process !== 'undefined') { + return (globalThis as any).process.env?.[env]?.trim() ?? undefined; + } + if (typeof (globalThis as any).Deno !== 'undefined') { + return (globalThis as any).Deno.env?.get?.(env)?.trim(); + } + return undefined; +}; diff --git a/src/interactions/internal/utils/log.ts b/src/interactions/internal/utils/log.ts new file mode 100644 index 000000000..3b8ca2d75 --- /dev/null +++ b/src/interactions/internal/utils/log.ts @@ -0,0 +1,133 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { hasOwn } from './values.js'; +import { type BaseGeminiNextGenAPIClient } from '../../client.js'; +import { RequestOptions } from '../request-options.js'; + +type LogFn = (message: string, ...rest: unknown[]) => void; +export type Logger = { + error: LogFn; + warn: LogFn; + info: LogFn; + debug: LogFn; +}; +export type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug'; + +const levelNumbers = { + off: 0, + error: 200, + warn: 300, + info: 400, + debug: 500, +}; + +export const parseLogLevel = ( + maybeLevel: string | undefined, + sourceName: string, + client: BaseGeminiNextGenAPIClient, +): LogLevel | undefined => { + if (!maybeLevel) { + return undefined; + } + if (hasOwn(levelNumbers, maybeLevel)) { + return maybeLevel; + } + loggerFor(client).warn( + `${sourceName} was set to ${JSON.stringify(maybeLevel)}, expected one of ${JSON.stringify( + Object.keys(levelNumbers), + )}`, + ); + return undefined; +}; + +function noop() {} + +function makeLogFn(fnLevel: keyof Logger, logger: Logger | undefined, logLevel: LogLevel) { + if (!logger || levelNumbers[fnLevel] > levelNumbers[logLevel]) { + return noop; + } else { + // Don't wrap logger functions, we want the stacktrace intact! + return logger[fnLevel].bind(logger); + } +} + +const noopLogger = { + error: noop, + warn: noop, + info: noop, + debug: noop, +}; + +let cachedLoggers = /* @__PURE__ */ new WeakMap(); + +export function loggerFor(client: BaseGeminiNextGenAPIClient): Logger { + const logger = client.logger; + const logLevel = client.logLevel ?? 'off'; + if (!logger) { + return noopLogger; + } + + const cachedLogger = cachedLoggers.get(logger); + if (cachedLogger && cachedLogger[0] === logLevel) { + return cachedLogger[1]; + } + + const levelLogger = { + error: makeLogFn('error', logger, logLevel), + warn: makeLogFn('warn', logger, logLevel), + info: makeLogFn('info', logger, logLevel), + debug: makeLogFn('debug', logger, logLevel), + }; + + cachedLoggers.set(logger, [logLevel, levelLogger]); + + return levelLogger; +} + +export const formatRequestDetails = (details: { + options?: RequestOptions | undefined; + headers?: Headers | Record | undefined; + retryOfRequestLogID?: string | undefined; + retryOf?: string | undefined; + url?: string | undefined; + status?: number | undefined; + method?: string | undefined; + durationMs?: number | undefined; + message?: unknown; + body?: unknown; +}) => { + if (details.options) { + details.options = { ...details.options }; + delete details.options['headers']; // redundant + leaks internals + } + if (details.headers) { + details.headers = Object.fromEntries( + (details.headers instanceof Headers ? [...details.headers] : Object.entries(details.headers)).map( + ([name, value]) => [ + name, + ( + name.toLowerCase() === 'x-goog-api-key' || + name.toLowerCase() === 'authorization' || + name.toLowerCase() === 'cookie' || + name.toLowerCase() === 'set-cookie' + ) ? + '***' + : value, + ], + ), + ); + } + if ('retryOfRequestLogID' in details) { + if (details.retryOfRequestLogID) { + details.retryOf = details.retryOfRequestLogID; + } + delete details.retryOfRequestLogID; + } + return details; +}; diff --git a/src/interactions/internal/utils/path.ts b/src/interactions/internal/utils/path.ts new file mode 100644 index 000000000..b1ffb9b50 --- /dev/null +++ b/src/interactions/internal/utils/path.ts @@ -0,0 +1,94 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { GeminiNextGenAPIClientError } from '../../core/error.js'; + +/** + * Percent-encode everything that isn't safe to have in a path without encoding safe chars. + * + * Taken from https://datatracker.ietf.org/doc/html/rfc3986#section-3.3: + * > unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * > sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + * > pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ +export function encodeURIPath(str: string) { + return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent); +} + +const EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null)); + +export const createPathTagFunction = (pathEncoder = encodeURIPath) => + (function path(statics: readonly string[], ...params: readonly unknown[]): string { + // If there are no params, no processing is needed. + if (statics.length === 1) return statics[0]!; + + let postPath = false; + const invalidSegments = []; + const path = statics.reduce((previousValue, currentValue, index) => { + if (/[?#]/.test(currentValue)) { + postPath = true; + } + const value = params[index]; + let encoded = (postPath ? encodeURIComponent : pathEncoder)('' + value); + if ( + index !== params.length && + (value == null || + (typeof value === 'object' && + // handle values from other realms + value.toString === + Object.getPrototypeOf(Object.getPrototypeOf((value as any).hasOwnProperty ?? EMPTY) ?? EMPTY) + ?.toString)) + ) { + encoded = value + ''; + invalidSegments.push({ + start: previousValue.length + currentValue.length, + length: encoded.length, + error: `Value of type ${Object.prototype.toString + .call(value) + .slice(8, -1)} is not a valid path parameter`, + }); + } + return previousValue + currentValue + (index === params.length ? '' : encoded); + }, ''); + + const pathOnly = path.split(/[?#]/, 1)[0]!; + const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi; + let match; + + // Find all invalid segments + while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) { + invalidSegments.push({ + start: match.index, + length: match[0].length, + error: `Value "${match[0]}" can\'t be safely passed as a path parameter`, + }); + } + + invalidSegments.sort((a, b) => a.start - b.start); + + if (invalidSegments.length > 0) { + let lastEnd = 0; + const underline = invalidSegments.reduce((acc, segment) => { + const spaces = ' '.repeat(segment.start - lastEnd); + const arrows = '^'.repeat(segment.length); + lastEnd = segment.start + segment.length; + return acc + spaces + arrows; + }, ''); + + throw new GeminiNextGenAPIClientError( + `Path parameters result in path with invalid segments:\n${invalidSegments + .map((e) => e.error) + .join('\n')}\n${path}\n${underline}`, + ); + } + + return path; + }); + +/** + * URI-encodes path params and ensures no unsafe /./ or /../ path segments are introduced. + */ +export const path = /* @__PURE__ */ createPathTagFunction(encodeURIPath); diff --git a/src/interactions/internal/utils/sleep.ts b/src/interactions/internal/utils/sleep.ts new file mode 100644 index 000000000..589f84f01 --- /dev/null +++ b/src/interactions/internal/utils/sleep.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/interactions/internal/utils/uuid.ts b/src/interactions/internal/utils/uuid.ts new file mode 100644 index 000000000..35dc42121 --- /dev/null +++ b/src/interactions/internal/utils/uuid.ts @@ -0,0 +1,25 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * https://stackoverflow.com/a/2117523 + */ +let uuid4Internal = function (): string { + const { crypto } = globalThis as any; + if (crypto?.randomUUID) { + uuid4Internal = crypto.randomUUID.bind(crypto); + return crypto.randomUUID(); + } + const u8 = new Uint8Array(1); + const randomByte = crypto ? () => crypto.getRandomValues(u8)[0]! : () => (Math.random() * 0xff) & 0xff; + return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => + (+c ^ (randomByte() & (15 >> (+c / 4)))).toString(16), + ); +}; + +export const uuid4 = (): string => uuid4Internal(); diff --git a/src/interactions/internal/utils/values.ts b/src/interactions/internal/utils/values.ts new file mode 100644 index 000000000..d203037ba --- /dev/null +++ b/src/interactions/internal/utils/values.ts @@ -0,0 +1,115 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { GeminiNextGenAPIClientError } from '../../core/error.js'; + +// https://url.spec.whatwg.org/#url-scheme-string +const startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i; + +export const isAbsoluteURL = (url: string): boolean => { + return startsWithSchemeRegexp.test(url); +}; + +let isArrayInternal = (val: unknown): val is unknown[] => ( + (isArrayInternal = Array.isArray), isArrayInternal(val) +); +export const isArray = isArrayInternal; +let isReadonlyArrayInternal = isArray as (val: unknown) => val is readonly unknown[]; +export const isReadonlyArray = isReadonlyArrayInternal; + +/** Returns an object if the given value isn't an object, otherwise returns as-is */ +export function maybeObj(x: unknown): object { + if (typeof x !== 'object') { + return {}; + } + + return x ?? {}; +} + +// https://stackoverflow.com/a/34491287 +export function isEmptyObj(obj: Object | null | undefined): boolean { + if (!obj) return true; + for (const _k in obj) return false; + return true; +} + +// https://eslint.org/docs/latest/rules/no-prototype-builtins +export function hasOwn(obj: T, key: PropertyKey): key is keyof T { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +export function isObj(obj: unknown): obj is Record { + return obj != null && typeof obj === 'object' && !Array.isArray(obj); +} + +export const ensurePresent = (value: T | null | undefined): T => { + if (value == null) { + throw new GeminiNextGenAPIClientError(`Expected a value to be given but received ${value} instead.`); + } + + return value; +}; + +export const validatePositiveInteger = (name: string, n: unknown): number => { + if (typeof n !== 'number' || !Number.isInteger(n)) { + throw new GeminiNextGenAPIClientError(`${name} must be an integer`); + } + if (n < 0) { + throw new GeminiNextGenAPIClientError(`${name} must be a positive integer`); + } + return n; +}; + +export const coerceInteger = (value: unknown): number => { + if (typeof value === 'number') return Math.round(value); + if (typeof value === 'string') return parseInt(value, 10); + + throw new GeminiNextGenAPIClientError(`Could not coerce ${value} (type: ${typeof value}) into a number`); +}; + +export const coerceFloat = (value: unknown): number => { + if (typeof value === 'number') return value; + if (typeof value === 'string') return parseFloat(value); + + throw new GeminiNextGenAPIClientError(`Could not coerce ${value} (type: ${typeof value}) into a number`); +}; + +export const coerceBoolean = (value: unknown): boolean => { + if (typeof value === 'boolean') return value; + if (typeof value === 'string') return value === 'true'; + return Boolean(value); +}; + +export const maybeCoerceInteger = (value: unknown): number | undefined => { + if (value == null) { + return undefined; + } + return coerceInteger(value); +}; + +export const maybeCoerceFloat = (value: unknown): number | undefined => { + if (value == null) { + return undefined; + } + return coerceFloat(value); +}; + +export const maybeCoerceBoolean = (value: unknown): boolean | undefined => { + if (value == null) { + return undefined; + } + return coerceBoolean(value); +}; + +export const safeJSON = (text: string) => { + try { + return JSON.parse(text); + } catch (err) { + return undefined; + } +}; diff --git a/src/interactions/resource.ts b/src/interactions/resource.ts new file mode 100644 index 000000000..d359b048e --- /dev/null +++ b/src/interactions/resource.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @deprecated Import from ./core/resource instead */ +export * from './core/resource.js'; diff --git a/src/interactions/resources.ts b/src/interactions/resources.ts new file mode 100644 index 000000000..c0019cc54 --- /dev/null +++ b/src/interactions/resources.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './resources/index.js'; diff --git a/src/interactions/resources/index.ts b/src/interactions/resources/index.ts new file mode 100644 index 000000000..91771cc2a --- /dev/null +++ b/src/interactions/resources/index.ts @@ -0,0 +1,71 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + Interactions, + BaseInteractions, + type AllowedTools, + type Annotation, + type AudioContent, + type AudioMimeType, + type CodeExecutionCallArguments, + type CodeExecutionCallContent, + type CodeExecutionResultContent, + type ContentDelta, + type ContentStart, + type ContentStop, + type DeepResearchAgentConfig, + type DocumentContent, + type DynamicAgentConfig, + type ErrorEvent, + type FileSearchResultContent, + type Function, + type FunctionCallContent, + type FunctionResultContent, + type GenerationConfig, + type GoogleSearchCallArguments, + type GoogleSearchCallContent, + type GoogleSearchResult, + type GoogleSearchResultContent, + type ImageContent, + type ImageMimeType, + type Interaction, + type InteractionEvent, + type InteractionSSEEvent, + type InteractionStatusUpdate, + type MCPServerToolCallContent, + type MCPServerToolResultContent, + type Model, + type SpeechConfig, + type TextContent, + type ThinkingLevel, + type ThoughtContent, + type Tool, + type ToolChoice, + type ToolChoiceConfig, + type ToolChoiceType, + type Turn, + type URLContextCallArguments, + type URLContextCallContent, + type URLContextResult, + type URLContextResultContent, + type Usage, + type VideoContent, + type VideoMimeType, + type InteractionDeleteResponse, + type InteractionCreateParams, + type CreateModelInteractionParamsNonStreaming, + type CreateModelInteractionParamsStreaming, + type CreateAgentInteractionParamsNonStreaming, + type CreateAgentInteractionParamsStreaming, + type InteractionDeleteParams, + type InteractionCancelParams, + type InteractionGetParams, + type InteractionGetParamsNonStreaming, + type InteractionGetParamsStreaming, +} from './interactions.js'; diff --git a/src/interactions/resources/interactions.ts b/src/interactions/resources/interactions.ts new file mode 100644 index 000000000..bfa14b7c6 --- /dev/null +++ b/src/interactions/resources/interactions.ts @@ -0,0 +1,2046 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource.js'; +import * as Errors from '../core/error.js'; + +import { APIPromise } from '../core/api-promise.js'; +import { Stream } from '../core/streaming.js'; +import { RequestOptions } from '../internal/request-options.js'; +import { path } from '../internal/utils/path.js'; + +export class BaseInteractions extends APIResource { + static override readonly _key: readonly ['interactions'] = Object.freeze(['interactions'] as const); + + /** + * Creates a new interaction. + */ + create(params: CreateModelInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + create( + params: CreateModelInteractionParamsStreaming, + options?: RequestOptions, + ): APIPromise>; + create(params: CreateAgentInteractionParamsNonStreaming, options?: RequestOptions): APIPromise; + create( + params: CreateAgentInteractionParamsStreaming, + options?: RequestOptions, + ): APIPromise>; + create( + params: BaseCreateModelInteractionParams | BaseCreateAgentInteractionParams, + options?: RequestOptions, + ): APIPromise | Interaction>; + create( + params: InteractionCreateParams, + options?: RequestOptions, + ): APIPromise | APIPromise> { + const { api_version = this._client.apiVersion, ...body } = params; + if ('model' in body && 'agent_config' in body) { + throw new Errors.GeminiNextGenAPIClientError( + `Invalid request: specified \`model\` and \`agent_config\`. If specifying \`model\`, use \`generation_config\`.`, + ); + } + if ('agent' in body && 'generation_config' in body) { + throw new Errors.GeminiNextGenAPIClientError( + `Invalid request: specified \`agent\` and \`generation_config\`. If specifying \`agent\`, use \`agent_config\`.`, + ); + } + return this._client.post(path`/${api_version}/interactions`, { + body, + ...options, + stream: params.stream ?? false, + }) as APIPromise | APIPromise>; + } + + /** + * Deletes the interaction by id. + */ + delete( + id: string, + params: InteractionDeleteParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + const { api_version = this._client.apiVersion } = params ?? {}; + return this._client.delete(path`/${api_version}/interactions/${id}`, options); + } + + /** + * Cancels an interaction based on its `Interaction.id`. + */ + cancel( + id: string, + params: InteractionCancelParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + const { api_version = this._client.apiVersion } = params ?? {}; + return this._client.post(path`/${api_version}/interactions/${id}/cancel`, options); + } + + /** + * Retrieves the full details of a single interaction based on its `Interaction.id`. + */ + get( + id: string, + params?: InteractionGetParamsNonStreaming, + options?: RequestOptions, + ): APIPromise; + get( + id: string, + params: InteractionGetParamsStreaming, + options?: RequestOptions, + ): APIPromise>; + get( + id: string, + params?: InteractionGetParamsBase | undefined, + options?: RequestOptions, + ): APIPromise | Interaction>; + get( + id: string, + params: InteractionGetParams | undefined = {}, + options?: RequestOptions, + ): APIPromise | APIPromise> { + const { api_version = this._client.apiVersion, ...query } = params ?? {}; + return this._client.get(path`/${api_version}/interactions/${id}`, { + query, + ...options, + stream: params?.stream ?? false, + }) as APIPromise | APIPromise>; + } +} +export class Interactions extends BaseInteractions {} + +/** + * The configuration for allowed tools. + */ +export interface AllowedTools { + /** + * The mode of the tool choice. + */ + mode?: ToolChoiceType; + + /** + * The names of the allowed tools. + */ + tools?: Array; +} + +/** + * Citation information for model-generated content. + */ +export interface Annotation { + /** + * End of the attributed segment, exclusive. + */ + end_index?: number; + + /** + * Source attributed for a portion of the text. Could be a URL, title, or + * other identifier. + */ + source?: string; + + /** + * Start of segment of the response that is attributed to this source. + * + * Index indicates the start of the segment, measured in bytes. + */ + start_index?: number; +} + +/** + * An audio content block. + */ +export interface AudioContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'audio'; + + data?: string; + + /** + * The mime type of the audio. + */ + mime_type?: AudioMimeType; + + uri?: string; +} + +/** + * The mime type of the audio. + */ +export type AudioMimeType = + | 'audio/wav' + | 'audio/mp3' + | 'audio/aiff' + | 'audio/aac' + | 'audio/ogg' + | 'audio/flac' + | (string & {}); + +/** + * The arguments to pass to the code execution. + */ +export interface CodeExecutionCallArguments { + /** + * The code to be executed. + */ + code?: string; + + /** + * Programming language of the `code`. + */ + language?: 'python'; +} + +/** + * Code execution content. + */ +export interface CodeExecutionCallContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'code_execution_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + /** + * The arguments to pass to the code execution. + */ + arguments?: CodeExecutionCallArguments; +} + +/** + * Code execution result content. + */ +export interface CodeExecutionResultContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'code_execution_result'; + + /** + * ID to match the ID from the code execution call block. + */ + call_id?: string; + + /** + * Whether the code execution resulted in an error. + */ + is_error?: boolean; + + /** + * The output of the code execution. + */ + result?: string; + + /** + * A signature hash for backend validation. + */ + signature?: string; +} + +export interface ContentDelta { + delta?: + | ContentDelta.TextDelta + | ContentDelta.ImageDelta + | ContentDelta.AudioDelta + | ContentDelta.DocumentDelta + | ContentDelta.VideoDelta + | ContentDelta.ThoughtSummaryDelta + | ContentDelta.ThoughtSignatureDelta + | ContentDelta.FunctionCallDelta + | ContentDelta.FunctionResultDelta + | ContentDelta.CodeExecutionCallDelta + | ContentDelta.CodeExecutionResultDelta + | ContentDelta.URLContextCallDelta + | ContentDelta.URLContextResultDelta + | ContentDelta.GoogleSearchCallDelta + | ContentDelta.GoogleSearchResultDelta + | ContentDelta.MCPServerToolCallDelta + | ContentDelta.MCPServerToolResultDelta + | ContentDelta.FileSearchResultDelta; + + /** + * The event_id token to be used to resume the interaction stream, from + * this event. + */ + event_id?: string; + + event_type?: 'content.delta'; + + index?: number; +} + +export namespace ContentDelta { + export interface TextDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'text'; + + /** + * Citation information for model-generated content. + */ + annotations?: Array; + + text?: string; + } + + export interface ImageDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'image'; + + data?: string; + + /** + * The mime type of the image. + */ + mime_type?: ImageMimeType; + + /** + * The resolution of the media. + */ + resolution?: 'low' | 'medium' | 'high'; + + uri?: string; + } + + export interface AudioDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'audio'; + + data?: string; + + /** + * The mime type of the audio. + */ + mime_type?: AudioMimeType; + + uri?: string; + } + + export interface DocumentDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'document'; + + data?: string; + + mime_type?: string; + + uri?: string; + } + + export interface VideoDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'video'; + + data?: string; + + /** + * The mime type of the video. + */ + mime_type?: VideoMimeType; + + /** + * The resolution of the media. + */ + resolution?: 'low' | 'medium' | 'high'; + + uri?: string; + } + + export interface ThoughtSummaryDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'thought_summary'; + + /** + * A text content block. + */ + content?: TextContent | ImageContent; + } + + export interface ThoughtSignatureDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'thought_signature'; + + /** + * Signature to match the backend source to be part of the generation. + */ + signature?: string; + } + + export interface FunctionCallDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'function_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + arguments?: { [key: string]: unknown }; + + name?: string; + } + + export interface FunctionResultDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'function_result'; + + /** + * ID to match the ID from the function call block. + */ + call_id?: string; + + is_error?: boolean; + + name?: string; + + /** + * Tool call result delta. + */ + result?: FunctionResultDelta.Items | string; + } + + export namespace FunctionResultDelta { + export interface Items { + items?: Array; + } + } + + export interface CodeExecutionCallDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'code_execution_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + /** + * The arguments to pass to the code execution. + */ + arguments?: CodeExecutionCallArguments; + } + + export interface CodeExecutionResultDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'code_execution_result'; + + /** + * ID to match the ID from the function call block. + */ + call_id?: string; + + is_error?: boolean; + + result?: string; + + signature?: string; + } + + export interface URLContextCallDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'url_context_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + /** + * The arguments to pass to the URL context. + */ + arguments?: URLContextCallArguments; + } + + export interface URLContextResultDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'url_context_result'; + + /** + * ID to match the ID from the function call block. + */ + call_id?: string; + + is_error?: boolean; + + result?: Array; + + signature?: string; + } + + export interface GoogleSearchCallDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'google_search_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + /** + * The arguments to pass to Google Search. + */ + arguments?: GoogleSearchCallArguments; + } + + export interface GoogleSearchResultDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'google_search_result'; + + /** + * ID to match the ID from the function call block. + */ + call_id?: string; + + is_error?: boolean; + + result?: Array; + + signature?: string; + } + + export interface MCPServerToolCallDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'mcp_server_tool_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + arguments?: { [key: string]: unknown }; + + name?: string; + + server_name?: string; + } + + export interface MCPServerToolResultDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'mcp_server_tool_result'; + + /** + * ID to match the ID from the function call block. + */ + call_id?: string; + + name?: string; + + /** + * Tool call result delta. + */ + result?: MCPServerToolResultDelta.Items | string; + + server_name?: string; + } + + export namespace MCPServerToolResultDelta { + export interface Items { + items?: Array; + } + } + + export interface FileSearchResultDelta { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'file_search_result'; + + result?: Array; + } + + export namespace FileSearchResultDelta { + /** + * The result of the File Search. + */ + export interface Result { + /** + * The name of the file search store. + */ + file_search_store?: string; + + /** + * The text of the search result. + */ + text?: string; + + /** + * The title of the search result. + */ + title?: string; + } + } +} + +export interface ContentStart { + /** + * The content of the response. + */ + content?: + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent; + + /** + * The event_id token to be used to resume the interaction stream, from + * this event. + */ + event_id?: string; + + event_type?: 'content.start'; + + index?: number; +} + +export interface ContentStop { + /** + * The event_id token to be used to resume the interaction stream, from + * this event. + */ + event_id?: string; + + event_type?: 'content.stop'; + + index?: number; +} + +/** + * Configuration for the Deep Research agent. + */ +export interface DeepResearchAgentConfig { + /** + * Whether to include thought summaries in the response. + */ + thinking_summaries?: 'auto' | 'none'; + + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type?: 'deep-research'; +} + +/** + * A document content block. + */ +export interface DocumentContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'document'; + + data?: string; + + mime_type?: string; + + uri?: string; +} + +/** + * Configuration for dynamic agents. + */ +export interface DynamicAgentConfig { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type?: 'dynamic'; + + [k: string]: unknown; +} + +export interface ErrorEvent { + /** + * Error message from an interaction. + */ + error?: ErrorEvent.Error; + + /** + * The event_id token to be used to resume the interaction stream, from + * this event. + */ + event_id?: string; + + event_type?: 'error'; +} + +export namespace ErrorEvent { + /** + * Error message from an interaction. + */ + export interface Error { + /** + * A URI that identifies the error type. + */ + code?: string; + + /** + * A human-readable error message. + */ + message?: string; + } +} + +/** + * File Search result content. + */ +export interface FileSearchResultContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'file_search_result'; + + /** + * The results of the File Search. + */ + result?: Array; +} + +export namespace FileSearchResultContent { + /** + * The result of the File Search. + */ + export interface Result { + /** + * The name of the file search store. + */ + file_search_store?: string; + + /** + * The text of the search result. + */ + text?: string; + + /** + * The title of the search result. + */ + title?: string; + } +} + +/** + * A tool that can be used by the model. + */ +export interface Function { + type: 'function'; + + /** + * A description of the function. + */ + description?: string; + + /** + * The name of the function. + */ + name?: string; + + /** + * The JSON Schema for the function's parameters. + */ + parameters?: unknown; +} + +/** + * A function tool call content block. + */ +export interface FunctionCallContent { + /** + * A unique ID for this specific tool call. + */ + id: string; + + /** + * The arguments to pass to the function. + */ + arguments: { [key: string]: unknown }; + + /** + * The name of the tool to call. + */ + name: string; + + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'function_call'; +} + +/** + * A function tool result content block. + */ +export interface FunctionResultContent { + /** + * ID to match the ID from the function call block. + */ + call_id: string; + + /** + * The result of the tool call. + */ + result: FunctionResultContent.Items | unknown | string; + + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'function_result'; + + /** + * Whether the tool call resulted in an error. + */ + is_error?: boolean; + + /** + * The name of the tool that was called. + */ + name?: string; +} + +export namespace FunctionResultContent { + export interface Items { + items?: Array; + } +} + +/** + * Configuration parameters for model interactions. + */ +export interface GenerationConfig { + /** + * The maximum number of tokens to include in the response. + */ + max_output_tokens?: number; + + /** + * Seed used in decoding for reproducibility. + */ + seed?: number; + + /** + * Configuration for speech interaction. + */ + speech_config?: Array; + + /** + * A list of character sequences that will stop output interaction. + */ + stop_sequences?: Array; + + /** + * Controls the randomness of the output. + */ + temperature?: number; + + /** + * The level of thought tokens that the model should generate. + */ + thinking_level?: ThinkingLevel; + + /** + * Whether to include thought summaries in the response. + */ + thinking_summaries?: 'auto' | 'none'; + + /** + * The tool choice for the interaction. + */ + tool_choice?: ToolChoice; + + /** + * The maximum cumulative probability of tokens to consider when sampling. + */ + top_p?: number; +} + +/** + * The arguments to pass to Google Search. + */ +export interface GoogleSearchCallArguments { + /** + * Web search queries for the following-up web search. + */ + queries?: Array; +} + +/** + * Google Search content. + */ +export interface GoogleSearchCallContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'google_search_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + /** + * The arguments to pass to Google Search. + */ + arguments?: GoogleSearchCallArguments; +} + +/** + * The result of the Google Search. + */ +export interface GoogleSearchResult { + /** + * Web content snippet that can be embedded in a web page or an app webview. + */ + rendered_content?: string; + + /** + * Title of the search result. + */ + title?: string; + + /** + * URI reference of the search result. + */ + url?: string; +} + +/** + * Google Search result content. + */ +export interface GoogleSearchResultContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'google_search_result'; + + /** + * ID to match the ID from the google search call block. + */ + call_id?: string; + + /** + * Whether the Google Search resulted in an error. + */ + is_error?: boolean; + + /** + * The results of the Google Search. + */ + result?: Array; + + /** + * The signature of the Google Search result. + */ + signature?: string; +} + +/** + * An image content block. + */ +export interface ImageContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'image'; + + data?: string; + + /** + * The mime type of the image. + */ + mime_type?: ImageMimeType; + + /** + * The resolution of the media. + */ + resolution?: 'low' | 'medium' | 'high'; + + uri?: string; +} + +/** + * The mime type of the image. + */ +export type ImageMimeType = + | 'image/png' + | 'image/jpeg' + | 'image/webp' + | 'image/heic' + | 'image/heif' + | (string & {}); + +/** + * The Interaction resource. + */ +export interface Interaction { + /** + * Output only. A unique identifier for the interaction completion. + */ + id: string; + + /** + * Output only. The status of the interaction. + */ + status: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; + + /** + * The name of the `Agent` used for generating the interaction. + */ + agent?: (string & {}) | 'deep-research-pro-preview-12-2025'; + + /** + * Output only. The time at which the response was created in ISO 8601 format + * (YYYY-MM-DDThh:mm:ssZ). + */ + created?: string; + + /** + * Output only. The error message for the interaction, if any. + */ + error?: Interaction.Error; + + /** + * The name of the `Model` used for generating the interaction. + */ + model?: Model; + + /** + * Output only. The object type of the interaction. Always set to `interaction`. + */ + object?: 'interaction'; + + /** + * Output only. Responses from the model. + */ + outputs?: Array< + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent + >; + + /** + * The ID of the previous interaction, if any. + */ + previous_interaction_id?: string; + + /** + * Output only. The role of the interaction. + */ + role?: string; + + /** + * Output only. The time at which the response was last updated in ISO 8601 format + * (YYYY-MM-DDThh:mm:ssZ). + */ + updated?: string; + + /** + * Output only. Statistics on the interaction request's token usage. + */ + usage?: Usage; +} + +export namespace Interaction { + /** + * Output only. The error message for the interaction, if any. + */ + export interface Error { + /** + * A URI that identifies the error type. + */ + code?: string; + + /** + * A human-readable error message. + */ + message?: string; + } +} + +export interface InteractionEvent { + /** + * The event_id token to be used to resume the interaction stream, from + * this event. + */ + event_id?: string; + + event_type?: 'interaction.start' | 'interaction.complete'; + + /** + * The Interaction resource. + */ + interaction?: Interaction; +} + +export type InteractionSSEEvent = + | InteractionEvent + | InteractionStatusUpdate + | ContentStart + | ContentDelta + | ContentStop + | ErrorEvent; + +export interface InteractionStatusUpdate { + /** + * The event_id token to be used to resume the interaction stream, from + * this event. + */ + event_id?: string; + + event_type?: 'interaction.status_update'; + + interaction_id?: string; + + status?: 'in_progress' | 'requires_action' | 'completed' | 'failed' | 'cancelled'; +} + +/** + * MCPServer tool call content. + */ +export interface MCPServerToolCallContent { + /** + * A unique ID for this specific tool call. + */ + id: string; + + /** + * The JSON object of arguments for the function. + */ + arguments: { [key: string]: unknown }; + + /** + * The name of the tool which was called. + */ + name: string; + + /** + * The name of the used MCP server. + */ + server_name: string; + + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'mcp_server_tool_call'; +} + +/** + * MCPServer tool result content. + */ +export interface MCPServerToolResultContent { + /** + * ID to match the ID from the MCP server tool call block. + */ + call_id: string; + + /** + * The result of the tool call. + */ + result: MCPServerToolResultContent.Items | unknown | string; + + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'mcp_server_tool_result'; + + /** + * Name of the tool which is called for this specific tool call. + */ + name?: string; + + /** + * The name of the used MCP server. + */ + server_name?: string; +} + +export namespace MCPServerToolResultContent { + export interface Items { + items?: Array; + } +} + +/** + * The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + */ +export type Model = + | 'gemini-2.5-pro' + | 'gemini-2.5-flash' + | 'gemini-2.5-flash-preview-09-2025' + | 'gemini-2.5-flash-lite' + | 'gemini-2.5-flash-lite-preview-09-2025' + | 'gemini-2.5-flash-preview-native-audio-dialog' + | 'gemini-2.5-flash-image-preview' + | 'gemini-2.5-pro-preview-tts' + | 'gemini-3-pro-preview' + | (string & {}); + +/** + * The configuration for speech interaction. + */ +export interface SpeechConfig { + /** + * The language of the speech. + */ + language?: string; + + /** + * The speaker's name, it should match the speaker name given in the prompt. + */ + speaker?: string; + + /** + * The voice of the speaker. + */ + voice?: string; +} + +/** + * A text content block. + */ +export interface TextContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'text'; + + /** + * Citation information for model-generated content. + */ + annotations?: Array; + + /** + * The text content. + */ + text?: string; +} + +export type ThinkingLevel = 'low' | 'high'; + +/** + * A thought content block. + */ +export interface ThoughtContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'thought'; + + /** + * Signature to match the backend source to be part of the generation. + */ + signature?: string; + + /** + * A summary of the thought. + */ + summary?: Array; +} + +/** + * A tool that can be used by the model. + */ +export type Tool = + | Function + | Tool.GoogleSearch + | Tool.CodeExecution + | Tool.URLContext + | Tool.ComputerUse + | Tool.MCPServer + | Tool.FileSearch; + +export namespace Tool { + /** + * A tool that can be used by the model to search Google. + */ + export interface GoogleSearch { + type: 'google_search'; + } + + /** + * A tool that can be used by the model to execute code. + */ + export interface CodeExecution { + type: 'code_execution'; + } + + /** + * A tool that can be used by the model to fetch URL context. + */ + export interface URLContext { + type: 'url_context'; + } + + /** + * A tool that can be used by the model to interact with the computer. + */ + export interface ComputerUse { + type: 'computer_use'; + + /** + * The environment being operated. + */ + environment?: 'browser'; + + /** + * The list of predefined functions that are excluded from the model call. + */ + excludedPredefinedFunctions?: Array; + } + + /** + * A MCPServer is a server that can be called by the model to perform actions. + */ + export interface MCPServer { + type: 'mcp_server'; + + /** + * The allowed tools. + */ + allowed_tools?: Array; + + /** + * Optional: Fields for authentication headers, timeouts, etc., if needed. + */ + headers?: { [key: string]: string }; + + /** + * The name of the MCPServer. + */ + name?: string; + + /** + * The full URL for the MCPServer endpoint. + * Example: "https://api.example.com/mcp" + */ + url?: string; + } + + /** + * A tool that can be used by the model to search files. + */ + export interface FileSearch { + type: 'file_search'; + + /** + * The file search store names to search. + */ + file_search_store_names?: Array; + + /** + * Metadata filter to apply to the semantic retrieval documents and chunks. + */ + metadata_filter?: string; + + /** + * The number of semantic retrieval chunks to retrieve. + */ + top_k?: number; + } +} + +/** + * The configuration for tool choice. + */ +export type ToolChoice = ToolChoiceType | ToolChoiceConfig; + +export interface ToolChoiceConfig { + /** + * The configuration for allowed tools. + */ + allowed_tools?: AllowedTools; +} + +export type ToolChoiceType = 'auto' | 'any' | 'none' | 'validated'; + +export interface Turn { + /** + * The content of the turn. + */ + content?: + | string + | Array< + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent + >; + + /** + * The originator of this turn. Must be user for input or model for + * model output. + */ + role?: string; +} + +/** + * The arguments to pass to the URL context. + */ +export interface URLContextCallArguments { + /** + * The URLs to fetch. + */ + urls?: Array; +} + +/** + * URL context content. + */ +export interface URLContextCallContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'url_context_call'; + + /** + * A unique ID for this specific tool call. + */ + id?: string; + + /** + * The arguments to pass to the URL context. + */ + arguments?: URLContextCallArguments; +} + +/** + * The result of the URL context. + */ +export interface URLContextResult { + /** + * The status of the URL retrieval. + */ + status?: 'success' | 'error' | 'paywall' | 'unsafe'; + + /** + * The URL that was fetched. + */ + url?: string; +} + +/** + * URL context result content. + */ +export interface URLContextResultContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'url_context_result'; + + /** + * ID to match the ID from the url context call block. + */ + call_id?: string; + + /** + * Whether the URL context resulted in an error. + */ + is_error?: boolean; + + /** + * The results of the URL context. + */ + result?: Array; + + /** + * The signature of the URL context result. + */ + signature?: string; +} + +/** + * Statistics on the interaction request's token usage. + */ +export interface Usage { + /** + * A breakdown of cached token usage by modality. + */ + cached_tokens_by_modality?: Array; + + /** + * A breakdown of input token usage by modality. + */ + input_tokens_by_modality?: Array; + + /** + * A breakdown of output token usage by modality. + */ + output_tokens_by_modality?: Array; + + /** + * A breakdown of tool-use token usage by modality. + */ + tool_use_tokens_by_modality?: Array; + + /** + * Number of tokens in the cached part of the prompt (the cached content). + */ + total_cached_tokens?: number; + + /** + * Number of tokens in the prompt (context). + */ + total_input_tokens?: number; + + /** + * Total number of tokens across all the generated responses. + */ + total_output_tokens?: number; + + /** + * Number of tokens of thoughts for thinking models. + */ + total_reasoning_tokens?: number; + + /** + * Total token count for the interaction request (prompt + responses + other + * internal tokens). + */ + total_tokens?: number; + + /** + * Number of tokens present in tool-use prompt(s). + */ + total_tool_use_tokens?: number; +} + +export namespace Usage { + /** + * The token count for a single response modality. + */ + export interface CachedTokensByModality { + /** + * The modality associated with the token count. + */ + modality?: 'text' | 'image' | 'audio'; + + /** + * Number of tokens for the modality. + */ + tokens?: number; + } + + /** + * The token count for a single response modality. + */ + export interface InputTokensByModality { + /** + * The modality associated with the token count. + */ + modality?: 'text' | 'image' | 'audio'; + + /** + * Number of tokens for the modality. + */ + tokens?: number; + } + + /** + * The token count for a single response modality. + */ + export interface OutputTokensByModality { + /** + * The modality associated with the token count. + */ + modality?: 'text' | 'image' | 'audio'; + + /** + * Number of tokens for the modality. + */ + tokens?: number; + } + + /** + * The token count for a single response modality. + */ + export interface ToolUseTokensByModality { + /** + * The modality associated with the token count. + */ + modality?: 'text' | 'image' | 'audio'; + + /** + * Number of tokens for the modality. + */ + tokens?: number; + } +} + +/** + * A video content block. + */ +export interface VideoContent { + /** + * Used as the OpenAPI type discriminator for the content oneof. + */ + type: 'video'; + + data?: string; + + /** + * The mime type of the video. + */ + mime_type?: VideoMimeType; + + /** + * The resolution of the media. + */ + resolution?: 'low' | 'medium' | 'high'; + + uri?: string; +} + +/** + * The mime type of the video. + */ +export type VideoMimeType = + | 'video/mp4' + | 'video/mpeg' + | 'video/mov' + | 'video/avi' + | 'video/x-flv' + | 'video/mpg' + | 'video/webm' + | 'video/wmv' + | 'video/3gpp' + | (string & {}); + +export type InteractionDeleteResponse = unknown; + +export type InteractionCreateParams = + | CreateModelInteractionParamsNonStreaming + | CreateModelInteractionParamsStreaming + | CreateAgentInteractionParamsNonStreaming + | CreateAgentInteractionParamsStreaming; + +export interface BaseCreateModelInteractionParams { + /** + * Body param: The inputs for the interaction. + */ + input: + | string + | Array< + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent + > + | Array + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent; + + /** + * Body param: The name of the `Model` used for generating the interaction. + */ + model: Model; + + /** + * Path param: Which version of the API to use. + */ + api_version?: string; + + /** + * Body param: Whether to run the model interaction in the background. + */ + background?: boolean; + + /** + * Body param: Input only. Configuration parameters for the model interaction. + */ + generation_config?: GenerationConfig; + + /** + * Body param: The ID of the previous interaction, if any. + */ + previous_interaction_id?: string; + + /** + * Body param: Enforces that the generated response is a JSON object that complies with + * the JSON schema specified in this field. + */ + response_format?: unknown; + + /** + * Body param: The mime type of the response. This is required if response_format is set. + */ + response_mime_type?: string; + + /** + * Body param: The requested modalities of the response (TEXT, IMAGE, AUDIO). + */ + response_modalities?: Array<'text' | 'image' | 'audio'>; + + /** + * Body param: Input only. Whether to store the response and request for later retrieval. + */ + store?: boolean; + + /** + * Body param: Input only. Whether the interaction will be streamed. + */ + stream?: boolean; + + /** + * Body param: System instruction for the interaction. + */ + system_instruction?: string; + + /** + * Body param: A list of tool declarations the model may call during interaction. + */ + tools?: Array; +} + +export interface BaseCreateAgentInteractionParams { + /** + * Body param: The name of the `Agent` used for generating the interaction. + */ + agent: (string & {}) | 'deep-research-pro-preview-12-2025'; + + /** + * Body param: The inputs for the interaction. + */ + input: + | string + | Array< + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent + > + | Array + | TextContent + | ImageContent + | AudioContent + | DocumentContent + | VideoContent + | ThoughtContent + | FunctionCallContent + | FunctionResultContent + | CodeExecutionCallContent + | CodeExecutionResultContent + | URLContextCallContent + | URLContextResultContent + | GoogleSearchCallContent + | GoogleSearchResultContent + | MCPServerToolCallContent + | MCPServerToolResultContent + | FileSearchResultContent; + + /** + * Path param: Which version of the API to use. + */ + api_version?: string; + + /** + * Body param: Configuration for the agent. + */ + agent_config?: DynamicAgentConfig | DeepResearchAgentConfig; + + /** + * Body param: Whether to run the model interaction in the background. + */ + background?: boolean; + + /** + * Body param: The ID of the previous interaction, if any. + */ + previous_interaction_id?: string; + + /** + * Body param: Enforces that the generated response is a JSON object that complies with + * the JSON schema specified in this field. + */ + response_format?: unknown; + + /** + * Body param: The mime type of the response. This is required if response_format is set. + */ + response_mime_type?: string; + + /** + * Body param: The requested modalities of the response (TEXT, IMAGE, AUDIO). + */ + response_modalities?: Array<'text' | 'image' | 'audio'>; + + /** + * Body param: Input only. Whether to store the response and request for later retrieval. + */ + store?: boolean; + + /** + * Body param: Input only. Whether the interaction will be streamed. + */ + stream?: boolean; + + /** + * Body param: System instruction for the interaction. + */ + system_instruction?: string; + + /** + * Body param: A list of tool declarations the model may call during interaction. + */ + tools?: Array; +} + +export interface CreateModelInteractionParamsNonStreaming extends BaseCreateModelInteractionParams { + /** + * Body param: Input only. Whether the interaction will be streamed. + */ + stream?: false; +} + +export interface CreateModelInteractionParamsStreaming extends BaseCreateModelInteractionParams { + /** + * Body param: Input only. Whether the interaction will be streamed. + */ + stream: true; +} + +export interface CreateAgentInteractionParamsNonStreaming extends BaseCreateAgentInteractionParams { + /** + * Body param: Input only. Whether the interaction will be streamed. + */ + stream?: false; +} + +export interface CreateAgentInteractionParamsStreaming extends BaseCreateAgentInteractionParams { + /** + * Body param: Input only. Whether the interaction will be streamed. + */ + stream: true; +} + +export interface InteractionDeleteParams { + /** + * Which version of the API to use. + */ + api_version?: string; +} + +export interface InteractionCancelParams { + /** + * Which version of the API to use. + */ + api_version?: string; +} + +export type InteractionGetParams = InteractionGetParamsNonStreaming | InteractionGetParamsStreaming; + +export interface InteractionGetParamsBase { + /** + * Path param: Which version of the API to use. + */ + api_version?: string; + + /** + * Query param: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + */ + last_event_id?: string; + + /** + * Query param: If set to true, the generated content will be streamed incrementally. + */ + stream?: boolean; +} + + + + +export interface InteractionGetParamsNonStreaming extends InteractionGetParamsBase { + /** + * Query param: If set to true, the generated content will be streamed incrementally. + */ + stream?: false; +} + +export interface InteractionGetParamsStreaming extends InteractionGetParamsBase { + /** + * Query param: If set to true, the generated content will be streamed incrementally. + */ + stream: true; +} + +export declare namespace Interactions { + export { + type AllowedTools as AllowedTools, + type Annotation as Annotation, + type AudioContent as AudioContent, + type AudioMimeType as AudioMimeType, + type CodeExecutionCallArguments as CodeExecutionCallArguments, + type CodeExecutionCallContent as CodeExecutionCallContent, + type CodeExecutionResultContent as CodeExecutionResultContent, + type ContentDelta as ContentDelta, + type ContentStart as ContentStart, + type ContentStop as ContentStop, + type DeepResearchAgentConfig as DeepResearchAgentConfig, + type DocumentContent as DocumentContent, + type DynamicAgentConfig as DynamicAgentConfig, + type ErrorEvent as ErrorEvent, + type FileSearchResultContent as FileSearchResultContent, + type Function as Function, + type FunctionCallContent as FunctionCallContent, + type FunctionResultContent as FunctionResultContent, + type GenerationConfig as GenerationConfig, + type GoogleSearchCallArguments as GoogleSearchCallArguments, + type GoogleSearchCallContent as GoogleSearchCallContent, + type GoogleSearchResult as GoogleSearchResult, + type GoogleSearchResultContent as GoogleSearchResultContent, + type ImageContent as ImageContent, + type ImageMimeType as ImageMimeType, + type Interaction as Interaction, + type InteractionEvent as InteractionEvent, + type InteractionSSEEvent as InteractionSSEEvent, + type InteractionStatusUpdate as InteractionStatusUpdate, + type MCPServerToolCallContent as MCPServerToolCallContent, + type MCPServerToolResultContent as MCPServerToolResultContent, + type Model as Model, + type SpeechConfig as SpeechConfig, + type TextContent as TextContent, + type ThinkingLevel as ThinkingLevel, + type ThoughtContent as ThoughtContent, + type Tool as Tool, + type ToolChoice as ToolChoice, + type ToolChoiceConfig as ToolChoiceConfig, + type ToolChoiceType as ToolChoiceType, + type Turn as Turn, + type URLContextCallArguments as URLContextCallArguments, + type URLContextCallContent as URLContextCallContent, + type URLContextResult as URLContextResult, + type URLContextResultContent as URLContextResultContent, + type Usage as Usage, + type VideoContent as VideoContent, + type VideoMimeType as VideoMimeType, + type InteractionDeleteResponse as InteractionDeleteResponse, + type InteractionCreateParams as InteractionCreateParams, + type CreateModelInteractionParamsNonStreaming as CreateModelInteractionParamsNonStreaming, + type CreateModelInteractionParamsStreaming as CreateModelInteractionParamsStreaming, + type CreateAgentInteractionParamsNonStreaming as CreateAgentInteractionParamsNonStreaming, + type CreateAgentInteractionParamsStreaming as CreateAgentInteractionParamsStreaming, + type InteractionDeleteParams as InteractionDeleteParams, + type InteractionCancelParams as InteractionCancelParams, + type InteractionGetParams as InteractionGetParams, + type InteractionGetParamsNonStreaming as InteractionGetParamsNonStreaming, + type InteractionGetParamsStreaming as InteractionGetParamsStreaming, + }; +} diff --git a/src/interactions/streaming.ts b/src/interactions/streaming.ts new file mode 100644 index 000000000..2b8527fd8 --- /dev/null +++ b/src/interactions/streaming.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @deprecated Import from ./core/streaming instead */ +export * from './core/streaming.js'; diff --git a/src/interactions/tree-shakable.ts b/src/interactions/tree-shakable.ts new file mode 100644 index 000000000..3533f9d17 --- /dev/null +++ b/src/interactions/tree-shakable.ts @@ -0,0 +1,108 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { BaseGeminiNextGenAPIClient, ClientOptions } from './client.js'; +import { APIResource } from './core/resource.js'; +import { hasOwn } from './internal/utils/values.js'; + +export type PartialGeminiNextGenAPIClient = BaseGeminiNextGenAPIClient & T; + +type InferClientStructure = + SimplifyResources>> extends infer O extends MaybeResource ? O + : never; + +/** + * Creates a client with a subset of the available resources to reduce + * bundle size. + * Import the resource classes you need from `gemini-next-gen-api/resources/*`. + * Use the BaseResource variants if you do not need to use subresources. + * + * @example + * ```ts + * import { Interactions } from `gemini-next-gen-api/resources/interactions`; + * import { createClient } from `gemini-next-gen-api/tree-shakable`; + * + * const client = createClient({ + * resources: [Interactions], + * }); + * ``` + */ +export function createClient( + options: ClientOptions & { resources: T }, +): PartialGeminiNextGenAPIClient> { + const client = new BaseGeminiNextGenAPIClient(options); + for (const ResourceClass of options.resources) { + const resourceInstance: APIResource = new (ResourceClass as any)(client); + let object: object = client; + for (const part of ResourceClass._key.slice(0, -1)) { + if (hasOwn(object, part)) { + object = object[part]; + } else { + Object.defineProperty(object, part, { + value: (object = {}), + configurable: true, + enumerable: true, + writable: true, + }); + } + } + const name = ResourceClass._key.at(-1)!; + if (!hasOwn(object, name)) { + Object.defineProperty(object, name, { + value: resourceInstance, + configurable: true, + enumerable: true, + writable: true, + }); + } else { + if ((object as any)[name] instanceof APIResource) { + throw new TypeError(`Resource at ${ResourceClass._key.join('.')} already exists!`); + } else { + (object as any)[name] = Object.assign(resourceInstance, (object as any)[name]); + } + } + } + return client as any; +} + +/** + * `FromDeepEntry<['a', 'b', 'c'], 'value'>` -> `{ a: { b: { c: 'value'; }; }; }` + */ +type FromDeepEntry = K['length'] extends 1 ? { [_ in K[0]]: V } & {} +: K extends readonly [infer First extends PropertyKey, ...infer Rest extends readonly PropertyKey[]] ? + { [_ in First]: FromDeepEntry } +: never; + +/** + * `ResourcePath<{ _key: ["a", "b", "c"]; new (): Instance }>` -> `{ a: { b: { c: Instance; }; }; }` + */ +type ResourcePath = + T extends any ? FromDeepEntry> : never; + +/** + * `UnionToIntersection<{ a: 1 } | { b: 2 }>` -> `{ a: 1 } & { b: 2 }` + */ +type UnionToIntersection = + (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never; + +/** + * Makes the types look nicer: + * `SimplifyResources<{ a: APIResource } & { b: { c: APIResource } & { d: APIResource } }>` -> `{ a: APIResource; b: { c: APIResource; d: APIResource; }; }` + */ +type SimplifyResources = + T extends APIResource ? T + : T extends object ? + { + [P in keyof T as string extends P ? never : P]: SimplifyResources; + } + : T; + +type MaybeResource = MaybeResource_ | APIResource; +interface MaybeResource_ { + [_: string]: MaybeResource; +} diff --git a/src/interactions/uploads.ts b/src/interactions/uploads.ts new file mode 100644 index 000000000..cf67ff42f --- /dev/null +++ b/src/interactions/uploads.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @deprecated Import from ./core/uploads instead */ +export * from './core/uploads.js'; diff --git a/src/interactions/version.ts b/src/interactions/version.ts new file mode 100644 index 000000000..8b8af8f96 --- /dev/null +++ b/src/interactions/version.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export const VERSION = '0.0.1'; diff --git a/src/node/index.ts b/src/node/index.ts index 8b56c1b35..83bc4e4d6 100644 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -11,6 +11,7 @@ export * from '../chats.js'; export {GoogleGenAIOptions} from '../client.js'; export * from '../errors.js'; export {Files} from '../files.js'; +export type * as Interactions from '../interactions/resources/interactions.js'; export * from '../live.js'; export {mcpToTool} from '../mcp/_mcp.js'; export {Models} from '../models.js'; diff --git a/src/node/node_client.ts b/src/node/node_client.ts index 17bd0e256..085015857 100644 --- a/src/node/node_client.ts +++ b/src/node/node_client.ts @@ -14,6 +14,8 @@ import {Chats} from '../chats.js'; import {GoogleGenAIOptions} from '../client.js'; import {Files} from '../files.js'; import {FileSearchStores} from '../filesearchstores.js'; +import GeminiNextGenAPI from '../interactions/index.js'; +import {Interactions as GeminiNextGenInteractions} from '../interactions/resources/interactions.js'; import {Live} from '../live.js'; import {Models} from '../models.js'; import {NodeAuth} from '../node/_node_auth.js'; @@ -23,6 +25,7 @@ import {Operations} from '../operations.js'; import {Tokens} from '../tokens.js'; import {Tunings} from '../tunings.js'; import {HttpOptions} from '../types.js'; + import {NodeUploader} from './_node_uploader.js'; const LANGUAGE_LABEL_PREFIX = 'gl-node/'; @@ -86,6 +89,42 @@ export class GoogleGenAI { readonly authTokens: Tokens; readonly tunings: Tunings; readonly fileSearchStores: FileSearchStores; + private _interactions: GeminiNextGenInteractions | undefined; + + get interactions(): GeminiNextGenInteractions { + if (this._interactions !== undefined) { + return this._interactions; + } + + console.warn( + 'GoogleGenAI.interactions: Interactions usage is experimental and may change in future versions.', + ); + + if (this.vertexai) { + throw new Error( + 'This version of the GenAI SDK does not support Vertex AI API for interactions.', + ); + } + + const httpOpts = this.httpOptions; + + // Unsupported Options Warnings + if (httpOpts?.extraBody) { + console.warn( + 'GoogleGenAI.interactions: Client level httpOptions.extraBody is not supported by the interactions client and will be ignored.', + ); + } + + const nextGenClient = new GeminiNextGenAPI({ + baseURL: this.apiClient.getBaseUrl(), + apiKey: this.apiKey, + defaultHeaders: this.apiClient.getDefaultHeaders(), + timeout: httpOpts?.timeout, + }); + this._interactions = nextGenClient.interactions; + + return this._interactions; + } constructor(options: GoogleGenAIOptions) { // Validate explicitly set initializer values. if ((options.project || options.location) && options.apiKey) { diff --git a/src/web/index.ts b/src/web/index.ts index 3863d76bb..72461be2f 100644 --- a/src/web/index.ts +++ b/src/web/index.ts @@ -11,6 +11,7 @@ export * from '../chats.js'; export {GoogleGenAIOptions} from '../client.js'; export * from '../errors.js'; export {Files} from '../files.js'; +export type * as Interactions from '../interactions/resources/interactions.js'; export * from '../live.js'; export {mcpToTool} from '../mcp/_mcp'; export {Models} from '../models.js'; diff --git a/src/web/web_client.ts b/src/web/web_client.ts index 3cfbbf14a..75edac022 100644 --- a/src/web/web_client.ts +++ b/src/web/web_client.ts @@ -12,6 +12,8 @@ import {Chats} from '../chats.js'; import {GoogleGenAIOptions} from '../client.js'; import {Files} from '../files.js'; import {FileSearchStores} from '../filesearchstores.js'; +import GeminiNextGenAPI from '../interactions/index.js'; +import {Interactions as GeminiNextGenInteractions} from '../interactions/resources/interactions.js'; import {Live} from '../live.js'; import {Models} from '../models.js'; import {Operations} from '../operations.js'; @@ -78,6 +80,41 @@ export class GoogleGenAI { readonly authTokens: Tokens; readonly tunings: Tunings; readonly fileSearchStores: FileSearchStores; + private _interactions: GeminiNextGenInteractions | undefined; + get interactions(): GeminiNextGenInteractions { + if (this._interactions !== undefined) { + return this._interactions; + } + + console.warn( + 'GoogleGenAI.interactions: Interactions usage is experimental and may change in future versions.', + ); + + if (this.vertexai) { + throw new Error( + 'This version of the GenAI SDK does not support Vertex AI API for interactions.', + ); + } + + const httpOpts = this.httpOptions; + + // Unsupported Options Warnings + if (httpOpts?.extraBody) { + console.warn( + 'GoogleGenAI.interactions: Client level httpOptions.extraBody is not supported by the interactions client and will be ignored.', + ); + } + + const nextGenClient = new GeminiNextGenAPI({ + baseURL: this.apiClient.getBaseUrl(), + apiKey: this.apiKey, + defaultHeaders: this.apiClient.getDefaultHeaders(), + timeout: httpOpts?.timeout, + }); + this._interactions = nextGenClient.interactions; + + return this._interactions; + } constructor(options: GoogleGenAIOptions) { if (options.apiKey == null) { throw new Error('An API Key must be set when running in a browser');