@umituz/react-native-ai-gemini-provider 3.0.40 → 3.0.42

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 (89) hide show
  1. package/package.json +1 -4
  2. package/src/application/builders/config-builder.ts +102 -0
  3. package/src/application/builders/index.ts +8 -0
  4. package/src/application/dtos/generation-request.dto.ts +89 -0
  5. package/src/application/dtos/index.ts +8 -0
  6. package/src/application/index.ts +16 -0
  7. package/src/application/providers/gemini-provider.ts +135 -0
  8. package/src/application/providers/index.ts +6 -0
  9. package/src/application/use-cases/generate-json.use-case.ts +73 -0
  10. package/src/application/use-cases/generate-text.use-case.ts +81 -0
  11. package/src/application/use-cases/index.ts +20 -0
  12. package/src/application/use-cases/stream-content.use-case.ts +46 -0
  13. package/src/domain/entities/error.types.ts +0 -5
  14. package/src/domain/entities/gemini.types.ts +3 -1
  15. package/src/domain/index.ts +16 -0
  16. package/src/domain/repositories/index.ts +19 -0
  17. package/src/domain/repositories/streaming.repository.ts +41 -0
  18. package/src/domain/repositories/structured-text.repository.ts +41 -0
  19. package/src/domain/repositories/text-generation.repository.ts +38 -0
  20. package/src/domain/services/validation.service.ts +157 -0
  21. package/src/domain/value-objects/api-key.vo.ts +55 -0
  22. package/src/domain/value-objects/index.ts +8 -0
  23. package/src/domain/value-objects/model-name.vo.ts +66 -0
  24. package/src/domain/value-objects/timeout.vo.ts +69 -0
  25. package/src/index.ts +110 -25
  26. package/src/infrastructure/external/gemini-client.singleton.ts +49 -0
  27. package/src/infrastructure/external/gemini-sdk.adapter.ts +143 -0
  28. package/src/infrastructure/external/index.ts +7 -0
  29. package/src/infrastructure/index.ts +16 -0
  30. package/src/infrastructure/mappers/content.mapper.ts +80 -0
  31. package/src/infrastructure/mappers/error.mapper.ts +152 -0
  32. package/src/infrastructure/mappers/index.ts +7 -0
  33. package/src/infrastructure/mappers/response.mapper.ts +165 -0
  34. package/src/infrastructure/repositories/base-gemini.repository.ts +94 -0
  35. package/src/infrastructure/repositories/gemini-streaming.repository.impl.ts +119 -0
  36. package/src/infrastructure/repositories/gemini-structured-text.repository.impl.ts +108 -0
  37. package/src/infrastructure/repositories/gemini-text.repository.impl.ts +76 -0
  38. package/src/infrastructure/repositories/index.ts +10 -0
  39. package/src/infrastructure/utils/index.ts +6 -0
  40. package/src/presentation/hooks/index.ts +8 -0
  41. package/src/presentation/hooks/use-gemini.hook.ts +181 -0
  42. package/src/presentation/hooks/use-operation-manager.hook.ts +67 -0
  43. package/src/presentation/index.ts +10 -0
  44. package/src/presentation/providers/gemini-provider.tsx +93 -0
  45. package/src/presentation/providers/index.ts +10 -0
  46. package/dist/domain/entities/error.types.d.ts +0 -96
  47. package/dist/domain/entities/gemini.types.d.ts +0 -128
  48. package/dist/domain/entities/index.d.ts +0 -6
  49. package/dist/domain/entities/models.d.ts +0 -23
  50. package/dist/index.d.ts +0 -15
  51. package/dist/infrastructure/services/BaseService.d.ts +0 -29
  52. package/dist/infrastructure/services/ChatSession.d.ts +0 -63
  53. package/dist/infrastructure/services/GeminiClient.d.ts +0 -16
  54. package/dist/infrastructure/services/GeminiProvider.d.ts +0 -10
  55. package/dist/infrastructure/services/Streaming.d.ts +0 -7
  56. package/dist/infrastructure/services/StructuredText.d.ts +0 -6
  57. package/dist/infrastructure/services/TextGeneration.d.ts +0 -8
  58. package/dist/infrastructure/services/index.d.ts +0 -6
  59. package/dist/infrastructure/telemetry/TelemetryHooks.d.ts +0 -41
  60. package/dist/infrastructure/telemetry/index.d.ts +0 -4
  61. package/dist/infrastructure/utils/async/execute-state.util.d.ts +0 -49
  62. package/dist/infrastructure/utils/async/index.d.ts +0 -4
  63. package/dist/infrastructure/utils/content-mapper.util.d.ts +0 -45
  64. package/dist/infrastructure/utils/error-mapper.util.d.ts +0 -2
  65. package/dist/infrastructure/utils/gemini-data-transformer.util.d.ts +0 -2
  66. package/dist/infrastructure/utils/json-parser.util.d.ts +0 -9
  67. package/dist/infrastructure/utils/stream-processor.util.d.ts +0 -14
  68. package/dist/presentation/hooks/index.d.ts +0 -1
  69. package/dist/presentation/hooks/useGemini.d.ts +0 -17
  70. package/dist/presentation/hooks/useOperationManager.d.ts +0 -23
  71. package/dist/providers/ConfigBuilder.d.ts +0 -46
  72. package/dist/providers/ProviderFactory.d.ts +0 -25
  73. package/dist/providers/index.d.ts +0 -7
  74. package/src/infrastructure/services/BaseService.ts +0 -53
  75. package/src/infrastructure/services/ChatSession.ts +0 -199
  76. package/src/infrastructure/services/GeminiClient.ts +0 -112
  77. package/src/infrastructure/services/Streaming.ts +0 -56
  78. package/src/infrastructure/services/StructuredText.ts +0 -57
  79. package/src/infrastructure/services/TextGeneration.ts +0 -57
  80. package/src/infrastructure/telemetry/TelemetryHooks.ts +0 -110
  81. package/src/infrastructure/utils/async/execute-state.util.ts +0 -93
  82. package/src/infrastructure/utils/content-mapper.util.ts +0 -175
  83. package/src/infrastructure/utils/error-mapper.util.ts +0 -145
  84. package/src/infrastructure/utils/gemini-data-transformer.util.ts +0 -40
  85. package/src/infrastructure/utils/text-calculations.util.ts +0 -70
  86. package/src/presentation/hooks/useGemini.ts +0 -125
  87. package/src/presentation/hooks/useOperationManager.ts +0 -88
  88. package/src/providers/ConfigBuilder.ts +0 -112
  89. package/src/providers/ProviderFactory.ts +0 -65
