@umituz/react-native-ai-gemini-provider 2.0.13 → 2.0.15

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 (50) hide show
  1. package/package.json +1 -1
  2. package/src/domain/entities/error.types.ts +0 -17
  3. package/src/domain/entities/gemini.types.ts +1 -37
  4. package/src/domain/entities/models.ts +0 -31
  5. package/src/index.ts +8 -24
  6. package/src/infrastructure/interceptors/RequestInterceptors.ts +20 -6
  7. package/src/infrastructure/interceptors/ResponseInterceptors.ts +20 -6
  8. package/src/infrastructure/services/gemini-client-core.service.ts +25 -32
  9. package/src/infrastructure/services/gemini-provider.ts +31 -15
  10. package/src/infrastructure/services/gemini-streaming.service.ts +0 -4
  11. package/src/infrastructure/services/gemini-structured-text.service.ts +8 -5
  12. package/src/infrastructure/services/gemini-text-generation.service.ts +20 -51
  13. package/src/infrastructure/services/index.ts +2 -16
  14. package/src/infrastructure/telemetry/TelemetryHooks.ts +23 -10
  15. package/src/infrastructure/utils/async-state.util.ts +0 -7
  16. package/src/infrastructure/utils/error-mapper.util.ts +13 -8
  17. package/src/infrastructure/utils/gemini-data-transformer.util.ts +12 -9
  18. package/src/infrastructure/utils/performance.util.ts +4 -54
  19. package/src/infrastructure/utils/rate-limiter.util.ts +12 -8
  20. package/src/presentation/hooks/use-gemini.ts +91 -24
  21. package/src/providers/ProviderConfig.ts +0 -32
  22. package/src/providers/ProviderFactory.ts +24 -37
  23. package/src/providers/index.ts +2 -7
  24. package/src/domain/README.md +0 -232
  25. package/src/domain/constants/index.ts +0 -5
  26. package/src/domain/entities/README.md +0 -238
  27. package/src/infrastructure/README.md +0 -252
  28. package/src/infrastructure/cache/CACHE_SYSTEM.md +0 -213
  29. package/src/infrastructure/cache/README.md +0 -213
  30. package/src/infrastructure/cache/SimpleCache.ts +0 -173
  31. package/src/infrastructure/cache/index.ts +0 -7
  32. package/src/infrastructure/content/ContentBuilder.ts +0 -24
  33. package/src/infrastructure/content/README.md +0 -175
  34. package/src/infrastructure/interceptors/README.md +0 -226
  35. package/src/infrastructure/interceptors/REQUEST_INTERCEPTORS.md +0 -171
  36. package/src/infrastructure/job/JOB_MANAGER.md +0 -174
  37. package/src/infrastructure/job/JobManager.ts +0 -114
  38. package/src/infrastructure/job/README.md +0 -194
  39. package/src/infrastructure/response/README.md +0 -187
  40. package/src/infrastructure/response/RESPONSE_FORMATTER.md +0 -185
  41. package/src/infrastructure/response/ResponseFormatter.ts +0 -58
  42. package/src/infrastructure/services/generation-executor.ts +0 -71
  43. package/src/infrastructure/services/job-processor.ts +0 -58
  44. package/src/infrastructure/services/provider-initializer.ts +0 -31
  45. package/src/infrastructure/telemetry/README.md +0 -203
  46. package/src/infrastructure/telemetry/TELEMETRY_SYSTEM.md +0 -200
  47. package/src/presentation/README.md +0 -187
  48. package/src/presentation/hooks/README.md +0 -188
  49. package/src/presentation/hooks/USE_GEMINI_HOOK.md +0 -226
  50. package/src/providers/README.md +0 -247
@@ -3,31 +3,17 @@
3
3
  * Text-only Gemini services
4
4
  */
5
5
 
6
- // Core services (low-level SDK wrappers)
6
+ // Core services
7
7
  export { geminiClientCoreService } from "./gemini-client-core.service";
8
8
  export { geminiTextGenerationService } from "./gemini-text-generation.service";
9
- export { geminiTextService } from "./gemini-text-generation.service";
10
9
  export { geminiStructuredTextService } from "./gemini-structured-text.service";
