@umituz/react-native-ai-gemini-provider 3.0.21 → 3.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/domain/entities/error.types.d.ts +96 -0
  2. package/dist/domain/entities/gemini.types.d.ts +133 -0
  3. package/dist/domain/entities/index.d.ts +6 -0
  4. package/dist/domain/entities/models.d.ts +23 -0
  5. package/dist/index.d.ts +16 -0
  6. package/dist/infrastructure/services/BaseService.d.ts +34 -0
  7. package/dist/infrastructure/services/ChatSession.d.ts +67 -0
  8. package/dist/infrastructure/services/GeminiClient.d.ts +19 -0
  9. package/dist/infrastructure/services/GeminiProvider.d.ts +10 -0
  10. package/dist/infrastructure/services/Streaming.d.ts +7 -0
  11. package/dist/infrastructure/services/StructuredText.d.ts +7 -0
  12. package/dist/infrastructure/services/TextGeneration.d.ts +8 -0
  13. package/dist/infrastructure/services/index.d.ts +6 -0
  14. package/dist/infrastructure/telemetry/TelemetryHooks.d.ts +57 -0
  15. package/dist/infrastructure/telemetry/index.d.ts +4 -0
  16. package/dist/infrastructure/utils/async/execute-state.util.d.ts +50 -0
  17. package/dist/infrastructure/utils/async/index.d.ts +4 -0
  18. package/dist/infrastructure/utils/content-mapper.util.d.ts +45 -0
  19. package/dist/infrastructure/utils/error-mapper.util.d.ts +2 -0
  20. package/dist/infrastructure/utils/gemini-data-transformer.util.d.ts +2 -0
  21. package/dist/infrastructure/utils/json-parser.util.d.ts +9 -0
  22. package/dist/infrastructure/utils/stream-processor.util.d.ts +14 -0
  23. package/dist/presentation/hooks/index.d.ts +1 -0
  24. package/dist/presentation/hooks/useGemini.d.ts +17 -0
  25. package/dist/presentation/hooks/useOperationManager.d.ts +23 -0
  26. package/dist/providers/ConfigBuilder.d.ts +50 -0
  27. package/dist/providers/ProviderFactory.d.ts +31 -0
  28. package/dist/providers/index.d.ts +7 -0
  29. package/package.json +1 -1
  30. package/src/index.ts +11 -1
  31. package/src/infrastructure/services/ChatSession.ts +175 -18
  32. package/src/infrastructure/services/index.ts +11 -1
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Categories of errors that can occur with Gemini API
3
+ */
4
+ export declare enum GeminiErrorType {
5
+ /** Network connectivity issues */
6
+ NETWORK = "NETWORK",
7
+ /** API rate limit exceeded */
8
+ RATE_LIMIT = "RATE_LIMIT",
9
+ /** Authentication/authorization failures */
10
+ AUTHENTICATION = "AUTHENTICATION",
11
+ /** Invalid input data or parameters */
12
+ VALIDATION = "VALIDATION",
13
+ /** Content blocked by safety filters */
14
+ SAFETY = "SAFETY",
15
+ /** Server-side errors */
16
+ SERVER = "SERVER",
17
+ /** Request timeout */
18
+ TIMEOUT = "TIMEOUT",
19
+ /** API quota exceeded */
20
+ QUOTA_EXCEEDED = "QUOTA_EXCEEDED",
21
+ /** Requested model not found */
22
+ MODEL_NOT_FOUND = "MODEL_NOT_FOUND",
23
+ /** Unknown/unclassified error */
24
+ UNKNOWN = "UNKNOWN"
25
+ }
26
+ /**
27
+ * Detailed error information for Gemini API errors
28
+ */
29
+ export interface GeminiErrorInfo {
30
+ /** Category of the error */
31
+ type: GeminiErrorType;
32
+ /** Message key for i18n translation */
33
+ messageKey: string;
34
+ /** Whether the request can be retried */
35
+ retryable: boolean;
36
+ /** Original error that caused this error */
37
+ originalError?: unknown;
38
+ /** HTTP status code if applicable */
39
+ statusCode?: number;
40
+ }
41
+ /**
42
+ * Structure of Gemini API error responses
43
+ */
44
+ export interface GeminiApiError {
45
+ error?: {
46
+ /** Error code */
47
+ code?: number;
48
+ /** Error message */
49
+ message?: string;
50
+ /** Error status */
51
+ status?: string;
52
+ /** Additional error details */
53
+ details?: Array<{
54
+ "@type"?: string;
55
+ reason?: string;
56
+ domain?: string;
57
+ metadata?: Record<string, string>;
58
+ }>;
59
+ };
60
+ }
61
+ /**
62
+ * Custom error class for Gemini API errors
63
+ * Provides structured error information and retry capability
64
+ */
65
+ export declare class GeminiError extends Error {
66
+ /** Error category */
67
+ readonly type: GeminiErrorType;
68
+ /** Whether the operation can be retried */
69
+ readonly retryable: boolean;
70
+ /** HTTP status code if applicable */
71
+ readonly statusCode?: number;
72
+ /** Original error that caused this error */
73
+ readonly originalError?: unknown;
74
+ /**
75
+ * Create a new GeminiError
76
+ * @param info - Error information
77
+ */
78
+ constructor(info: GeminiErrorInfo);
79
+ /**
80
+ * Check if this error is retryable
81
+ * @returns true if the operation can be retried
82
+ */
83
+ isRetryable(): boolean;
84
+ /**
85
+ * Get the error type
86
+ * @returns The error category
87
+ */
88
+ getErrorType(): GeminiErrorType;
89
+ /**
90
+ * Create a GeminiError from an unknown error
91
+ * @param error - The original error
92
+ * @param info - Error information
93
+ * @returns A new GeminiError instance
94
+ */
95
+ static fromError(error: unknown, info: GeminiErrorInfo): GeminiError;
96
+ }
@@ -0,0 +1,133 @@
1
+ import type { GenerationConfig } from "@google/generative-ai";
2
+ /**
3
+ * Configuration for Gemini AI client initialization
4
+ */
5
+ export interface GeminiConfig {
6
+ /** API key for authentication */
7
+ apiKey: string;
8
+ /** Optional base URL for API requests */
9
+ baseUrl?: string;
10
+ /** Default timeout in milliseconds */
11
+ defaultTimeoutMs?: number;
12
+ /** Default model to use for text generation */
13
+ textModel?: string;
14
+ }
15
+ /**
16
+ * Generation configuration for AI requests
17
+ * Extends the SDK's GenerationConfig with proper schema typing
18
+ */
19
+ export type GeminiGenerationConfig = Omit<GenerationConfig, "responseSchema"> & {
20
+ responseSchema?: GenerationConfig["responseSchema"];
21
+ };
22
+ /**
23
+ * Harm categories for content safety filtering
24
+ */
25
+ export type GeminiHarmCategory = "HARM_CATEGORY_HARASSMENT" | "HARM_CATEGORY_HATE_SPEECH" | "HARM_CATEGORY_SEXUALLY_EXPLICIT" | "HARM_CATEGORY_DANGEROUS_CONTENT";
26
+ /**
27
+ * Threshold levels for blocking harmful content
28
+ */
29
+ export type GeminiHarmBlockThreshold = "BLOCK_NONE" | "BLOCK_LOW_AND_ABOVE" | "BLOCK_MEDIUM_AND_ABOVE" | "BLOCK_ONLY_HIGH";
30
+ /**
31
+ * Content structure for Gemini API requests
32
+ */
33
+ export interface GeminiContent {
34
+ /** Array of content parts (text, images, etc.) */
35
+ parts: GeminiPart[];
36
+ /** Role of the content creator (user or model) */
37
+ role?: "user" | "model";
38
+ }
39
+ /**
40
+ * Individual content part
41
+ */
42
+ export type GeminiPart = {
43
+ text: string;
44
+ };
45
+ /**
46
+ * Response structure from Gemini API
47
+ */
48
+ export interface GeminiResponse {
49
+ /** Array of response candidates */
50
+ candidates?: GeminiCandidate[];
51
+ /** Token usage information */
52
+ usageMetadata?: GeminiUsageMetadata;
53
+ }
54
+ /**
55
+ * Individual response candidate
56
+ */
57
+ export interface GeminiCandidate {
58
+ /** Generated content */
59
+ content: GeminiContent;
60
+ /** Reason for generation completion */
61
+ finishReason?: GeminiFinishReason;
62
+ /** Safety ratings for the content */
63
+ safetyRatings?: GeminiSafetyRating[];
64
+ }
65
+ /**
66
+ * Reasons why generation finished
67
+ */
68
+ export type GeminiFinishReason = "FINISH_REASON_UNSPECIFIED" | "STOP" | "MAX_TOKENS" | "SAFETY" | "RECITATION" | "OTHER";
69
+ /**
70
+ * Safety rating for generated content
71
+ */
72
+ export interface GeminiSafetyRating {
73
+ /** Category of safety check */
74
+ category: GeminiHarmCategory;
75
+ /** Probability of content being unsafe */
76
+ probability: "NEGLIGIBLE" | "LOW" | "MEDIUM" | "HIGH";
77
+ /** Whether the content was blocked */
78
+ blocked?: boolean;
79
+ }
80
+ /**
81
+ * Token usage metadata for the request
82
+ */
83
+ export interface GeminiUsageMetadata {
84
+ /** Number of tokens in the prompt */
85
+ promptTokenCount?: number;
86
+ /** Number of tokens in the response candidates */
87
+ candidatesTokenCount?: number;
88
+ /** Total number of tokens used */
89
+ totalTokenCount?: number;
90
+ }
91
+ /**
92
+ * Safety setting for a single harm category
93
+ */
94
+ export interface GeminiSafetySetting {
95
+ category: GeminiHarmCategory;
96
+ threshold: GeminiHarmBlockThreshold;
97
+ }
98
+ /**
99
+ * Inline data part for binary content (images, audio)
100
+ */
101
+ export interface GeminiInlineDataPart {
102
+ inlineData: {
103
+ mimeType: string;
104
+ data: string;
105
+ };
106
+ }
107
+ /**
108
+ * A message part that can be text or inline data
109
+ */
110
+ export type GeminiMessagePart = GeminiPart | GeminiInlineDataPart;
111
+ /**
112
+ * Options for creating a generative model instance
113
+ */
114
+ export interface GeminiModelOptions {
115
+ model?: string;
116
+ systemInstruction?: string;
117
+ safetySettings?: GeminiSafetySetting[];
118
+ }
119
+ /**
120
+ * Configuration for a chat session
121
+ */
122
+ export interface GeminiChatConfig {
123
+ /** Model name override */
124
+ model?: string;
125
+ /** System instruction for the model */
126
+ systemInstruction?: string;
127
+ /** Safety settings (defaults to BLOCK_NONE for all categories) */
128
+ safetySettings?: GeminiSafetySetting[];
129
+ /** Generation config (temperature, maxOutputTokens, etc.) */
130
+ generationConfig?: GeminiGenerationConfig;
131
+ /** Initial conversation history */
132
+ history?: GeminiContent[];
133
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Domain Entities
3
+ */
4
+ export * from "./gemini.types";
5
+ export * from "./error.types";
6
+ export * from "./models";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Available Gemini AI models
3
+ */
4
+ export declare const GEMINI_MODELS: {
5
+ /** Text generation models */
6
+ readonly TEXT: {
7
+ /** Lightweight flash model for fast text generation */
8
+ readonly FLASH_LITE: "gemini-2.5-flash-lite";
9
+ /** Balanced flash model for general use */
10
+ readonly FLASH: "gemini-2.5-flash";
11
+ /** Premium model for complex tasks */
12
+ readonly PRO: "gemini-2.5-pro";
13
+ };
14
+ };
15
+ /**
16
+ * Default models to use for each category
17
+ */
18
+ export declare const DEFAULT_MODELS: {
19
+ /** Default model for text generation */
20
+ readonly TEXT: "gemini-2.5-flash-lite";
21
+ /** Default model for chat sessions */
22
+ readonly CHAT: "gemini-2.5-flash";
23
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @umituz/react-native-ai-gemini-provider
3
+ * Google Gemini AI provider for React Native applications
4
+ */
5
+ export type { GeminiConfig, GeminiGenerationConfig, GeminiHarmCategory, GeminiHarmBlockThreshold, GeminiContent, GeminiPart, GeminiInlineDataPart, GeminiMessagePart, GeminiSafetySetting, GeminiModelOptions, GeminiChatConfig, GeminiResponse, GeminiCandidate, GeminiFinishReason, GeminiSafetyRating, GeminiUsageMetadata, GeminiErrorInfo, GeminiApiError, } from "./domain/entities";
6
+ export { GeminiErrorType, GeminiError, GEMINI_MODELS, DEFAULT_MODELS } from "./domain/entities";
7
+ export { geminiClient } from "./infrastructure/services/GeminiClient";
8
+ export { createChatSession, sendChatMessage, buildChatHistory, trimChatHistory, resolveAudioMimeType, resolveImageMimeType, type ChatSendResult, type ChatHistoryMessage, type SendChatMessageOptions, } from "./infrastructure/services/ChatSession";
9
+ export { textGeneration } from "./infrastructure/services/TextGeneration";
10
+ export { structuredText } from "./infrastructure/services/StructuredText";
11
+ export { streaming } from "./infrastructure/services/Streaming";
12
+ export { geminiProvider, GeminiProvider } from "./infrastructure/services/GeminiProvider";
13
+ export { useGemini } from "./presentation/hooks";
14
+ export type { UseGeminiOptions, UseGeminiReturn } from "./presentation/hooks";
15
+ export { ConfigBuilder, providerFactory } from "./providers";
16
+ export type { ProviderConfig, ProviderFactoryOptions, } from "./providers";
@@ -0,0 +1,34 @@
1
+ import type { GeminiContent, GeminiGenerationConfig } from "../../domain/entities";
2
+ import type { GenerativeModel } from "@google/generative-ai";
3
+ export interface BaseRequestOptions {
4
+ model: string;
5
+ contents: GeminiContent[];
6
+ generationConfig?: GeminiGenerationConfig;
7
+ signal?: AbortSignal;
8
+ }
9
+ export declare abstract class BaseGeminiService {
10
+ protected validateAndPrepare(options: BaseRequestOptions): {
11
+ genModel: GenerativeModel;
12
+ sdkContents: Array<{
13
+ role: string;
14
+ parts: Array<{
15
+ text: string;
16
+ }>;
17
+ }>;
18
+ };
19
+ protected handleError(error: unknown, abortMessage: string): never;
20
+ protected createRequestOptions(sdkContents: Array<{
21
+ role: string;
22
+ parts: Array<{
23
+ text: string;
24
+ }>;
25
+ }>, generationConfig?: GeminiGenerationConfig): {
26
+ contents: {
27
+ role: string;
28
+ parts: Array<{
29
+ text: string;
30
+ }>;
31
+ }[];
32
+ generationConfig: GeminiGenerationConfig | undefined;
33
+ };
34
+ }
@@ -0,0 +1,67 @@
1
+ import type { GeminiChatConfig, GeminiGenerationConfig, GeminiContent, GeminiInlineDataPart, GeminiMessagePart, GeminiSafetySetting } from "../../domain/entities";
2
+ export interface ChatSendResult {
3
+ text: string;
4
+ finishReason?: string;
5
+ }
6
+ /** A simple chat message (user/assistant/system) */
7
+ export interface ChatHistoryMessage {
8
+ readonly role: "user" | "assistant" | "system";
9
+ readonly content: string;
10
+ }
11
+ /** Options for the high-level sendChatMessage() */
12
+ export interface SendChatMessageOptions {
13
+ /** Conversation history */
14
+ history: ChatHistoryMessage[];
15
+ /** Current user message text */
16
+ message: string;
17
+ /** System instruction for the model */
18
+ systemPrompt?: string;
19
+ /** Model name (defaults to gemini-2.5-flash) */
20
+ model?: string;
21
+ /** Generation config (temperature, maxOutputTokens, etc.) */
22
+ generationConfig?: GeminiGenerationConfig;
23
+ /** Safety settings (defaults to BLOCK_NONE for all) */
24
+ safetySettings?: GeminiSafetySetting[];
25
+ /** Pre-built attachments (base64 inline data parts for images/audio) */
26
+ attachments?: GeminiInlineDataPart[];
27
+ /** Max character budget for history trimming (default 12000) */
28
+ historyMaxChars?: number;
29
+ /** Min messages to always keep regardless of budget (default 4) */
30
+ historyMinMessages?: number;
31
+ }
32
+ /** Resolve MIME type for an audio file extension */
33
+ export declare function resolveAudioMimeType(extension: string): string;
34
+ /** Resolve MIME type for an image file extension */
35
+ export declare function resolveImageMimeType(extension: string): string;
36
+ /**
37
+ * Converts chat messages (user/assistant/system) to Gemini SDK content format.
38
+ * Skips system messages, merges consecutive same-role messages.
39
+ */
40
+ export declare function buildChatHistory(history: readonly ChatHistoryMessage[]): GeminiContent[];
41
+ /**
42
+ * Trims conversation history to fit within a character budget.
43
+ * Keeps at least `minMessages` entries regardless of budget.
44
+ */
45
+ export declare function trimChatHistory(history: ChatHistoryMessage[], maxChars?: number, minMessages?: number): ChatHistoryMessage[];
46
+ /**
47
+ * Creates a Gemini chat session with full support for system instructions,
48
+ * safety settings, generation config, and multi-turn conversation history.
49
+ */
50
+ export declare function createChatSession(config?: GeminiChatConfig): {
51
+ send(parts: GeminiMessagePart[]): Promise<ChatSendResult>;
52
+ };
53
+ /**
54
+ * All-in-one: trims history, builds session, sends message, handles safety.
55
+ * Returns the AI response text.
56
+ *
57
+ * ```ts
58
+ * const text = await sendChatMessage({
59
+ * history: messages,
60
+ * message: "hello",
61
+ * systemPrompt: "You are Aria...",
62
+ * model: GEMINI_MODELS.TEXT.FLASH,
63
+ * generationConfig: { temperature: 0.7, maxOutputTokens: 512 },
64
+ * });
65
+ * ```
66
+ */
67
+ export declare function sendChatMessage(opts: SendChatMessageOptions): Promise<string>;
@@ -0,0 +1,19 @@
1
+ import { GoogleGenerativeAI, type GenerativeModel } from "@google/generative-ai";
2
+ import type { GeminiConfig, GeminiModelOptions } from "../../domain/entities";
3
+ declare class GeminiClient {
4
+ private client;
5
+ private config;
6
+ private initialized;
7
+ initialize(config: GeminiConfig): void;
8
+ isInitialized(): boolean;
9
+ getConfig(): GeminiConfig | null;
10
+ getClient(): GoogleGenerativeAI | null;
11
+ /**
12
+ * Returns a GenerativeModel configured with optional safety settings and system instruction.
13
+ * When no safety settings are provided, defaults to BLOCK_NONE for all categories.
14
+ */
15
+ getModel(modelNameOrOptions?: string | GeminiModelOptions): GenerativeModel;
16
+ reset(): void;
17
+ }
18
+ export declare const geminiClient: GeminiClient;
19
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { GeminiConfig } from "../../domain/entities";
2
+ export declare class GeminiProvider {
3
+ readonly providerId = "gemini";
4
+ readonly providerName = "Google Gemini";
5
+ initialize(config: GeminiConfig): void;
6
+ isInitialized(): boolean;
7
+ reset(): void;
8
+ generateStructuredText<T>(prompt: string, schema: Record<string, unknown>, model: string): Promise<T>;
9
+ }
10
+ export declare const geminiProvider: GeminiProvider;
@@ -0,0 +1,7 @@
1
+ import { BaseGeminiService } from "./BaseService";
2
+ import type { GeminiContent, GeminiGenerationConfig } from "../../domain/entities";
3
+ declare class StreamingService extends BaseGeminiService {
4
+ streamContent(model: string, contents: GeminiContent[], onChunk: (text: string) => void, generationConfig?: GeminiGenerationConfig, signal?: AbortSignal): Promise<string>;
5
+ }
6
+ export declare const streaming: StreamingService;
7
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { GeminiGenerationConfig } from "../../domain/entities";
2
+ declare class StructuredTextService {
3
+ generateStructuredText<T>(model: string, prompt: string, schema: Record<string, unknown>, config?: Omit<GeminiGenerationConfig, "responseMimeType" | "responseSchema">, signal?: AbortSignal): Promise<T>;
4
+ private parseJSONResponse;
5
+ }
6
+ export declare const structuredText: StructuredTextService;
7
+ export {};
@@ -0,0 +1,8 @@
1
+ import { BaseGeminiService } from "./BaseService";
2
+ import type { GeminiContent, GeminiGenerationConfig, GeminiResponse } from "../../domain/entities";
3
+ declare class TextGenerationService extends BaseGeminiService {
4
+ generateContent(model: string, contents: GeminiContent[], generationConfig?: GeminiGenerationConfig, signal?: AbortSignal): Promise<GeminiResponse>;
5
+ generateText(model: string, prompt: string, config?: GeminiGenerationConfig, signal?: AbortSignal): Promise<string>;
6
+ }
7
+ export declare const textGeneration: TextGenerationService;
8
+ export {};
@@ -0,0 +1,6 @@
1
+ export { geminiClient } from "./GeminiClient";
2
+ export { textGeneration } from "./TextGeneration";
3
+ export { structuredText } from "./StructuredText";
4
+ export { streaming } from "./Streaming";
5
+ export { geminiProvider, GeminiProvider } from "./GeminiProvider";
6
+ export { createChatSession, sendChatMessage, buildChatHistory, trimChatHistory, resolveAudioMimeType, resolveImageMimeType, type ChatSendResult, type ChatHistoryMessage, type SendChatMessageOptions, } from "./ChatSession";
@@ -0,0 +1,57 @@
1
+ export interface TelemetryEvent {
2
+ type: "request" | "response" | "error" | "retry";
3
+ timestamp: number;
4
+ model?: string;
5
+ feature?: string;
6
+ duration?: number;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+ export type TelemetryListener = (event: TelemetryEvent) => void;
10
+ declare class TelemetryHooks {
11
+ private listeners;
12
+ private failedListeners;
13
+ private readonly MAX_FAILURES;
14
+ private listenerFailureCounts;
15
+ /**
16
+ * Register a telemetry listener
17
+ */
18
+ subscribe(listener: TelemetryListener): () => void;
19
+ /**
20
+ * Emit a telemetry event to all listeners
21
+ */
22
+ emit(event: TelemetryEvent): void;
23
+ /**
24
+ * Log request start
25
+ */
26
+ logRequest(model: string, feature?: string): number;
27
+ /**
28
+ * Log response received
29
+ */
30
+ logResponse(model: string, startTime: number, feature?: string, metadata?: Record<string, unknown>): void;
31
+ /**
32
+ * Log error
33
+ */
34
+ logError(model: string, error: Error, feature?: string): void;
35
+ /**
36
+ * Log retry attempt
37
+ */
38
+ logRetry(model: string, attempt: number, feature?: string): void;
39
+ /**
40
+ * Remove a specific listener
41
+ */
42
+ unsubscribe(listener: TelemetryListener): void;
43
+ /**
44
+ * Reset failure counts for all listeners (clear blacklist)
45
+ */
46
+ resetFailures(): void;
47
+ /**
48
+ * Clear all listeners
49
+ */
50
+ clear(): void;
51
+ /**
52
+ * Get current listener count
53
+ */
54
+ getListenerCount(): number;
55
+ }
56
+ export declare const telemetryHooks: TelemetryHooks;
57
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Telemetry Module
3
+ */
4
+ export { telemetryHooks } from "./TelemetryHooks";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Async State Execution Utilities
3
+ * Utilities for managing asynchronous operation state
4
+ */
5
+ /**
6
+ * Callbacks for async operation outcomes
7
+ */
8
+ export interface AsyncStateCallbacks<T = string> {
9
+ onSuccess?: (result: T) => void;
10
+ onError?: (error: string) => void;
11
+ }
12
+ /**
13
+ * Setter functions for updating state
14
+ */
15
+ export interface AsyncStateSetters<T = string, U = unknown> {
16
+ setIsLoading: (value: boolean) => void;
17
+ setError: (value: string | null) => void;
18
+ setResult: (value: T | null) => void;
19
+ setSecondaryResult?: (value: U | null) => void;
20
+ }
21
+ /**
22
+ * Configuration for executeWithState
23
+ */
24
+ export interface AsyncStateConfig<T = string> {
25
+ resetState?: boolean;
26
+ throwOnError?: boolean;
27
+ transformResult?: (result: T) => T;
28
+ }
29
+ /**
30
+ * Execute an async operation with automatic state management
31
+ *
32
+ * @param setters - State setter functions
33
+ * @param callbacks - Optional callbacks for success/error
34
+ * @param execute - The async operation to execute
35
+ * @param onResult - Function to handle successful result
36
+ * @param config - Optional configuration
37
+ *
38
+ * @returns The result or null if failed/aborted
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const result = await executeWithState(
43
+ * { setIsLoading, setError, setResult },
44
+ * { onSuccess: console.log },
45
+ * () => apiCall(),
46
+ * (data) => setResult(data)
47
+ * );
48
+ * ```
49
+ */
50
+ export declare function executeWithState<T, U = unknown>(setters: AsyncStateSetters<T, U>, callbacks: AsyncStateCallbacks<T>, execute: () => Promise<T>, onResult: (result: T) => void, config?: AsyncStateConfig<T>): Promise<T | null>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Async State Management - Internal Use Only
3
+ */
4
+ export { executeWithState, type AsyncStateSetters, } from "./execute-state.util";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Content Mapper Utilities
3
+ * Handles transformation between domain content and SDK format
4
+ */
5
+ import type { GeminiContent, GeminiPart, GeminiResponse } from "../../domain/entities";
6
+ /**
7
+ * Convert domain content to SDK format
8
+ */
9
+ export declare function toSdkContent(contents: GeminiContent[]): Array<{
10
+ role: string;
11
+ parts: Array<{
12
+ text: string;
13
+ }>;
14
+ }>;
15
+ /**
16
+ * Create a simple text content
17
+ */
18
+ export declare function createTextContent(text: string, role?: "user" | "model"): GeminiContent;
19
+ /**
20
+ * Transform SDK response to domain format
21
+ */
22
+ export declare function transformResponse(response: {
23
+ candidates?: Array<{
24
+ content: {
25
+ parts: Array<{
26
+ text?: string;
27
+ }>;
28
+ role?: string;
29
+ };
30
+ finishReason?: string;
31
+ safetyRatings?: Array<{
32
+ category: string;
33
+ probability: string;
34
+ }>;
35
+ }>;
36
+ usageMetadata?: {
37
+ promptTokenCount?: number;
38
+ candidatesTokenCount?: number;
39
+ totalTokenCount?: number;
40
+ };
41
+ }): GeminiResponse;
42
+ /**
43
+ * Extract text from content parts
44
+ */
45
+ export declare function extractTextFromParts(parts: GeminiPart[]): string;
@@ -0,0 +1,2 @@
1
+ import { GeminiError } from "../../domain/entities";
2
+ export declare function createGeminiError(error: unknown): GeminiError;
@@ -0,0 +1,2 @@
1
+ import type { GeminiResponse } from "../../domain/entities";
2
+ export declare function extractTextFromResponse(response: GeminiResponse): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * JSON Parser Utilities
3
+ * Handles cleaning and parsing JSON responses from AI models
4
+ */
5
+ /**
6
+ * Parse JSON response with error handling
7
+ * @throws Error if parsing fails with detailed error message
8
+ */
9
+ export declare function parseJsonResponse<T>(text: string): T;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Stream Processing Utilities
3
+ * Reusable stream handling logic
4
+ */
5
+ interface StreamChunk {
6
+ text: () => string;
7
+ }
8
+ type ChunkCallback = (text: string) => void;
9
+ type ErrorLogger = (error: unknown, context?: string) => void;
10
+ /**
11
+ * Process async stream with chunk callback
12
+ */
13
+ export declare function processStream(stream: AsyncIterable<StreamChunk>, onChunk: ChunkCallback, onError?: ErrorLogger): Promise<string>;
14
+ export {};
@@ -0,0 +1 @@
1
+ export { useGemini, type UseGeminiOptions, type UseGeminiReturn } from "./useGemini";
@@ -0,0 +1,17 @@
1
+ import type { GeminiGenerationConfig } from "../../domain/entities";
2
+ export interface UseGeminiOptions {
3
+ model?: string;
4
+ generationConfig?: GeminiGenerationConfig;
5
+ onSuccess?: (result: string) => void;
6
+ onError?: (error: string) => void;
7
+ }
8
+ export interface UseGeminiReturn {
9
+ generate: (prompt: string) => Promise<void>;
10
+ generateJSON: <T>(prompt: string, schema?: Record<string, unknown>) => Promise<T | null>;
11
+ result: string | null;
12
+ jsonResult: unknown;
13
+ isGenerating: boolean;
14
+ error: string | null;
15
+ reset: () => void;
16
+ }
17
+ export declare function useGemini(options?: UseGeminiOptions): UseGeminiReturn;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Operation Manager Hook
3
+ * Reusable abort controller and operation ID management
4
+ * Eliminates code duplication in hooks
5
+ */
6
+ export interface OperationManager {
7
+ /**
8
+ * Execute an operation with abort support and operation ID tracking
9
+ */
10
+ executeOperation: <T>(operation: (signal: AbortSignal, operationId: number) => Promise<T>) => Promise<T>;
11
+ /**
12
+ * Abort current operation
13
+ */
14
+ abort: () => void;
15
+ /**
16
+ * Check if current operation is active
17
+ */
18
+ isOperationActive: (operationId: number) => boolean;
19
+ }
20
+ /**
21
+ * Hook for managing operations with abort control
22
+ */
23
+ export declare function useOperationManager(): OperationManager;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Config Builder Pattern
3
+ * Fluent API for building provider configuration
4
+ */
5
+ import type { GeminiConfig } from "../domain/entities";
6
+ export interface ProviderConfig {
7
+ apiKey: string;
8
+ textModel: string;
9
+ timeout: number;
10
+ strategy?: "cost" | "quality";
11
+ }
12
+ /**
13
+ * Builder for constructing provider configuration
14
+ * Provides a fluent API with validation and defaults
15
+ */
16
+ export declare class ConfigBuilder {
17
+ private config;
18
+ /**
19
+ * Set API key (required)
20
+ */
21
+ withApiKey(apiKey: string): this;
22
+ /**
23
+ * Set text model
24
+ */
25
+ withTextModel(model: string): this;
26
+ /**
27
+ * Set request timeout (ms)
28
+ */
29
+ withTimeout(timeout: number): this;
30
+ /**
31
+ * Set strategy (applies preset timeout)
32
+ */
33
+ withStrategy(strategy: "cost" | "quality"): this;
34
+ /**
35
+ * Build final configuration
36
+ */
37
+ build(): ProviderConfig;
38
+ /**
39
+ * Convert to GeminiConfig format
40
+ */
41
+ toGeminiConfig(): GeminiConfig;
42
+ /**
43
+ * Create a new builder instance
44
+ */
45
+ static create(): ConfigBuilder;
46
+ /**
47
+ * Create from existing config (for updates)
48
+ */
49
+ static from(config: Partial<ProviderConfig>): ConfigBuilder;
50
+ }
@@ -0,0 +1,31 @@
1
+ import { type ProviderConfig } from "./ConfigBuilder";
2
+ export { ConfigBuilder } from "./ConfigBuilder";
3
+ export type { ProviderConfig } from "./ConfigBuilder";
4
+ export interface ProviderFactoryOptions {
5
+ apiKey: string;
6
+ timeout?: number;
7
+ textModel?: string;
8
+ strategy?: "cost" | "quality";
9
+ }
10
+ declare class ProviderFactory {
11
+ private currentConfig;
12
+ private builder;
13
+ /**
14
+ * Initialize provider with configuration
15
+ */
16
+ initialize(options: ProviderFactoryOptions): void;
17
+ /**
18
+ * Get current configuration
19
+ */
20
+ getConfig(): ProviderConfig | null;
21
+ /**
22
+ * Check if provider is initialized
23
+ */
24
+ isInitialized(): boolean;
25
+ /**
26
+ * Update configuration
27
+ * API key changes require re-initialization
28
+ */
29
+ updateConfig(updates: Partial<ProviderFactoryOptions>): void;
30
+ }
31
+ export declare const providerFactory: ProviderFactory;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Provider Configuration & Factory - Public API
3
+ */
4
+ export { ConfigBuilder } from "./ConfigBuilder";
5
+ export type { ProviderConfig } from "./ConfigBuilder";
6
+ export { providerFactory } from "./ProviderFactory";
7
+ export type { ProviderFactoryOptions } from "./ProviderFactory";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-gemini-provider",
3
- "version": "3.0.21",
3
+ "version": "3.0.23",
4
4
  "description": "Google Gemini AI text generation provider for React Native applications",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
package/src/index.ts CHANGED
@@ -34,7 +34,17 @@ export {
34
34
 
35
35
  // Services
36
36
  export { geminiClient } from "./infrastructure/services/GeminiClient";
37
- export { createChatSession } from "./infrastructure/services/ChatSession";
37
+ export {
38
+ createChatSession,
39
+ sendChatMessage,
40
+ buildChatHistory,
41
+ trimChatHistory,
42
+ resolveAudioMimeType,
43
+ resolveImageMimeType,
44
+ type ChatSendResult,
45
+ type ChatHistoryMessage,
46
+ type SendChatMessageOptions,
47
+ } from "./infrastructure/services/ChatSession";
38
48
  export { textGeneration } from "./infrastructure/services/TextGeneration";
39
49
  export { structuredText } from "./infrastructure/services/StructuredText";
40
50
  export { streaming } from "./infrastructure/services/Streaming";
@@ -3,25 +3,133 @@ import { geminiClient } from "./GeminiClient";
3
3
  import { DEFAULT_MODELS } from "../../domain/entities";
4
4
  import type {
5
5
  GeminiChatConfig,
6
+ GeminiGenerationConfig,
6
7
  GeminiContent,
8
+ GeminiInlineDataPart,
7
9
  GeminiMessagePart,
10
+ GeminiSafetySetting,
8
11
  } from "../../domain/entities";
9
12
 
13
+ // ─── Types ───────────────────────────────────────────────────────────────────
14
+
15
+ export interface ChatSendResult {
16
+ text: string;
17
+ finishReason?: string;
18
+ }
19
+
20
+ /** A simple chat message (user/assistant/system) */
21
+ export interface ChatHistoryMessage {
22
+ readonly role: "user" | "assistant" | "system";
23
+ readonly content: string;
24
+ }
25
+
26
+ /** Options for the high-level sendChatMessage() */
27
+ export interface SendChatMessageOptions {
28
+ /** Conversation history */
29
+ history: ChatHistoryMessage[];
30
+ /** Current user message text */
31
+ message: string;
32
+ /** System instruction for the model */
33
+ systemPrompt?: string;
34
+ /** Model name (defaults to gemini-2.5-flash) */
35
+ model?: string;
36
+ /** Generation config (temperature, maxOutputTokens, etc.) */
37
+ generationConfig?: GeminiGenerationConfig;
38
+ /** Safety settings (defaults to BLOCK_NONE for all) */
39
+ safetySettings?: GeminiSafetySetting[];
40
+ /** Pre-built attachments (base64 inline data parts for images/audio) */
41
+ attachments?: GeminiInlineDataPart[];
42
+ /** Max character budget for history trimming (default 12000) */
43
+ historyMaxChars?: number;
44
+ /** Min messages to always keep regardless of budget (default 4) */
45
+ historyMinMessages?: number;
46
+ }
47
+
48
+ // ─── MIME Utilities ──────────────────────────────────────────────────────────
49
+
50
+ const AUDIO_MIME: Record<string, string> = {
51
+ mp3: "audio/mpeg",
52
+ wav: "audio/wav",
53
+ ogg: "audio/ogg",
54
+ flac: "audio/flac",
55
+ aac: "audio/aac",
56
+ mp4: "audio/mp4",
57
+ m4a: "audio/mp4",
58
+ caf: "audio/mp4",
59
+ "3gp": "audio/3gpp",
60
+ };
61
+
62
+ /** Resolve MIME type for an audio file extension */
63
+ export function resolveAudioMimeType(extension: string): string {
64
+ return AUDIO_MIME[extension.toLowerCase()] ?? "audio/mp4";
65
+ }
66
+
67
+ /** Resolve MIME type for an image file extension */
68
+ export function resolveImageMimeType(extension: string): string {
69
+ return extension.toLowerCase() === "png" ? "image/png" : "image/jpeg";
70
+ }
71
+
72
+ // ─── History Utilities ───────────────────────────────────────────────────────
73
+
74
+ /**
75
+ * Converts chat messages (user/assistant/system) to Gemini SDK content format.
76
+ * Skips system messages, merges consecutive same-role messages.
77
+ */
78
+ export function buildChatHistory(
79
+ history: readonly ChatHistoryMessage[],
80
+ ): GeminiContent[] {
81
+ const result: GeminiContent[] = [];
82
+ let seenUser = false;
83
+
84
+ for (const m of history) {
85
+ if (m.role === "system") continue;
86
+ if (!seenUser && m.role !== "user") continue;
87
+ seenUser = true;
88
+
89
+ const role = m.role === "assistant" ? "model" : "user";
90
+ const last = result[result.length - 1];
91
+
92
+ if (last && last.role === role) {
93
+ const existingText =
94
+ "text" in last.parts[0] ? (last.parts[0].text ?? "") : "";
95
+ last.parts[0] = { text: existingText + "\n" + m.content };
96
+ } else {
97
+ result.push({ role, parts: [{ text: m.content }] });
98
+ }
99
+ }
100
+
101
+ return result;
102
+ }
103
+
104
+ /**
105
+ * Trims conversation history to fit within a character budget.
106
+ * Keeps at least `minMessages` entries regardless of budget.
107
+ */
108
+ export function trimChatHistory(
109
+ history: ChatHistoryMessage[],
110
+ maxChars = 12000,
111
+ minMessages = 4,
112
+ ): ChatHistoryMessage[] {
113
+ if (history.length <= minMessages) return history;
114
+
115
+ let totalChars = 0;
116
+ const trimmed: ChatHistoryMessage[] = [];
117
+
118
+ for (let i = history.length - 1; i >= 0; i--) {
119
+ const chars = history[i].content.length;
120
+ if (trimmed.length >= minMessages && totalChars + chars > maxChars) break;
121
+ trimmed.unshift(history[i]);
122
+ totalChars += chars;
123
+ }
124
+
125
+ return trimmed;
126
+ }
127
+
128
+ // ─── Low-level: createChatSession ────────────────────────────────────────────
129
+
10
130
  /**
11
131
  * Creates a Gemini chat session with full support for system instructions,
12
132
  * safety settings, generation config, and multi-turn conversation history.
13
- *
14
- * Usage:
15
- * ```ts
16
- * const session = createChatSession({
17
- * model: "gemini-2.5-flash",
18
- * systemInstruction: "You are a helpful assistant.",
19
- * generationConfig: { temperature: 0.7, maxOutputTokens: 512 },
20
- * history: geminiHistory,
21
- * });
22
- *
23
- * const text = await session.send([{ text: "Hello" }]);
24
- * ```
25
133
  */
26
134
  export function createChatSession(config: GeminiChatConfig = {}) {
27
135
  const model = geminiClient.getModel({
@@ -41,14 +149,63 @@ export function createChatSession(config: GeminiChatConfig = {}) {
41
149
  });
42
150
 
43
151
  return {
44
- /**
45
- * Send a message and return the full response text.
46
- * Accepts text parts and/or inline data parts (images, audio).
47
- */
48
- async send(parts: GeminiMessagePart[]): Promise<string> {
152
+ async send(parts: GeminiMessagePart[]): Promise<ChatSendResult> {
49
153
  const result = await chat.sendMessage(parts as Part[]);
50
154
  if (!result.response) throw new Error("No response from Gemini SDK");
51
- return result.response.text();
155
+ const candidate = result.response.candidates?.[0];
156
+ return {
157
+ text: result.response.text(),
158
+ finishReason: candidate?.finishReason ?? undefined,
159
+ };
52
160
  },
53
161
  };
54
162
  }
163
+
164
+ // ─── High-level: sendChatMessage ─────────────────────────────────────────────
165
+
166
+ /**
167
+ * All-in-one: trims history, builds session, sends message, handles safety.
168
+ * Returns the AI response text.
169
+ *
170
+ * ```ts
171
+ * const text = await sendChatMessage({
172
+ * history: messages,
173
+ * message: "hello",
174
+ * systemPrompt: "You are Aria...",
175
+ * model: GEMINI_MODELS.TEXT.FLASH,
176
+ * generationConfig: { temperature: 0.7, maxOutputTokens: 512 },
177
+ * });
178
+ * ```
179
+ */
180
+ export async function sendChatMessage(
181
+ opts: SendChatMessageOptions,
182
+ ): Promise<string> {
183
+ const trimmed = trimChatHistory(
184
+ opts.history,
185
+ opts.historyMaxChars,
186
+ opts.historyMinMessages,
187
+ );
188
+ const geminiHistory = buildChatHistory(trimmed);
189
+
190
+ const session = createChatSession({
191
+ model: opts.model ?? DEFAULT_MODELS.CHAT,
192
+ systemInstruction: opts.systemPrompt,
193
+ safetySettings: opts.safetySettings,
194
+ generationConfig: opts.generationConfig,
195
+ history: geminiHistory,
196
+ });
197
+
198
+ const parts: GeminiMessagePart[] = [{ text: opts.message }];
199
+ if (opts.attachments) {
200
+ parts.push(...opts.attachments);
201
+ }
202
+
203
+ const result = await session.send(parts);
204
+
205
+ if (result.finishReason === "SAFETY") {
206
+ if (result.text.trim()) return result.text;
207
+ throw new Error("Response blocked by safety filter.");
208
+ }
209
+
210
+ return result.text;
211
+ }
@@ -3,4 +3,14 @@ export { textGeneration } from "./TextGeneration";
3
3
  export { structuredText } from "./StructuredText";
4
4
  export { streaming } from "./Streaming";
5
5
  export { geminiProvider, GeminiProvider } from "./GeminiProvider";
6
- export { createChatSession } from "./ChatSession";
6
+ export {
7
+ createChatSession,
8
+ sendChatMessage,
9
+ buildChatHistory,
10
+ trimChatHistory,
11
+ resolveAudioMimeType,
12
+ resolveImageMimeType,
13
+ type ChatSendResult,
14
+ type ChatHistoryMessage,
15
+ type SendChatMessageOptions,
16
+ } from "./ChatSession";