@umituz/react-native-ai-gemini-provider 3.0.41 → 3.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/application/builders/config-builder.ts +102 -0
- package/src/application/builders/index.ts +8 -0
- package/src/application/dtos/generation-request.dto.ts +89 -0
- package/src/application/dtos/index.ts +8 -0
- package/src/application/index.ts +16 -0
- package/src/application/providers/gemini-provider.ts +135 -0
- package/src/application/providers/index.ts +6 -0
- package/src/application/use-cases/generate-json.use-case.ts +73 -0
- package/src/application/use-cases/generate-text.use-case.ts +81 -0
- package/src/application/use-cases/index.ts +20 -0
- package/src/application/use-cases/stream-content.use-case.ts +46 -0
- package/src/domain/entities/error.types.ts +0 -5
- package/src/domain/entities/gemini.types.ts +3 -1
- package/src/domain/index.ts +16 -0
- package/src/domain/repositories/index.ts +19 -0
- package/src/domain/repositories/streaming.repository.ts +41 -0
- package/src/domain/repositories/structured-text.repository.ts +41 -0
- package/src/domain/repositories/text-generation.repository.ts +38 -0
- package/src/domain/services/validation.service.ts +157 -0
- package/src/domain/value-objects/api-key.vo.ts +55 -0
- package/src/domain/value-objects/index.ts +8 -0
- package/src/domain/value-objects/model-name.vo.ts +66 -0
- package/src/domain/value-objects/timeout.vo.ts +69 -0
- package/src/index.ts +110 -25
- package/src/infrastructure/external/gemini-client.singleton.ts +49 -0
- package/src/infrastructure/external/gemini-sdk.adapter.ts +143 -0
- package/src/infrastructure/external/index.ts +7 -0
- package/src/infrastructure/index.ts +16 -0
- package/src/infrastructure/mappers/content.mapper.ts +80 -0
- package/src/infrastructure/mappers/error.mapper.ts +152 -0
- package/src/infrastructure/mappers/index.ts +7 -0
- package/src/infrastructure/mappers/response.mapper.ts +165 -0
- package/src/infrastructure/repositories/base-gemini.repository.ts +94 -0
- package/src/infrastructure/repositories/gemini-streaming.repository.impl.ts +119 -0
- package/src/infrastructure/repositories/gemini-structured-text.repository.impl.ts +108 -0
- package/src/infrastructure/repositories/gemini-text.repository.impl.ts +76 -0
- package/src/infrastructure/repositories/index.ts +10 -0
- package/src/infrastructure/utils/index.ts +6 -0
- package/src/presentation/hooks/index.ts +8 -0
- package/src/presentation/hooks/use-gemini.hook.ts +181 -0
- package/src/presentation/hooks/use-operation-manager.hook.ts +67 -0
- package/src/presentation/index.ts +10 -0
- package/src/presentation/providers/gemini-provider.tsx +93 -0
- package/src/presentation/providers/index.ts +10 -0
- package/dist/domain/entities/error.types.d.ts +0 -96
- package/dist/domain/entities/gemini.types.d.ts +0 -128
- package/dist/domain/entities/index.d.ts +0 -6
- package/dist/domain/entities/models.d.ts +0 -23
- package/dist/index.d.ts +0 -15
- package/dist/infrastructure/services/BaseService.d.ts +0 -29
- package/dist/infrastructure/services/ChatSession.d.ts +0 -63
- package/dist/infrastructure/services/GeminiClient.d.ts +0 -16
- package/dist/infrastructure/services/GeminiProvider.d.ts +0 -10
- package/dist/infrastructure/services/Streaming.d.ts +0 -7
- package/dist/infrastructure/services/StructuredText.d.ts +0 -6
- package/dist/infrastructure/services/TextGeneration.d.ts +0 -8
- package/dist/infrastructure/services/index.d.ts +0 -6
- package/dist/infrastructure/telemetry/TelemetryHooks.d.ts +0 -41
- package/dist/infrastructure/telemetry/index.d.ts +0 -4
- package/dist/infrastructure/utils/async/execute-state.util.d.ts +0 -49
- package/dist/infrastructure/utils/async/index.d.ts +0 -4
- package/dist/infrastructure/utils/content-mapper.util.d.ts +0 -45
- package/dist/infrastructure/utils/error-mapper.util.d.ts +0 -2
- package/dist/infrastructure/utils/gemini-data-transformer.util.d.ts +0 -2
- package/dist/infrastructure/utils/json-parser.util.d.ts +0 -9
- package/dist/infrastructure/utils/stream-processor.util.d.ts +0 -14
- package/dist/presentation/hooks/index.d.ts +0 -1
- package/dist/presentation/hooks/useGemini.d.ts +0 -17
- package/dist/presentation/hooks/useOperationManager.d.ts +0 -23
- package/dist/providers/ConfigBuilder.d.ts +0 -46
- package/dist/providers/ProviderFactory.d.ts +0 -25
- package/dist/providers/index.d.ts +0 -7
- package/src/infrastructure/services/BaseService.ts +0 -53
- package/src/infrastructure/services/ChatSession.ts +0 -199
- package/src/infrastructure/services/GeminiClient.ts +0 -112
- package/src/infrastructure/services/Streaming.ts +0 -56
- package/src/infrastructure/services/StructuredText.ts +0 -57
- package/src/infrastructure/services/TextGeneration.ts +0 -57
- package/src/infrastructure/telemetry/TelemetryHooks.ts +0 -110
- package/src/infrastructure/utils/async/execute-state.util.ts +0 -93
- package/src/infrastructure/utils/content-mapper.util.ts +0 -175
- package/src/infrastructure/utils/error-mapper.util.ts +0 -145
- package/src/infrastructure/utils/gemini-data-transformer.util.ts +0 -40
- package/src/infrastructure/utils/text-calculations.util.ts +0 -51
- package/src/presentation/hooks/useGemini.ts +0 -125
- package/src/presentation/hooks/useOperationManager.ts +0 -88
- package/src/providers/ConfigBuilder.ts +0 -112
- package/src/providers/ProviderFactory.ts +0 -65
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming Repository Interface
|
|
3
|
+
* Domain layer contract for streaming operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { GeminiContent, GeminiGenerationConfig } from "../entities";
|
|
7
|
+
|
|
8
|
+
export interface StreamingRequest {
|
|
9
|
+
model: string;
|
|
10
|
+
contents: GeminiContent[];
|
|
11
|
+
onChunk: (text: string) => void;
|
|
12
|
+
generationConfig?: GeminiGenerationConfig;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IStreamingRepository {
|
|
17
|
+
/**
|
|
18
|
+
* Stream text content from Gemini API
|
|
19
|
+
* @param request - Streaming request parameters
|
|
20
|
+
* @returns Complete generated text
|
|
21
|
+
* @throws GeminiError on API errors
|
|
22
|
+
*/
|
|
23
|
+
stream(request: StreamingRequest): Promise<string>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Stream text from a simple prompt
|
|
27
|
+
* @param model - Model name
|
|
28
|
+
* @param prompt - Text prompt
|
|
29
|
+
* @param onChunk - Callback for each chunk
|
|
30
|
+
* @param config - Optional generation config
|
|
31
|
+
* @param signal - Optional abort signal
|
|
32
|
+
* @returns Complete generated text
|
|
33
|
+
*/
|
|
34
|
+
streamText(
|
|
35
|
+
model: string,
|
|
36
|
+
prompt: string,
|
|
37
|
+
onChunk: (text: string) => void,
|
|
38
|
+
config?: GeminiGenerationConfig,
|
|
39
|
+
signal?: AbortSignal
|
|
40
|
+
): Promise<string>;
|
|
41
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Text Repository Interface
|
|
3
|
+
* Domain layer contract for structured JSON generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { GeminiContent, GeminiGenerationConfig } from "../entities";
|
|
7
|
+
|
|
8
|
+
export interface StructuredGenerationRequest {
|
|
9
|
+
model: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
schema: Record<string, unknown>;
|
|
12
|
+
config?: Omit<GeminiGenerationConfig, "responseMimeType" | "responseSchema">;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IStructuredTextRepository {
|
|
17
|
+
/**
|
|
18
|
+
* Generate structured JSON response from Gemini API
|
|
19
|
+
* @param request - Structured generation request
|
|
20
|
+
* @returns Parsed JSON response
|
|
21
|
+
* @throws GeminiError on API errors
|
|
22
|
+
*/
|
|
23
|
+
generateStructured<T>(request: StructuredGenerationRequest): Promise<T>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate structured JSON from contents
|
|
27
|
+
* @param model - Model name
|
|
28
|
+
* @param contents - Content array
|
|
29
|
+
* @param schema - JSON schema
|
|
30
|
+
* @param config - Optional generation config
|
|
31
|
+
* @param signal - Optional abort signal
|
|
32
|
+
* @returns Parsed JSON response
|
|
33
|
+
*/
|
|
34
|
+
generateStructuredFromContents<T>(
|
|
35
|
+
model: string,
|
|
36
|
+
contents: GeminiContent[],
|
|
37
|
+
schema: Record<string, unknown>,
|
|
38
|
+
config?: GeminiGenerationConfig,
|
|
39
|
+
signal?: AbortSignal
|
|
40
|
+
): Promise<T>;
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Generation Repository Interface
|
|
3
|
+
* Domain layer contract for text generation operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { GeminiContent, GeminiGenerationConfig, GeminiResponse } from "../entities";
|
|
7
|
+
|
|
8
|
+
export interface TextGenerationRequest {
|
|
9
|
+
model: string;
|
|
10
|
+
contents: GeminiContent[];
|
|
11
|
+
generationConfig?: GeminiGenerationConfig;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ITextGenerationRepository {
|
|
16
|
+
/**
|
|
17
|
+
* Generate text content from Gemini API
|
|
18
|
+
* @param request - Generation request parameters
|
|
19
|
+
* @returns Generated response
|
|
20
|
+
* @throws GeminiError on API errors
|
|
21
|
+
*/
|
|
22
|
+
generate(request: TextGenerationRequest): Promise<GeminiResponse>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generate simple text from a prompt
|
|
26
|
+
* @param model - Model name
|
|
27
|
+
* @param prompt - Text prompt
|
|
28
|
+
* @param config - Optional generation config
|
|
29
|
+
* @param signal - Optional abort signal
|
|
30
|
+
* @returns Generated text
|
|
31
|
+
*/
|
|
32
|
+
generateText(
|
|
33
|
+
model: string,
|
|
34
|
+
prompt: string,
|
|
35
|
+
config?: GeminiGenerationConfig,
|
|
36
|
+
signal?: AbortSignal
|
|
37
|
+
): Promise<string>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Validation Service
|
|
3
|
+
* Pure validation functions for domain entities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { GeminiContent } from "../entities";
|
|
7
|
+
|
|
8
|
+
export class ValidationError extends Error {
|
|
9
|
+
constructor(message: string) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "ValidationError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class ValidationService {
|
|
16
|
+
/**
|
|
17
|
+
* Validate prompt text
|
|
18
|
+
*/
|
|
19
|
+
validatePrompt(prompt: string): void {
|
|
20
|
+
if (!prompt || typeof prompt !== "string") {
|
|
21
|
+
throw new ValidationError("Prompt must be a string");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const trimmed = prompt.trim();
|
|
25
|
+
if (trimmed.length < 3) {
|
|
26
|
+
throw new ValidationError("Prompt must be at least 3 characters");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (trimmed.length > 100000) {
|
|
30
|
+
throw new ValidationError("Prompt too long (max 100k characters)");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate JSON schema object
|
|
36
|
+
*/
|
|
37
|
+
validateSchema(schema: unknown): void {
|
|
38
|
+
if (!schema || typeof schema !== "object") {
|
|
39
|
+
throw new ValidationError("Schema must be an object");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (Array.isArray(schema)) {
|
|
43
|
+
throw new ValidationError("Schema cannot be an array");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const keys = Object.keys(schema);
|
|
47
|
+
if (keys.length === 0) {
|
|
48
|
+
throw new ValidationError("Schema cannot be empty");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Recursively validate nested schema properties
|
|
52
|
+
for (const value of Object.values(schema)) {
|
|
53
|
+
if (typeof value === "object" && value !== null) {
|
|
54
|
+
this.validateSchema(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validate contents array
|
|
61
|
+
*/
|
|
62
|
+
validateContents(contents: GeminiContent[]): void {
|
|
63
|
+
if (!Array.isArray(contents)) {
|
|
64
|
+
throw new ValidationError("Contents must be an array");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (contents.length === 0) {
|
|
68
|
+
throw new ValidationError("Contents cannot be empty");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (contents.length > 1000) {
|
|
72
|
+
throw new ValidationError("Too many content items (max 1000)");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Validate each content item
|
|
76
|
+
for (const content of contents) {
|
|
77
|
+
this.validateContent(content);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validate single content item
|
|
83
|
+
*/
|
|
84
|
+
private validateContent(content: GeminiContent): void {
|
|
85
|
+
if (!content.role || typeof content.role !== "string") {
|
|
86
|
+
throw new ValidationError("Content role is required");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!content.parts || !Array.isArray(content.parts)) {
|
|
90
|
+
throw new ValidationError("Content parts must be an array");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (content.parts.length === 0) {
|
|
94
|
+
throw new ValidationError("Content parts cannot be empty");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Validate each part
|
|
98
|
+
for (const part of content.parts) {
|
|
99
|
+
const hasText = "text" in part && typeof part.text === "string" && part.text.length > 0;
|
|
100
|
+
const hasInlineData = "inlineData" in part && part.inlineData !== null;
|
|
101
|
+
|
|
102
|
+
if (!hasText && !hasInlineData) {
|
|
103
|
+
throw new ValidationError("Each part must have text or inlineData");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Validate model name
|
|
110
|
+
*/
|
|
111
|
+
validateModelName(model: string): void {
|
|
112
|
+
if (!model || typeof model !== "string") {
|
|
113
|
+
throw new ValidationError("Model name is required");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!model.startsWith("gemini-")) {
|
|
117
|
+
throw new ValidationError("Model name must start with 'gemini-'");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Validate callback function
|
|
123
|
+
*/
|
|
124
|
+
validateCallback(callback: unknown, name: string): void {
|
|
125
|
+
if (typeof callback !== "function") {
|
|
126
|
+
throw new ValidationError(`${name} must be a function`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate generation config
|
|
132
|
+
*/
|
|
133
|
+
validateConfig(config: unknown): void {
|
|
134
|
+
if (!config) return; // Config is optional
|
|
135
|
+
|
|
136
|
+
if (typeof config !== "object" || Array.isArray(config)) {
|
|
137
|
+
throw new ValidationError("Config must be an object");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Validate temperature if present
|
|
141
|
+
const cfg = config as Record<string, unknown>;
|
|
142
|
+
if (cfg.temperature !== undefined) {
|
|
143
|
+
const temp = cfg.temperature as number;
|
|
144
|
+
if (typeof temp !== "number" || temp < 0 || temp > 2) {
|
|
145
|
+
throw new ValidationError("Temperature must be between 0 and 2");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Validate max output tokens if present
|
|
150
|
+
if (cfg.maxOutputTokens !== undefined) {
|
|
151
|
+
const tokens = cfg.maxOutputTokens as number;
|
|
152
|
+
if (typeof tokens !== "number" || tokens < 1 || tokens > 8192) {
|
|
153
|
+
throw new ValidationError("maxOutputTokens must be between 1 and 8192");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Value Object
|
|
3
|
+
* Encapsulates API key validation and formatting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class ApiKey {
|
|
7
|
+
private static readonly MIN_LENGTH = 10;
|
|
8
|
+
private static readonly PREFIX = "AIza";
|
|
9
|
+
|
|
10
|
+
private constructor(private readonly value: string) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a validated API key
|
|
14
|
+
* @throws Error if validation fails
|
|
15
|
+
*/
|
|
16
|
+
static create(value: string): ApiKey {
|
|
17
|
+
const trimmed = value?.trim() || "";
|
|
18
|
+
|
|
19
|
+
if (trimmed.length < ApiKey.MIN_LENGTH) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`API key must be at least ${ApiKey.MIN_LENGTH} characters`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!trimmed.startsWith(ApiKey.PREFIX)) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`API key must start with "${ApiKey.PREFIX}"`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return new ApiKey(trimmed);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the API key value
|
|
36
|
+
*/
|
|
37
|
+
getValue(): string {
|
|
38
|
+
return this.value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if API key equals another
|
|
43
|
+
*/
|
|
44
|
+
equals(other: ApiKey): boolean {
|
|
45
|
+
return this.value === other.value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get masked version for logging (e.g., "AIza...xyz")
|
|
50
|
+
*/
|
|
51
|
+
toMasked(): string {
|
|
52
|
+
if (this.value.length <= 10) return "***";
|
|
53
|
+
return `${this.value.substring(0, 6)}...${this.value.substring(this.value.length - 3)}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Name Value Object
|
|
3
|
+
* Encapsulates Gemini model name validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class ModelName {
|
|
7
|
+
private static readonly PREFIX = "gemini-";
|
|
8
|
+
|
|
9
|
+
private constructor(private readonly value: string) {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a validated model name
|
|
13
|
+
* @throws Error if validation fails
|
|
14
|
+
*/
|
|
15
|
+
static create(value: string): ModelName {
|
|
16
|
+
const trimmed = value?.trim() || "";
|
|
17
|
+
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
throw new Error("Model name cannot be empty");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!trimmed.startsWith(ModelName.PREFIX)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Model name must start with "${ModelName.PREFIX}"`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return new ModelName(trimmed);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the model name value
|
|
33
|
+
*/
|
|
34
|
+
getValue(): string {
|
|
35
|
+
return this.value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if this is a Flash model (faster, cheaper)
|
|
40
|
+
*/
|
|
41
|
+
isFlash(): boolean {
|
|
42
|
+
return this.value.includes("flash");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if this is a Pro model (higher quality)
|
|
47
|
+
*/
|
|
48
|
+
isPro(): boolean {
|
|
49
|
+
return this.value.includes("pro");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get model family (e.g., "gemini-2.5-flash" -> "2.5")
|
|
54
|
+
*/
|
|
55
|
+
getVersion(): string {
|
|
56
|
+
const match = this.value.match(/gemini-([\d.]+)/);
|
|
57
|
+
return match ? match[1] : "unknown";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if model name equals another
|
|
62
|
+
*/
|
|
63
|
+
equals(other: ModelName): boolean {
|
|
64
|
+
return this.value === other.value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout Value Object
|
|
3
|
+
* Encapsulates timeout duration validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class Timeout {
|
|
7
|
+
private static readonly MIN_MS = 1;
|
|
8
|
+
private static readonly MAX_MS = 300000; // 5 minutes
|
|
9
|
+
|
|
10
|
+
private constructor(private readonly valueMs: number) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a validated timeout in milliseconds
|
|
14
|
+
* @throws Error if validation fails
|
|
15
|
+
*/
|
|
16
|
+
static create(milliseconds: number): Timeout {
|
|
17
|
+
const value = milliseconds ?? 30000; // Default 30s
|
|
18
|
+
|
|
19
|
+
if (value < Timeout.MIN_MS || value > Timeout.MAX_MS) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Timeout must be between ${Timeout.MIN_MS}ms and ${Timeout.MAX_MS}ms`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return new Timeout(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create timeout in seconds
|
|
30
|
+
*/
|
|
31
|
+
static fromSeconds(seconds: number): Timeout {
|
|
32
|
+
return Timeout.create(seconds * 1000);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get timeout in milliseconds
|
|
37
|
+
*/
|
|
38
|
+
toMilliseconds(): number {
|
|
39
|
+
return this.valueMs;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get timeout in seconds
|
|
44
|
+
*/
|
|
45
|
+
toSeconds(): number {
|
|
46
|
+
return Math.round(this.valueMs / 1000);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if this is a short timeout (< 10s)
|
|
51
|
+
*/
|
|
52
|
+
isShort(): boolean {
|
|
53
|
+
return this.valueMs < 10000;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if this is a long timeout (> 60s)
|
|
58
|
+
*/
|
|
59
|
+
isLong(): boolean {
|
|
60
|
+
return this.valueMs > 60000;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if timeout equals another
|
|
65
|
+
*/
|
|
66
|
+
equals(other: Timeout): boolean {
|
|
67
|
+
return this.valueMs === other.valueMs;
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @umituz/react-native-ai-gemini-provider
|
|
3
3
|
* Google Gemini AI provider for React Native applications
|
|
4
|
+
*
|
|
5
|
+
* Clean DDD Architecture - Production Ready
|
|
6
|
+
*
|
|
7
|
+
* @version 4.0.0
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
|
-
//
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// DOMAIN LAYER - Core business logic and types
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
// Domain Entities
|
|
7
15
|
export type {
|
|
8
16
|
GeminiConfig,
|
|
9
17
|
GeminiGenerationConfig,
|
|
@@ -29,31 +37,108 @@ export {
|
|
|
29
37
|
GeminiErrorType,
|
|
30
38
|
GeminiError,
|
|
31
39
|
GEMINI_MODELS,
|
|
32
|
-
DEFAULT_MODELS
|
|
40
|
+
DEFAULT_MODELS,
|
|
33
41
|
} from "./domain/entities";
|
|
34
42
|
|
|
35
|
-
//
|
|
36
|
-
export {
|
|
43
|
+
// Value Objects
|
|
44
|
+
export { ApiKey, ModelName, Timeout } from "./domain/value-objects";
|
|
45
|
+
|
|
46
|
+
// Domain Services
|
|
47
|
+
export { ValidationService, ValidationError } from "./domain/services/validation.service";
|
|
48
|
+
|
|
49
|
+
// Repository Interfaces
|
|
50
|
+
export type {
|
|
51
|
+
ITextGenerationRepository,
|
|
52
|
+
TextGenerationRequest,
|
|
53
|
+
} from "./domain/repositories/text-generation.repository";
|
|
54
|
+
|
|
55
|
+
export type {
|
|
56
|
+
IStreamingRepository,
|
|
57
|
+
StreamingRequest,
|
|
58
|
+
} from "./domain/repositories/streaming.repository";
|
|
59
|
+
|
|
60
|
+
export type {
|
|
61
|
+
IStructuredTextRepository,
|
|
62
|
+
StructuredGenerationRequest,
|
|
63
|
+
} from "./domain/repositories/structured-text.repository";
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// APPLICATION LAYER - Use cases and orchestration
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
// Use Cases
|
|
37
70
|
export {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
type ChatHistoryMessage,
|
|
44
|
-
type SendChatMessageOptions,
|
|
45
|
-
} from "./infrastructure/services/ChatSession";
|
|
46
|
-
export { textGeneration } from "./infrastructure/services/TextGeneration";
|
|
47
|
-
export { structuredText } from "./infrastructure/services/StructuredText";
|
|
48
|
-
export { streaming } from "./infrastructure/services/Streaming";
|
|
49
|
-
|
|
50
|
-
// React Hook
|
|
51
|
-
export { useGemini } from "./presentation/hooks/useGemini";
|
|
52
|
-
export type { UseGeminiOptions, UseGeminiReturn } from "./presentation/hooks/useGemini";
|
|
53
|
-
|
|
54
|
-
// Provider Configuration & Factory
|
|
55
|
-
export { ConfigBuilder, providerFactory } from "./providers/ProviderFactory";
|
|
71
|
+
GenerateTextUseCase,
|
|
72
|
+
StreamContentUseCase,
|
|
73
|
+
GenerateJSONUseCase,
|
|
74
|
+
} from "./application/use-cases";
|
|
75
|
+
|
|
56
76
|
export type {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
GenerateTextOptions,
|
|
78
|
+
GenerateTextResult,
|
|
79
|
+
StreamContentOptions,
|
|
80
|
+
GenerateJSONOptions,
|
|
81
|
+
GenerateJSONResult,
|
|
82
|
+
} from "./application/use-cases";
|
|
83
|
+
|
|
84
|
+
// Builders
|
|
85
|
+
export { GeminiConfigBuilder } from "./application/builders";
|
|
86
|
+
|
|
87
|
+
export type { GeminiConfigOptions } from "./application/builders";
|
|
88
|
+
|
|
89
|
+
// Providers
|
|
90
|
+
export { geminiProvider, GeminiProviderClass } from "./application/providers";
|
|
91
|
+
|
|
92
|
+
export type { GeminiProvider } from "./application/providers";
|
|
93
|
+
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// INFRASTRUCTURE LAYER - External integrations
|
|
96
|
+
// ============================================================================
|
|
97
|
+
|
|
98
|
+
// Mappers
|
|
99
|
+
export {
|
|
100
|
+
ContentMapper,
|
|
101
|
+
ResponseMapper,
|
|
102
|
+
ErrorMapper,
|
|
103
|
+
} from "./infrastructure/mappers";
|
|
104
|
+
|
|
105
|
+
// SDK Adapter
|
|
106
|
+
export {
|
|
107
|
+
GeminiSDKAdapter,
|
|
108
|
+
GeminiClient,
|
|
109
|
+
geminiClient,
|
|
110
|
+
} from "./infrastructure/external";
|
|
111
|
+
|
|
112
|
+
// Repository Implementations
|
|
113
|
+
export {
|
|
114
|
+
BaseGeminiRepository,
|
|
115
|
+
GeminiTextRepository,
|
|
116
|
+
GeminiStreamingRepository,
|
|
117
|
+
GeminiStructuredTextRepository,
|
|
118
|
+
} from "./infrastructure/repositories";
|
|
119
|
+
|
|
120
|
+
// Utilities
|
|
121
|
+
export { parseJsonResponse } from "./infrastructure/utils/json-parser.util";
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// PRESENTATION LAYER - React hooks and providers
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
// React Hooks
|
|
128
|
+
export { useGemini, useOperationManager } from "./presentation/hooks";
|
|
129
|
+
|
|
130
|
+
export type {
|
|
131
|
+
UseGeminiOptions,
|
|
132
|
+
UseGeminiReturn,
|
|
133
|
+
} from "./presentation/hooks";
|
|
134
|
+
|
|
135
|
+
export type { OperationManager } from "./presentation/hooks/use-operation-manager.hook";
|
|
136
|
+
|
|
137
|
+
// React Provider Component
|
|
138
|
+
export {
|
|
139
|
+
GeminiProviderComponent,
|
|
140
|
+
useGeminiContext,
|
|
141
|
+
useGeminiInitializer,
|
|
142
|
+
} from "./presentation/providers";
|
|
143
|
+
|
|
144
|
+
export type { GeminiProviderProps } from "./presentation/providers";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Client Singleton
|
|
3
|
+
* Global access point for Gemini SDK adapter
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { GeminiSDKAdapter } from "./gemini-sdk.adapter";
|
|
7
|
+
|
|
8
|
+
let instance: GeminiSDKAdapter | null = null;
|
|
9
|
+
|
|
10
|
+
export class GeminiClient {
|
|
11
|
+
private constructor() {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get the singleton instance
|
|
15
|
+
*/
|
|
16
|
+
static getInstance(): GeminiSDKAdapter {
|
|
17
|
+
if (!instance) {
|
|
18
|
+
instance = new GeminiSDKAdapter();
|
|
19
|
+
}
|
|
20
|
+
return instance;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Export adapter class for type safety
|
|
25
|
+
*/
|
|
26
|
+
static Adapter = GeminiSDKAdapter;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the client (convenience method)
|
|
30
|
+
*/
|
|
31
|
+
static initialize(apiKey: string): void {
|
|
32
|
+
GeminiClient.getInstance().initialize(apiKey);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reset the client (for testing)
|
|
37
|
+
*/
|
|
38
|
+
static reset(): void {
|
|
39
|
+
if (instance) {
|
|
40
|
+
instance.reset();
|
|
41
|
+
instance = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Export a simpler interface for backward compatibility
|
|
48
|
+
*/
|
|
49
|
+
export const geminiClient = GeminiClient.getInstance();
|