@@ -1,46 +0,0 @@
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
- }
@@ -1,25 +0,0 @@
1
- export { ConfigBuilder } from "./ConfigBuilder";
2
- export type { ProviderConfig } from "./ConfigBuilder";
3
- export interface ProviderFactoryOptions {
4
- apiKey: string;
5
- timeout?: number;
6
- textModel?: string;
7
- strategy?: "cost" | "quality";
8
- }
9
- declare class ProviderFactory {
10
- private currentConfig;
11
- /**
12
- * Initialize provider with configuration
13
- */
14
- initialize(options: ProviderFactoryOptions): void;
15
- /**
16
- * Check if provider is initialized
17
- */
18
- isInitialized(): boolean;
19
- /**
20
- * Update configuration
21
- * API key changes require re-initialization
22
- */
23
- updateConfig(updates: Partial<ProviderFactoryOptions>): void;
24
- }
25
- export declare const providerFactory: ProviderFactory;
@@ -1,7 +0,0 @@
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";
@@ -1,53 +0,0 @@
1
- import { geminiClient } from "./GeminiClient";
2
- import { toSdkContent } from "../utils/content-mapper.util";
3
- import { createGeminiError } from "../utils/error-mapper.util";
4
- import { GeminiError } from "../../domain/entities";
5
- import type { GeminiContent, GeminiGenerationConfig } from "../../domain/entities";
6
- import type { GenerativeModel } from "@google/generative-ai";
7
- import type { Part } from "@google/generative-ai";
8
-
9
- export interface BaseRequestOptions {
10
- model: string;
11
- contents: GeminiContent[];
12
- generationConfig?: GeminiGenerationConfig;
13
- signal?: AbortSignal;
14
- }
15
-
16
- export abstract class BaseGeminiService {
17
- protected validateAndPrepare(options: BaseRequestOptions): {
18
- genModel: GenerativeModel;
19
- sdkContents: Array<{ role: string; parts: Part[] }>;
20
- } {
21
- if (!options.contents || options.contents.length === 0) {
22
- throw new Error("Contents array cannot be empty");
23
- }
24
-
25
- if (options.signal?.aborted) {
26
- throw new Error("Request was aborted");
27
- }
28
-
29
- const genModel = geminiClient.getModel(options.model);
30
- const sdkContents = toSdkContent(options.contents);
31
-
32
- return { genModel, sdkContents };
33
- }
34
-
35
- protected handleError(error: unknown, abortMessage: string): never {
36
- if (error instanceof GeminiError) {
37
- throw error;
38
- }
39
-
40
- if (error instanceof Error && error.name === "AbortError") {
41
- throw new Error(abortMessage);
42
- }
43
-
44
- throw createGeminiError(error);
45
- }
46
-
47
- protected createRequestOptions(
48
- sdkContents: Array<{ role: string; parts: Part[] }>,
49
- generationConfig?: GeminiGenerationConfig
50
- ) {
51
- return { contents: sdkContents, generationConfig };
52
- }
53
- }
@@ -1,199 +0,0 @@
1
- import type { ChatSession as SdkChatSession, Part } from "@google/generative-ai";
2
- import { geminiClient } from "./GeminiClient";
3
- import { DEFAULT_MODELS } from "../../domain/entities";
4
- import { trimArrayByCharBudget } from "../utils/text-calculations.util";
5
- import type {
6
- GeminiChatConfig,
7
- GeminiGenerationConfig,
8
- GeminiContent,
9
- GeminiInlineDataPart,
10
- GeminiMessagePart,
11
- GeminiSafetySetting,
12
- } from "../../domain/entities";
13
-
14
- // ─── Types ───────────────────────────────────────────────────────────────────
15
-
16
- export interface ChatSendResult {
17
- text: string;
18
- finishReason?: string;
19
- }
20
-
21
- /** A simple chat message (user/assistant/system) */
22
- export interface ChatHistoryMessage {
23
- readonly role: "user" | "assistant" | "system";
24
- readonly content: string;
25
- }
26
-
27
- /** Options for the high-level sendChatMessage() */
28
- export interface SendChatMessageOptions {
29
- /** Conversation history */
30
- history: ChatHistoryMessage[];
31
- /** Current user message text */
32
- message: string;
33
- /** System instruction for the model */
34
- systemPrompt?: string;
35
- /** Model name (defaults to gemini-2.5-flash) */
36
- model?: string;
37
- /** Generation config (temperature, maxOutputTokens, etc.) */
38
- generationConfig?: GeminiGenerationConfig;
39
- /** Safety settings (defaults to BLOCK_NONE for all) */
40
- safetySettings?: GeminiSafetySetting[];
41
- /** Pre-built attachments (base64 inline data parts for images/audio) */
42
- attachments?: GeminiInlineDataPart[];
43
- /** Max character budget for history trimming (default 12000) */
44
- historyMaxChars?: number;
45
- /** Min messages to always keep regardless of budget (default 4) */
46
- historyMinMessages?: number;
47
- }
48
-
49
- // ─── History Utilities ───────────────────────────────────────────────────────
50
-
51
- /**
52
- * Converts chat messages (user/assistant/system) to Gemini SDK content format.
53
- * Skips system messages, merges consecutive same-role messages.
54
- */
55
- export function buildChatHistory(
56
- history: readonly ChatHistoryMessage[],
57
- ): GeminiContent[] {
58
- const result: GeminiContent[] = [];
59
- let seenUser = false;
60
-
61
- for (const m of history) {
62
- if (m.role === "system") continue;
63
- if (!seenUser && m.role !== "user") continue;
64
- seenUser = true;
65
-
66
- const role = m.role === "assistant" ? "model" : "user";
67
- const last = result[result.length - 1];
68
-
69
- if (last && last.role === role) {
70
- // Merge by extracting all text from existing parts and appending new content
71
- // Optimized: reduce instead of map + filter + join
72
- const existingText = last.parts.reduce((acc, p) => acc + ("text" in p ? p.text : ""), "");
73
- last.parts = [{ text: existingText + "\n" + m.content }];
74
- } else {
75
- result.push({ role, parts: [{ text: m.content }] });
76
- }
77
- }
78
-
79
- return result;
80
- }
81
-
82
- /**
83
- * Trims conversation history to fit within a character budget.
84
- * Keeps at least `minMessages` entries regardless of budget.
85
- */
86
- export function trimChatHistory(
87
- history: ChatHistoryMessage[],
88
- maxChars = 12000,
89
- minMessages = 4,
90
- ): ChatHistoryMessage[] {
91
- return trimArrayByCharBudget(
92
- history,
93
- (m) => m.content.length,
94
- maxChars,
95
- minMessages,
96
- );
97
- }
98
-
99
- // ─── Low-level: createChatSession ────────────────────────────────────────────
100
-
101
- /**
102
- * Creates a Gemini chat session with full support for system instructions,
103
- * safety settings, generation config, and multi-turn conversation history.
104
- */
105
- export function createChatSession(config: GeminiChatConfig = {}) {
106
- const model = geminiClient.getModel({
107
- model: config.model ?? DEFAULT_MODELS.CHAT,
108
- systemInstruction: config.systemInstruction,
109
- safetySettings: config.safetySettings,
110
- });
111
-
112
- const historyForChat = (config.history ?? []).map((turn) => ({
113
- role: turn.role === "model" ? ("model" as const) : ("user" as const),
114
- parts: turn.parts as Part[],
115
- }));
116
-
117
- const chat: SdkChatSession = model.startChat({
118
- history: historyForChat,
119
- ...(config.generationConfig && { generationConfig: config.generationConfig }),
120
- });
121
-
122
- return {
123
- async send(parts: GeminiMessagePart[]): Promise<ChatSendResult> {
124
- const result = await chat.sendMessage(parts as Part[]);
125
- if (!result.response) throw new Error("No response from Gemini SDK");
126
- const candidate = result.response.candidates?.[0];
127
-
128
- // SDK's text() throws on safety-blocked responses with no text
129
- let text: string;
130
- try {
131
- text = result.response.text();
132
- } catch {
133
- if (String(candidate?.finishReason) === "SAFETY") {
134
- throw new Error("Response blocked by safety filter.");
135
- }
136
- throw new Error("No text content in response");
137
- }
138
-
139
- return {
140
- text,
141
- finishReason: candidate?.finishReason,
142
- };
143
- },
144
- };
145
- }
146
-
147
- // ─── High-level: sendChatMessage ─────────────────────────────────────────────
148
-
149
- /**
150
- * All-in-one: trims history, builds session, sends message, handles safety.
151
- * Returns the AI response text.
152
- *
153
- * ```ts
154
- * const text = await sendChatMessage({
155
- * history: messages,
156
- * message: "hello",
157
- * systemPrompt: "You are Aria...",
158
- * model: GEMINI_MODELS.TEXT.FLASH,
159
- * generationConfig: { temperature: 0.7, maxOutputTokens: 512 },
160
- * });
161
- * ```
162
- */
163
- export async function sendChatMessage(
164
- opts: SendChatMessageOptions,
165
- ): Promise<string> {
166
- if (!opts.message || opts.message.trim().length === 0) {
167
- throw new Error("Message cannot be empty");
168
- }
169
-
170
- const trimmed = trimChatHistory(
171
- opts.history,
172
- opts.historyMaxChars,
173
- opts.historyMinMessages,
174
- );
175
-
176
- const geminiHistory = buildChatHistory(trimmed);
177
-
178
- const session = createChatSession({
179
- model: opts.model ?? DEFAULT_MODELS.CHAT,
180
- systemInstruction: opts.systemPrompt,
181
- safetySettings: opts.safetySettings,
182
- generationConfig: opts.generationConfig,
183
- history: geminiHistory,
184
- });
185
-
186
- const parts: GeminiMessagePart[] = [{ text: opts.message }];
187
- if (opts.attachments) {
188
- parts.push(...opts.attachments);
189
- }
190
-
191
- const result = await session.send(parts);
192
-
193
- if (result.finishReason === "SAFETY") {
194
- if (result.text.trim()) return result.text;
195
- throw new Error("Response blocked by safety filter.");
196
- }
197
-
198
- return result.text;
199
- }
@@ -1,112 +0,0 @@
1
- import {
2
- GoogleGenerativeAI,
3
- HarmCategory,
4
- HarmBlockThreshold,
5
- type GenerativeModel,
6
- type SafetySetting,
7
- } from "@google/generative-ai";
8
- import { DEFAULT_MODELS } from "../../domain/entities";
9
- import type { GeminiConfig, GeminiModelOptions } from "../../domain/entities";
10
-
11
- const DEFAULT_CONFIG: Partial<GeminiConfig> = {
12
- textModel: DEFAULT_MODELS.TEXT,
13
- };
14
-
15
- /** All categories set to BLOCK_NONE */
16
- const PERMISSIVE_SAFETY: SafetySetting[] = [
17
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
18
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
19
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
20
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE },
21
- ];
22
-
23
- class GeminiClient {
24
- private client: GoogleGenerativeAI | null = null;
25
- private config: GeminiConfig | null = null;
26
- private initialized = false;
27
-
28
- initialize(config: GeminiConfig): void {
29
- const apiKey = config.apiKey?.trim();
30
-
31
- if (!apiKey || apiKey.length < 10) {
32
- throw new Error("API key is required and must be at least 10 characters");
33
- }
34
-
35
- // Basic format validation for Google AI API keys (starts with "AIza")
36
- if (!apiKey.startsWith("AIza")) {
37
- throw new Error('Invalid API key format. Google AI API keys should start with "AIza"');
38
- }
39
-
40
- // Allow re-initialization with new config (e.g. API key change)
41
- this.client = new GoogleGenerativeAI(apiKey);
42
- this.config = { ...DEFAULT_CONFIG, ...config, apiKey };
43
- this.initialized = true;
44
- }
45
-
46
- /**
47
- * Returns a GenerativeModel configured with optional safety settings and system instruction.
48
- * When no safety settings are provided, defaults to BLOCK_NONE for all categories.
49
- */
50
- getModel(modelNameOrOptions?: string | GeminiModelOptions): GenerativeModel {
51
- if (!this.client || !this.initialized) {
52
- throw new Error("Gemini client not initialized. Call initialize() first.");
53
- }
54
-
55
- // Normalize args
56
- const opts: GeminiModelOptions =
57
- typeof modelNameOrOptions === "string"
58
- ? { model: modelNameOrOptions }
59
- : modelNameOrOptions ?? {};
60
-
61
- const effectiveModel = opts.model || this.config?.textModel || DEFAULT_MODELS.TEXT;
62
-
63
- if (!effectiveModel.startsWith("gemini-")) {
64
- throw new Error('Model name must start with "gemini-"');
65
- }
66
-
67
- // Map package safety settings to SDK format
68
- const sdkSafety: SafetySetting[] = opts.safetySettings
69
- ? opts.safetySettings.map((s) => {
70
- // Validate safety settings to prevent runtime errors
71
- const validCategories = [
72
- HarmCategory.HARM_CATEGORY_HARASSMENT,
73
- HarmCategory.HARM_CATEGORY_HATE_SPEECH,
74
- HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
75
- HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
76
- ];
77
-
78
- const validThresholds = [
79
- HarmBlockThreshold.BLOCK_NONE,
80
- HarmBlockThreshold.BLOCK_LOW_AND_ABOVE,
81
- HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
82
- HarmBlockThreshold.BLOCK_ONLY_HIGH,
83
- ];
84
-
85
- // Check if category and threshold are valid enum values
86
- const category = validCategories.includes(s.category as unknown as HarmCategory)
87
- ? (s.category as unknown as HarmCategory)
88
- : HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT; // Fallback to safest
89
-
90
- const threshold = validThresholds.includes(s.threshold as unknown as HarmBlockThreshold)
91
- ? (s.threshold as unknown as HarmBlockThreshold)
92
- : HarmBlockThreshold.BLOCK_NONE; // Fallback to most permissive
93
-
94
- return { category, threshold };
95
- })
96
- : PERMISSIVE_SAFETY;
97
-
98
- return this.client.getGenerativeModel({
99
- model: effectiveModel,
100
- ...(opts.systemInstruction && { systemInstruction: opts.systemInstruction }),
101
- safetySettings: sdkSafety,
102
- });
103
- }
104
-
105
- reset(): void {
106
- this.client = null;
107
- this.config = null;
108
- this.initialized = false;
109
- }
110
- }
111
-
112
- export const geminiClient = new GeminiClient();
@@ -1,56 +0,0 @@
1
- import { BaseGeminiService } from "./BaseService";
2
- import { telemetryHooks } from "../telemetry/TelemetryHooks";
3
- import { processStream } from "../utils/stream-processor.util";
4
- import type {
5
- GeminiContent,
6
- GeminiGenerationConfig,
7
- } from "../../domain/entities";
8
-
9
- class StreamingService extends BaseGeminiService {
10
- async streamContent(
11
- model: string,
12
- contents: GeminiContent[],
13
- onChunk: (text: string) => void,
14
- generationConfig?: GeminiGenerationConfig,
15
- signal?: AbortSignal,
16
- ): Promise<string> {
17
- if (typeof onChunk !== "function") {
18
- throw new Error("onChunk must be a function");
19
- }
20
-
21
- try {
22
- const { genModel, sdkContents } = this.validateAndPrepare({
23
- model,
24
- contents,
25
- generationConfig,
26
- signal,
27
- });
28
-
29
- const requestOptions = this.createRequestOptions(sdkContents, generationConfig);
30
-
31
- const result = signal
32
- ? await genModel.generateContentStream(requestOptions, { signal })
33
- : await genModel.generateContentStream(requestOptions);
34
-
35
- return await processStream(
36
- result.stream,
37
- onChunk,
38
- (error, context) => {
39
- try {
40
- telemetryHooks.logError(
41
- model,
42
- error instanceof Error ? error : new Error(String(error)),
43
- context
44
- );
45
- } catch {
46
- // Silently ignore telemetry errors
47
- }
48
- }
49
- );
50
- } catch (error) {
51
- return this.handleError(error, "Stream generation was aborted");
52
- }
53
- }
54
- }
55
-
56
- export const streaming = new StreamingService();
@@ -1,57 +0,0 @@
1
- import { textGeneration } from "./TextGeneration";
2
- import { parseJsonResponse } from "../utils/json-parser.util";
3
- import { extractTextFromParts } from "../utils/content-mapper.util";
4
- import type { GenerationConfig } from "@google/generative-ai";
5
- import type {
6
- GeminiContent,
7
- GeminiGenerationConfig,
8
- } from "../../domain/entities";
9
-
10
- class StructuredTextService {
11
- async generateStructuredText<T>(
12
- model: string,
13
- prompt: string,
14
- schema: Record<string, unknown>,
15
- config?: Omit<GeminiGenerationConfig, "responseMimeType" | "responseSchema">,
16
- signal?: AbortSignal,
17
- ): Promise<T> {
18
- if (!prompt || prompt.trim().length < 3) {
19
- throw new Error("Prompt must be at least 3 characters");
20
- }
21
-
22
- if (!schema || typeof schema !== "object" || Object.keys(schema).length === 0) {
23
- throw new Error("Schema must be a non-empty object");
24
- }
25
-
26
- const generationConfig: GeminiGenerationConfig = {
27
- ...config,
28
- responseMimeType: "application/json",
29
- responseSchema: schema as unknown as GenerationConfig["responseSchema"],
30
- };
31
-
32
- const contents: GeminiContent[] = [
33
- { parts: [{ text: prompt }], role: "user" },
34
- ];
35
-
36
- const response = await textGeneration.generateContent(
37
- model,
38
- contents,
39
- generationConfig,
40
- signal,
41
- );
42
-
43
- const candidates = response.candidates;
44
- if (!candidates || candidates.length === 0) {
45
- throw new Error("No candidates in response");
46
- }
47
-
48
- const text = extractTextFromParts(candidates[0]?.content?.parts);
49
- if (!text || text.trim().length === 0) {
50
- throw new Error("Empty response received from Gemini");
51
- }
52
-
53
- return parseJsonResponse<T>(text);
54
- }
55
- }
56
-
57
- export const structuredText = new StructuredTextService();
@@ -1,57 +0,0 @@
1
- import { BaseGeminiService } from "./BaseService";
2
- import { extractTextFromResponse } from "../utils/gemini-data-transformer.util";
3
- import { transformResponse, createTextContent } from "../utils/content-mapper.util";
4
- import type {
5
- GeminiContent,
6
- GeminiGenerationConfig,
7
- GeminiResponse,
8
- } from "../../domain/entities";
9
-
10
- class TextGenerationService extends BaseGeminiService {
11
- async generateContent(
12
- model: string,
13
- contents: GeminiContent[],
14
- generationConfig?: GeminiGenerationConfig,
15
- signal?: AbortSignal,
16
- ): Promise<GeminiResponse> {
17
- try {
18
- const { genModel, sdkContents } = this.validateAndPrepare({
19
- model,
20
- contents,
21
- generationConfig,
22
- signal,
23
- });
24
-
25
- const requestOptions = this.createRequestOptions(sdkContents, generationConfig);
26
-
27
- const result = signal
28
- ? await genModel.generateContent(requestOptions, { signal })
29
- : await genModel.generateContent(requestOptions);
30
-
31
- if (!result.response) {
32
- throw new Error("No response received from Gemini API");
33
- }
34
-
35
- return transformResponse(result.response);
36
- } catch (error) {
37
- return this.handleError(error, "Request was aborted");
38
- }
39
- }
40
-
41
- async generateText(
42
- model: string,
43
- prompt: string,
44
- config?: GeminiGenerationConfig,
45
- signal?: AbortSignal,
46
- ): Promise<string> {
47
- if (!prompt || prompt.trim().length < 3) {
48
- throw new Error("Prompt must be at least 3 characters");
49
- }
50
-
51
- const contents: GeminiContent[] = [createTextContent(prompt, "user")];
52
- const response = await this.generateContent(model, contents, config, signal);
53
- return extractTextFromResponse(response);
54
- }
55
- }
56
-
57
- export const textGeneration = new TextGenerationService();