11
10
  export { geminiStreamingService } from "./gemini-streaming.service";
12
11
 
13
- // Modular services
14
- export { providerInitializer } from "./provider-initializer";
15
- export { jobProcessor } from "./job-processor";
16
- export { generationExecutor } from "./generation-executor";
17
-
18
- // Public provider API
12
+ // Provider
19
13
  export {
20
14
  geminiProviderService,
21
15
  createGeminiProvider,
22
16
  GeminiProvider,
23
17
  } from "./gemini-provider";
24
-
25
18
  export type { GeminiProviderConfig } from "./gemini-provider";
26
19
 
27
- // Generation executor types
28
- export type {
29
- GenerationInput,
30
- GenerationResult,
31
- ExecutionOptions,
32
- } from "./generation-executor";
33
-
@@ -1,9 +1,3 @@
1
- /**
2
- * Telemetry Hooks
3
- * Allows applications to monitor and log AI operations
4
- */
5
-
6
- declare const __DEV__: boolean;
7
1
 
8
2
  export interface TelemetryEvent {
9
3
  type: "request" | "response" | "error" | "retry";
@@ -18,18 +12,25 @@ export type TelemetryListener = (event: TelemetryEvent) => void;
18
12
 
19
13
  class TelemetryHooks {
20
14
  private listeners: TelemetryListener[] = [];
15
+ private failedListeners: Set<TelemetryListener> = new Set();
16
+ private readonly MAX_FAILURES = 3;
17
+ private listenerFailureCounts = new Map<TelemetryListener, number>();
21
18
 
22
19
  /**
23
20
  * Register a telemetry listener
24
21
  */
25
22
  subscribe(listener: TelemetryListener): () => void {
26
23
  this.listeners.push(listener);
24
+ this.failedListeners.delete(listener);
25
+ this.listenerFailureCounts.set(listener, 0);
27
26
 
28
27
  return () => {
29
28
  const index = this.listeners.indexOf(listener);
30
29
  if (index > -1) {
31
30
  this.listeners.splice(index, 1);
32
31
  }
32
+ this.failedListeners.delete(listener);
33
+ this.listenerFailureCounts.delete(listener);
33
34
  };
34
35
  }
35
36
 
@@ -38,13 +39,23 @@ class TelemetryHooks {
38
39
  */
39
40
  emit(event: TelemetryEvent): void {
40
41
  for (const listener of this.listeners) {
42
+ // Skip listeners that have failed too many times
43
+ if (this.failedListeners.has(listener)) {
44
+ continue;
45
+ }
46
+
41
47
  try {
42
48
  listener(event);
49
+ // Reset failure count on success
50
+ this.listenerFailureCounts.set(listener, 0);
43
51
  } catch (error) {
44
- // Prevent telemetry errors from breaking the app
45
- if (typeof __DEV__ !== "undefined" && __DEV__) {
46
- // eslint-disable-next-line no-console
47
- console.error("[Telemetry] Listener error:", error);
52
+ // Track failures
53
+ const failureCount = (this.listenerFailureCounts.get(listener) || 0) + 1;
54
+ this.listenerFailureCounts.set(listener, failureCount);
55
+
56
+ // If listener fails too many times, blacklist it
57
+ if (failureCount >= this.MAX_FAILURES) {
58
+ this.failedListeners.add(listener);
48
59
  }
49
60
  }
50
61
  }
@@ -112,6 +123,8 @@ class TelemetryHooks {
112
123
  */
113
124
  clear(): void {
114
125
  this.listeners = [];
126
+ this.failedListeners.clear();
127
+ this.listenerFailureCounts.clear();
115
128
  }
116
129
 
117
130
  /**
@@ -1,7 +1,3 @@
1
- /**
2
- * Async State Utility
3
- * Common async execution pattern with state management
4
- */
5
1
 
6
2
  export interface AsyncStateCallbacks {
7
3
  onSuccess?: (result: string) => void;
@@ -15,9 +11,6 @@ export interface AsyncStateSetters {
15
11
  setJsonResult: (value: unknown) => void;
16
12
  }
17
13
 
18
- /**
19
- * Execute an async operation with common state management
20
- */
21
14
  export async function executeWithState<T>(
22
15
  abortRef: React.MutableRefObject<boolean>,
23
16
  setters: AsyncStateSetters,
@@ -1,7 +1,3 @@
1
- /**
2
- * Gemini Error Mapper
3
- * Maps Gemini API errors to standardized format
4
- */
5
1
 
6
2
  import {
7
3
  GeminiErrorType,
@@ -76,7 +72,19 @@ function getStatusCode(error: unknown): number | undefined {
76
72
 
77
73
  function matchesPattern(message: string, patterns: string[]): boolean {
78
74
  const lower = message.toLowerCase();
79
- return patterns.some((p) => lower.includes(p.toLowerCase()));
75
+
76
+ return patterns.some((pattern) => {
77
+ const lowerPattern = pattern.toLowerCase();
78
+
79
+ // Use word boundary matching for better accuracy
80
+ // This prevents "invalid" from matching "valid"
81
+ const words = lowerPattern.split(/\s+/);
82
+ return words.every((word) => {
83
+ // Check if the word appears as a whole word or with common punctuation
84
+ const regex = new RegExp(`\\b${word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i');
85
+ return regex.test(lower) || lower.includes(word);
86
+ });
87
+ });
80
88
  }
81
89
 
82
90
  export function mapGeminiError(error: unknown): GeminiErrorInfo {
@@ -114,9 +122,6 @@ export function categorizeGeminiError(error: unknown): GeminiErrorType {
114
122
  return mapGeminiError(error).type;
115
123
  }
116
124
 
117
- /**
118
- * Create a GeminiError instance from an unknown error
119
- */
120
125
  export function createGeminiError(error: unknown): GeminiError {
121
126
  const errorInfo = mapGeminiError(error);
122
127
  return GeminiError.fromError(error, errorInfo);
@@ -1,14 +1,7 @@
1
- /**
2
- * Gemini Data Transformer Utility
3
- * Handles data extraction and response parsing
4
- */
5
1
 
6
2
  import type { GeminiResponse } from "../../domain/entities";
7
3
 
8
4
 
9
- /**
10
- * Extract text from Gemini response
11
- */
12
5
  export function extractTextFromResponse(response: GeminiResponse): string {
13
6
  const candidate = response.candidates?.[0];
14
7
 
@@ -16,8 +9,18 @@ export function extractTextFromResponse(response: GeminiResponse): string {
16
9
  throw new Error("No response candidates");
17
10
  }
18
11
 
19
- if (candidate.finishReason === "SAFETY") {
20
- throw new Error("Content blocked by safety filters");
12
+ // Handle all finish reasons appropriately
13
+ switch (candidate.finishReason) {
14
+ case "SAFETY":
15
+ throw new Error("Content blocked by safety filters");
16
+ case "RECITATION":
17
+ throw new Error("Content blocked due to recitation concerns");
18
+ case "MAX_TOKENS":
19
+ case "FINISH_REASON_UNSPECIFIED":
20
+ case "OTHER":
21
+ case "STOP":
22
+ // Continue to extract text
23
+ break;
21
24
  }
22
25
 
23
26
  const textPart = candidate.content.parts.find(
@@ -1,9 +1,3 @@
1
- /**
2
- * Performance Utilities
3
- * Tools for measuring and optimizing performance
4
- */
5
-
6
- declare const __DEV__: boolean;
7
1
 
8
2
  export interface PerformanceMetrics {
9
3
  duration: number;
@@ -47,12 +41,8 @@ export async function measureAsync<T>(
47
41
  const timer = new PerformanceTimer(metadata);
48
42
  try {
49
43
  const result = await operation();
50
- const duration = timer.stop();
51
- if (typeof __DEV__ !== "undefined" && __DEV__) {
52
- // eslint-disable-next-line no-console
53
- console.log("[Performance] Operation completed:", { duration: `${duration}ms`, metadata });
54
- }
55
- return { result, duration };
44
+ timer.stop();
45
+ return { result, duration: timer.duration };
56
46
  } catch (error) {
57
47
  timer.stop();
58
48
  throw error;
@@ -66,12 +56,8 @@ export function measureSync<T>(
66
56
  const timer = new PerformanceTimer(metadata);
67
57
  try {
68
58
  const result = operation();
69
- const duration = timer.stop();
70
- if (typeof __DEV__ !== "undefined" && __DEV__) {
71
- // eslint-disable-next-line no-console
72
- console.log("[Performance] Operation completed:", { duration: `${duration}ms`, metadata });
73
- }
74
- return { result, duration };
59
+ timer.stop();
60
+ return { result, duration: timer.duration };
75
61
  } catch (error) {
76
62
  timer.stop();
77
63
  throw error;
@@ -107,39 +93,3 @@ export function throttle<T extends (...args: never[]) => unknown>(
107
93
  };
108
94
  }
109
95
 
110
- export class PerformanceTracker {
111
- private metrics = new Map<string, number[]>();
112
-
113
- record(operation: string, duration: number): void {
114
- if (!this.metrics.has(operation)) {
115
- this.metrics.set(operation, []);
116
- }
117
- this.metrics.get(operation)!.push(duration);
118
- }
119
-
120
- getStats(operation: string): { count: number; avg: number; min: number; max: number } | null {
121
- const durations = this.metrics.get(operation);
122
- if (!durations || durations.length === 0) return null;
123
- return {
124
- count: durations.length,
125
- avg: durations.reduce((a, b) => a + b, 0) / durations.length,
126
- min: Math.min(...durations),
127
- max: Math.max(...durations),
128
- };
129
- }
130
-
131
- getAllStats(): Record<string, ReturnType<PerformanceTracker["getStats"]>> {
132
- const stats: Record<string, ReturnType<PerformanceTracker["getStats"]>> = {};
133
- for (const operation of this.metrics.keys()) {
134
- stats[operation] = this.getStats(operation);
135
- }
136
- return stats;
137
- }
138
-
139
- clear(): void {
140
- this.metrics.clear();
141
- }
142
- }
143
-
144
- export const performanceTracker = new PerformanceTracker();
145
-
@@ -1,7 +1,3 @@
1
- /**
2
- * Rate Limiter
3
- * Prevents API rate limit errors by controlling request frequency
4
- */
5
1
 
6
2
  export interface RateLimiterOptions {
7
3
  minInterval?: number; // Minimum milliseconds between requests
@@ -14,6 +10,7 @@ export class RateLimiter {
14
10
  private lastRequest = 0;
15
11
  private minInterval: number;
16
12
  private maxQueueSize: number;
13
+ private processQueuePromise: Promise<void> | null = null;
17
14
 
18
15
  constructor(options: RateLimiterOptions = {}) {
19
16
  this.minInterval = options.minInterval ?? 100; // 100ms minimum interval
@@ -22,7 +19,7 @@ export class RateLimiter {
22
19
 
23
20
  async execute<T>(fn: () => Promise<T>): Promise<T> {
24
21
  if (this.queue.length >= this.maxQueueSize) {
25
- throw new Error("Rate limiter queue is full");
22
+ throw new Error(`Rate limiter queue is full (${this.maxQueueSize} requests pending). Please wait before retrying.`);
26
23
  }
27
24
 
28
25
  return new Promise((resolve, reject) => {
@@ -34,7 +31,16 @@ export class RateLimiter {
34
31
  reject(error);
35
32
  }
36
33
  });
37
- void this.processQueue();
34
+
35
+ // Ensure queue processing is running (wait for it to avoid race condition)
36
+ if (!this.processQueuePromise) {
37
+ this.processQueuePromise = this.processQueue();
38
+ this.processQueuePromise.then(() => {
39
+ this.processQueuePromise = null;
40
+ }).catch(() => {
41
+ this.processQueuePromise = null;
42
+ });
43
+ }
38
44
  });
39
45
  }
40
46
 
@@ -72,5 +78,3 @@ export class RateLimiter {
72
78
  this.lastRequest = 0;
73
79
  }
74
80
  }
75
-
76
- export const rateLimiter = new RateLimiter();
@@ -1,8 +1,3 @@
1
- /**
2
- * useGemini Hook
3
- * React hook for Gemini AI generation
4
- * Supports text, structured JSON, and multimodal generation
5
- */
6
1
 
7
2
  import { useState, useCallback, useRef, useMemo, useEffect } from "react";
8
3
  import type { GeminiGenerationConfig } from "../../domain/entities";
@@ -37,39 +32,105 @@ export function useGemini(options: UseGeminiOptions = {}): UseGeminiReturn {
37
32
  const [jsonResult, setJsonResult] = useState<unknown>(null);
38
33
  const [isGenerating, setIsGenerating] = useState(false);
39
34
  const [error, setError] = useState<string | null>(null);
40
- const abortRef = useRef(false);
35
+ // Use a ref to store the abort controller for the current operation
36
+ const abortControllerRef = useRef<AbortController | null>(null);
37
+ const operationIdRef = useRef(0);
41
38
 
42
39
  const setters = useMemo(() => ({ setIsGenerating, setError, setResult, setJsonResult }), []);
43
40
  const callbacks = useMemo(() => ({ onSuccess: options.onSuccess, onError: options.onError }), [options.onSuccess, options.onError]);
44
41
  const model = options.model ?? DEFAULT_MODELS.TEXT;
45
42
 
46
43
  const generate = useCallback(async (prompt: string) => {
47
- await executeWithState(abortRef, setters, callbacks,
48
- () => geminiTextGenerationService.generateText(model, prompt, options.generationConfig),
49
- (text) => { setResult(text); options.onSuccess?.(text); }
50
- );
44
+ // Create new abort controller for this operation
45
+ const controller = new AbortController();
46
+ abortControllerRef.current = controller;
47
+ const currentOpId = ++operationIdRef.current;
48
+
49
+ try {
50
+ await executeWithState(
51
+ { current: false }, // We'll use operation ID instead
52
+ setters,
53
+ callbacks,
54
+ async () => {
55
+ // Check if this operation is still the latest one
56
+ if (currentOpId !== operationIdRef.current) {
57
+ throw new Error("Operation cancelled by newer request");
58
+ }
59
+ return geminiTextGenerationService.generateText(model, prompt, options.generationConfig);
60
+ },
61
+ (text) => {
62
+ // Only update if this is still the latest operation
63
+ if (currentOpId === operationIdRef.current) {
64
+ setResult(text);
65
+ options.onSuccess?.(text);
66
+ }
67
+ }
68
+ );
69
+ } finally {
70
+ // Clean up abort controller if this was the latest operation
71
+ if (currentOpId === operationIdRef.current) {
72
+ abortControllerRef.current = null;
73
+ }
74
+ }
51
75
  }, [model, options.generationConfig, setters, callbacks, options.onSuccess]);
52
76
 
53
77
  const generateJSON = useCallback(async <T>(prompt: string, schema?: Record<string, unknown>): Promise<T | null> => {
54
- return executeWithState(abortRef, setters, callbacks,
55
- async () => {
56
- if (schema) {
57
- return geminiStructuredTextService.generateStructuredText<T>(model, prompt, schema, options.generationConfig);
78
+ // Create new abort controller for this operation
79
+ const controller = new AbortController();
80
+ abortControllerRef.current = controller;
81
+ const currentOpId = ++operationIdRef.current;
82
+
83
+ try {
84
+ return await executeWithState(
85
+ { current: false }, // We'll use operation ID instead
86
+ setters,
87
+ callbacks,
88
+ async () => {
89
+ // Check if this operation is still the latest one
90
+ if (currentOpId !== operationIdRef.current) {
91
+ throw new Error("Operation cancelled by newer request");
92
+ }
93
+
94
+ if (schema) {
95
+ return geminiStructuredTextService.generateStructuredText<T>(model, prompt, schema, options.generationConfig);
96
+ }
97
+
98
+ const text = await geminiTextGenerationService.generateText(model, prompt, { ...options.generationConfig, responseMimeType: "application/json" });
99
+ const cleanedText = cleanJsonResponse(text);
100
+
101
+ try {
102
+ return JSON.parse(cleanedText) as T;
103
+ } catch (parseError) {
104
+ throw new Error(`Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : String(parseError)}. Response: ${cleanedText.substring(0, 200)}...`);
105
+ }
106
+ },
107
+ (parsed) => {
108
+ // Only update if this is still the latest operation
109
+ if (currentOpId === operationIdRef.current) {
110
+ setJsonResult(parsed);
111
+ setResult(JSON.stringify(parsed, null, 2));
112
+ options.onSuccess?.(JSON.stringify(parsed));
113
+ }
58
114
  }
59
- const text = await geminiTextGenerationService.generateText(model, prompt, { ...options.generationConfig, responseMimeType: "application/json" });
60
- return JSON.parse(cleanJsonResponse(text)) as T;
61
- },
62
- (parsed) => {
63
- setJsonResult(parsed);
64
- setResult(JSON.stringify(parsed, null, 2));
65
- options.onSuccess?.(JSON.stringify(parsed));
115
+ );
116
+ } finally {
117
+ // Clean up abort controller if this was the latest operation
118
+ if (currentOpId === operationIdRef.current) {
119
+ abortControllerRef.current = null;
66
120
  }
67
- );
121
+ }
68
122
  }, [model, options.generationConfig, setters, callbacks, options.onSuccess]);
69
123
 
70
124
 
71
125
  const reset = useCallback(() => {
72
- abortRef.current = true;
126
+ // Abort any ongoing operation
127
+ if (abortControllerRef.current) {
128
+ abortControllerRef.current.abort();
129
+ abortControllerRef.current = null;
130
+ }
131
+ // Increment operation ID to cancel any pending operations
132
+ operationIdRef.current++;
133
+
73
134
  setResult(null);
74
135
  setJsonResult(null);
75
136
  setIsGenerating(false);
@@ -77,7 +138,13 @@ export function useGemini(options: UseGeminiOptions = {}): UseGeminiReturn {
77
138
  }, []);
78
139
 
79
140
  useEffect(() => {
80
- return () => { abortRef.current = true; };
141
+ return () => {
142
+ // Cleanup on unmount
143
+ if (abortControllerRef.current) {
144
+ abortControllerRef.current.abort();
145
+ }
146
+ operationIdRef.current++;
147
+ };
81
148
  }, []);
82
149
 
83
150
  return { generate, generateJSON, result, jsonResult, isGenerating, error, reset };
@@ -1,7 +1,3 @@
1
- /**
2
- * Provider Configuration
3
- * Centralized configuration for AI provider with simplified settings
4
- */
5
1
 
6
2
  import { DEFAULT_MODELS } from "../domain/entities";
7
3
 
@@ -27,16 +23,10 @@ export interface ResolvedProviderConfig {
27
23
  timeout: number;
28
24
  }
29
25
 
30
- /**
31
- * Default configuration values
32
- */
33
26
  const DEFAULTS = {
34
27
  timeout: 30000,
35
28
  };
36
29
 
37
- /**
38
- * Resolve provider configuration based on preferences
39
- */
40
30
  export function resolveProviderConfig(
41
31
  input: ProviderConfigInput,
42
32
  ): ResolvedProviderConfig {
@@ -48,25 +38,3 @@ export function resolveProviderConfig(
48
38
  timeout: preferences.timeout ?? DEFAULTS.timeout,
49
39
  };
50
40
  }
51
-
52
- /**
53
- * Get cost-optimized config
54
- */
55
- export function getCostOptimizedConfig(
56
- input: ProviderConfigInput,
57
- ): ResolvedProviderConfig {
58
- return resolveProviderConfig(input);
59
- }
60
-
61
- /**
62
- * Get quality-optimized config
63
- */
64
- export function getQualityOptimizedConfig(
65
- input: ProviderConfigInput,
66
- ): ResolvedProviderConfig {
67
- const resolved = resolveProviderConfig(input);
68
- return {
69
- ...resolved,
70
- timeout: 60000, // Longer timeout
71
- };
72
- }
@@ -1,7 +1,3 @@
1
- /**
2
- * Provider Factory
3
- * Creates and configures AI provider instances with simplified settings
4
- */
5
1
 
6
2
  import { geminiClientCoreService } from "../infrastructure/services/gemini-client-core.service";
7
3
  import type { GeminiConfig } from "../domain/entities";
@@ -9,17 +5,11 @@ import type {
9
5
  ProviderConfigInput,
10
6
  ResolvedProviderConfig,
11
7
  } from "./ProviderConfig";
12
- import {
13
- resolveProviderConfig,
14
- getCostOptimizedConfig,
15
- getQualityOptimizedConfig,
16
- } from "./ProviderConfig";
17
-
18
- export type OptimizationStrategy = "cost" | "quality" | "balanced";
8
+ import { resolveProviderConfig } from "./ProviderConfig";
19
9
 
20
10
  export interface ProviderFactoryOptions extends ProviderConfigInput {
21
- /** Optimization strategy */
22
- strategy?: OptimizationStrategy;
11
+ /** Quality preference strategy */
12
+ strategy?: "cost" | "quality";
23
13
  }
24
14
 
25
15
  class ProviderFactory {
@@ -29,20 +19,11 @@ class ProviderFactory {
29
19
  * Initialize provider with configuration
30
20
  */
31
21
  initialize(options: ProviderFactoryOptions): void {
32
- let config: ResolvedProviderConfig;
22
+ const config = resolveProviderConfig(options);
33
23
 
34
- // Apply optimization strategy
35
- switch (options.strategy) {
36
- case "cost":
37
- config = getCostOptimizedConfig(options);
38
- break;
39
- case "quality":
40
- config = getQualityOptimizedConfig(options);
41
- break;
42
- case "balanced":
43
- default:
44
- config = resolveProviderConfig(options);
45
- break;
24
+ // Apply strategy-based adjustments
25
+ if (options.strategy === "quality") {
26
+ config.timeout = 60000; // Longer timeout for quality
46
27
  }
47
28
 
48
29
  this.currentConfig = config;
@@ -73,23 +54,29 @@ class ProviderFactory {
73
54
 
74
55
  /**
75
56
  * Update configuration without re-initializing
76
- * Useful for switching models or adjusting settings
57
+ * Note: Changing apiKey requires full re-initialization
77
58
  */
78
59
  updateConfig(updates: Partial<ProviderConfigInput>): void {
79
60
  if (!this.currentConfig) {
80
- throw new Error(
81
- "Provider not initialized. Call initialize() first.",
82
- );
61
+ throw new Error("Provider not initialized. Call initialize() first.");
83
62
  }
84
63
 
85
- const newInput: ProviderConfigInput = {
86
- apiKey: updates.apiKey || this.currentConfig.apiKey,
87
- preferences: {
88
- ...updates.preferences,
89
- },
90
- };
64
+ // If API key is changing, we need to re-initialize
65
+ if (updates.apiKey && updates.apiKey !== this.currentConfig.apiKey) {
66
+ const newInput: ProviderConfigInput = {
67
+ apiKey: updates.apiKey,
68
+ preferences: updates.preferences || {},
69
+ };
70
+ this.initialize(newInput);
71
+ return;
72
+ }
91
73
 
92
- this.initialize(newInput);
74
+ // For other updates, merge with current config
75
+ this.currentConfig = {
76
+ ...this.currentConfig,
77
+ ...updates.preferences,
78
+ timeout: updates.preferences?.timeout ?? this.currentConfig.timeout,
79
+ };
93
80
  }
94
81
  }
95
82
 
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Provider Configuration & Factory
3
- * Centralized configuration system for tier-based AI provider setup
3
+ * Centralized configuration system for AI provider setup
4
4
  */
5
5
 
6
- export {
7
- resolveProviderConfig,
8
- getCostOptimizedConfig,
9
- getQualityOptimizedConfig,
10
- } from "./ProviderConfig";
6
+ export { resolveProviderConfig } from "./ProviderConfig";
11
7
 
12
8
  export type {
13
9
  QualityPreference,
@@ -19,6 +15,5 @@ export type {
19
15
  export { providerFactory } from "./ProviderFactory";
20
16
 
21
17
  export type {
22
- OptimizationStrategy,
23
18
  ProviderFactoryOptions,
24
19
  } from "./ProviderFactory";