@realtimex/email-automator 2.11.0 → 2.11.1

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.
@@ -1,6 +1,7 @@
1
1
  import { Router, Request, Response } from 'express';
2
2
  import { SDKService } from '../services/SDKService.js';
3
3
  import { ProvidersResponse } from '@realtimex/sdk';
4
+ import { apiRateLimit } from '../middleware/rateLimit.js';
4
5
 
5
6
  const router = Router();
6
7
 
@@ -8,7 +9,7 @@ const router = Router();
8
9
  * GET /api/sdk/providers/chat
9
10
  * Returns available chat providers and their models
10
11
  */
11
- router.get('/providers/chat', async (req: Request, res: Response) => {
12
+ router.get('/providers/chat', apiRateLimit, async (req: Request, res: Response) => {
12
13
  try {
13
14
  const sdk = SDKService.getSDK();
14
15
  if (!sdk) {
@@ -23,7 +24,6 @@ router.get('/providers/chat', async (req: Request, res: Response) => {
23
24
 
24
25
  res.json({ success: true, providers: providers || [] });
25
26
  } catch (error: any) {
26
- console.warn('[SDK API] Failed to fetch chat providers:', error.message);
27
27
  res.json({ success: false, providers: [], message: error.message });
28
28
  }
29
29
  });
@@ -32,7 +32,7 @@ router.get('/providers/chat', async (req: Request, res: Response) => {
32
32
  * GET /api/sdk/providers/embed
33
33
  * Returns available embedding providers and their models
34
34
  */
35
- router.get('/providers/embed', async (req: Request, res: Response) => {
35
+ router.get('/providers/embed', apiRateLimit, async (req: Request, res: Response) => {
36
36
  try {
37
37
  const sdk = SDKService.getSDK();
38
38
  if (!sdk) {
@@ -47,7 +47,6 @@ router.get('/providers/embed', async (req: Request, res: Response) => {
47
47
 
48
48
  res.json({ success: true, providers: providers || [] });
49
49
  } catch (error: any) {
50
- console.warn('[SDK API] Failed to fetch embed providers:', error.message);
51
50
  res.json({ success: false, providers: [], message: error.message });
52
51
  }
53
52
  });
@@ -165,10 +165,14 @@ router.post('/test-llm',
165
165
  apiRateLimit,
166
166
  authMiddleware,
167
167
  asyncHandler(async (req, res) => {
168
+ const { llm_provider, llm_model } = req.body;
168
169
  const { getIntelligenceService } = await import('../services/intelligence.js');
169
170
  const intelligence = getIntelligenceService();
170
171
 
171
- const result = await intelligence.testConnection();
172
+ const result = await intelligence.testConnection({
173
+ provider: llm_provider,
174
+ model: llm_model
175
+ });
172
176
  res.json(result);
173
177
  })
174
178
  );
@@ -1,6 +1,15 @@
1
1
  import { RealtimeXSDK, ProvidersResponse } from '@realtimex/sdk';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
+ import { createLogger } from '../utils/logger.js';
5
+
6
+ const logger = createLogger('SDKService');
7
+
8
+ export interface ProviderResult {
9
+ provider: string;
10
+ model: string;
11
+ isDefaultFallback?: boolean;
12
+ }
4
13
 
5
14
  /**
6
15
  * Centralized SDK Service
@@ -10,6 +19,13 @@ export class SDKService {
10
19
  private static instance: RealtimeXSDK | null = null;
11
20
  private static initAttempted = false;
12
21
 
22
+ // Default provider/model configuration
23
+ // realtimexai routes through RealTimeX Desktop to user's configured providers
24
+ static readonly DEFAULT_LLM_PROVIDER = 'realtimexai';
25
+ static readonly DEFAULT_LLM_MODEL = 'gpt-4o-mini';
26
+ static readonly DEFAULT_EMBED_PROVIDER = 'realtimexai';
27
+ static readonly DEFAULT_EMBED_MODEL = 'text-embedding-3-small';
28
+
13
29
  /**
14
30
  * Initialize SDK with required permissions
15
31
  * Safe to call multiple times - returns existing instance
@@ -39,16 +55,16 @@ export class SDKService {
39
55
  ],
40
56
  });
41
57
 
42
- console.log('[SDKService] RealTimeX SDK initialized successfully');
58
+ logger.info('RealTimeX SDK initialized successfully');
43
59
 
44
60
  // Verify connection with Desktop App
45
61
  // @ts-ignore - Dev Mode feature
46
62
  this.instance.ping()
47
- .then((status: any) => console.log('[SDKService] Desktop App Connection:', status))
48
- .catch((err: any) => console.warn('[SDKService] Desktop App Connection failed:', err.message));
63
+ .then((status: any) => logger.debug('Desktop App Connection:', { status }))
64
+ .catch((err: any) => logger.warn('Desktop App Connection failed', { error: err.message }));
49
65
 
50
66
  } catch (error: any) {
51
- console.warn('[SDKService] Failed to initialize SDK:', error.message);
67
+ logger.error('Failed to initialize SDK', error);
52
68
  this.instance = null;
53
69
  }
54
70
  }
@@ -84,8 +100,8 @@ export class SDKService {
84
100
  await sdk.llm.chatProviders();
85
101
  return true;
86
102
  }
87
- } catch (error) {
88
- console.warn('[SDKService] SDK not available:', error);
103
+ } catch (error: any) {
104
+ logger.warn('SDK not available', { error: error.message });
89
105
  return false;
90
106
  }
91
107
  }
@@ -108,14 +124,14 @@ export class SDKService {
108
124
  }
109
125
 
110
126
  // Cache for default providers (avoid repeated SDK calls)
111
- private static defaultChatProvider: { provider: string; model: string } | null = null;
112
- private static defaultEmbedProvider: { provider: string; model: string } | null = null;
127
+ private static defaultChatProvider: ProviderResult | null = null;
128
+ private static defaultEmbedProvider: ProviderResult | null = null;
113
129
 
114
130
  /**
115
131
  * Get default chat provider/model from SDK dynamically
116
132
  * Caches result to avoid repeated SDK calls
117
133
  */
118
- static async getDefaultChatProvider(): Promise<{ provider: string; model: string }> {
134
+ static async getDefaultChatProvider(): Promise<ProviderResult> {
119
135
  // Return cached if available
120
136
  if (this.defaultChatProvider) {
121
137
  return this.defaultChatProvider;
@@ -137,22 +153,40 @@ export class SDKService {
137
153
  throw new Error('No LLM providers available. Please configure a provider in RealTimeX Desktop.');
138
154
  }
139
155
 
140
- // Find first provider with available models
156
+ // 1. Try to find the preferred default provider (realtimexai)
157
+ const preferredProvider = providers.find(p => p.provider === this.DEFAULT_LLM_PROVIDER);
158
+ if (preferredProvider && preferredProvider.models && preferredProvider.models.length > 0) {
159
+ // Try to find the preferred default model (gpt-4o-mini) within it
160
+ const preferredModel = preferredProvider.models.find(m => m.id === this.DEFAULT_LLM_MODEL) || preferredProvider.models[0];
161
+
162
+ this.defaultChatProvider = {
163
+ provider: preferredProvider.provider,
164
+ model: preferredModel.id
165
+ };
166
+ logger.info(`Using preferred default chat provider: ${this.defaultChatProvider.provider}/${this.defaultChatProvider.model}`);
167
+ return this.defaultChatProvider;
168
+ }
169
+
170
+ // 2. Fallback to the first provider with available models
141
171
  for (const p of providers) {
142
172
  if (p.models && p.models.length > 0) {
143
173
  this.defaultChatProvider = {
144
174
  provider: p.provider,
145
175
  model: p.models[0].id
146
176
  };
147
- console.log(`[SDKService] Default chat provider: ${this.defaultChatProvider.provider}/${this.defaultChatProvider.model}`);
177
+ logger.info(`Defaulting to first available chat provider: ${this.defaultChatProvider.provider}/${this.defaultChatProvider.model}`);
148
178
  return this.defaultChatProvider;
149
179
  }
150
180
  }
151
181
 
152
182
  throw new Error('No LLM models available. Please configure a model in RealTimeX Desktop.');
153
183
  } catch (error: any) {
154
- console.error('[SDKService] Failed to get default chat provider:', error.message);
155
- throw error;
184
+ logger.warn('Failed to get default chat provider from SDK. Using hardcoded fallback.', error);
185
+ return {
186
+ provider: this.DEFAULT_LLM_PROVIDER,
187
+ model: this.DEFAULT_LLM_MODEL,
188
+ isDefaultFallback: true
189
+ };
156
190
  }
157
191
  }
158
192
 
@@ -160,7 +194,7 @@ export class SDKService {
160
194
  * Get default embedding provider/model from SDK dynamically
161
195
  * Caches result to avoid repeated SDK calls
162
196
  */
163
- static async getDefaultEmbedProvider(): Promise<{ provider: string; model: string }> {
197
+ static async getDefaultEmbedProvider(): Promise<ProviderResult> {
164
198
  // Return cached if available
165
199
  if (this.defaultEmbedProvider) {
166
200
  return this.defaultEmbedProvider;
@@ -189,61 +223,46 @@ export class SDKService {
189
223
  provider: p.provider,
190
224
  model: p.models[0].id
191
225
  };
192
- console.log(`[SDKService] Default embed provider: ${this.defaultEmbedProvider.provider}/${this.defaultEmbedProvider.model}`);
226
+ logger.info(`Default embed provider: ${this.defaultEmbedProvider.provider}/${this.defaultEmbedProvider.model}`);
193
227
  return this.defaultEmbedProvider;
194
228
  }
195
229
  }
196
230
 
197
231
  throw new Error('No embedding models available. Please configure a model in RealTimeX Desktop.');
198
232
  } catch (error: any) {
199
- console.error('[SDKService] Failed to get default embed provider:', error.message);
200
- throw error;
233
+ logger.warn('Failed to get default embed provider from SDK. Using hardcoded fallback.', error);
234
+ return {
235
+ provider: this.DEFAULT_EMBED_PROVIDER,
236
+ model: this.DEFAULT_EMBED_MODEL,
237
+ isDefaultFallback: true
238
+ };
201
239
  }
202
240
  }
203
241
 
204
- // Default provider/model configuration
205
- // realtimexai routes through RealTimeX Desktop to user's configured providers
206
- static readonly DEFAULT_LLM_PROVIDER = 'realtimexai';
207
- static readonly DEFAULT_LLM_MODEL = 'gpt-4o-mini';
208
- static readonly DEFAULT_EMBED_PROVIDER = 'realtimexai';
209
- static readonly DEFAULT_EMBED_MODEL = 'text-embedding-3-small';
210
-
211
242
  /**
212
243
  * Resolve LLM provider/model - use settings if available, otherwise use defaults
213
244
  */
214
- static async resolveChatProvider(settings: { llm_provider?: string; llm_model?: string }): Promise<{ provider: string; model: string }> {
245
+ static async resolveChatProvider(settings: { llm_provider?: string; llm_model?: string }): Promise<ProviderResult> {
215
246
  // If both provider and model are set in settings, use them
216
247
  if (settings.llm_provider && settings.llm_model) {
217
248
  return { provider: settings.llm_provider, model: settings.llm_model };
218
249
  }
219
250
 
220
251
  // Try to get from SDK discovery first
221
- try {
222
- return await this.getDefaultChatProvider();
223
- } catch (error) {
224
- // Fallback to hardcoded defaults if SDK discovery fails
225
- console.log(`[SDKService] Using default LLM: ${this.DEFAULT_LLM_PROVIDER}/${this.DEFAULT_LLM_MODEL}`);
226
- return { provider: this.DEFAULT_LLM_PROVIDER, model: this.DEFAULT_LLM_MODEL };
227
- }
252
+ return await this.getDefaultChatProvider();
228
253
  }
229
254
 
230
255
  /**
231
256
  * Resolve embedding provider/model - use settings if available, otherwise use defaults
232
257
  */
233
- static async resolveEmbedProvider(settings: { embedding_provider?: string; embedding_model?: string }): Promise<{ provider: string; model: string }> {
258
+ static async resolveEmbedProvider(settings: { embedding_provider?: string; embedding_model?: string }): Promise<ProviderResult> {
234
259
  // If both provider and model are set in settings, use them
235
260
  if (settings.embedding_provider && settings.embedding_model) {
236
261
  return { provider: settings.embedding_provider, model: settings.embedding_model };
237
262
  }
238
263
 
239
264
  // Try to get from SDK discovery first
240
- try {
241
- return await this.getDefaultEmbedProvider();
242
- } catch (error) {
243
- // Fallback to hardcoded defaults if SDK discovery fails
244
- console.log(`[SDKService] Using default embedding: ${this.DEFAULT_EMBED_PROVIDER}/${this.DEFAULT_EMBED_MODEL}`);
245
- return { provider: this.DEFAULT_EMBED_PROVIDER, model: this.DEFAULT_EMBED_MODEL };
246
- }
265
+ return await this.getDefaultEmbedProvider();
247
266
  }
248
267
 
249
268
  /**
@@ -91,9 +91,7 @@ export class IntelligenceService {
91
91
  return this.isConfigured && !!SDKService.getSDK();
92
92
  }
93
93
 
94
- async analyzeEmail(content: string, context: EmailContext, eventLogger?: EventLogger, emailId?: string): Promise<EmailAnalysis | null> {
95
- console.log('[Intelligence] analyzeEmail called for:', context.subject);
96
-
94
+ async analyzeEmail(content: string, context: EmailContext, eventLogger?: EventLogger, emailId?: string): Promise<(EmailAnalysis & { _metadata?: any }) | null> {
97
95
  const sdk = SDKService.getSDK();
98
96
  if (!sdk) {
99
97
  logger.warn('Intelligence service not ready, skipping analysis');
@@ -103,7 +101,7 @@ export class IntelligenceService {
103
101
  return null;
104
102
  }
105
103
 
106
- const { provider, model } = await SDKService.resolveChatProvider({});
104
+ const { provider, model, isDefaultFallback } = await SDKService.resolveChatProvider({});
107
105
 
108
106
  const cleanedContent = ContentCleaner.cleanEmailBody(content).substring(0, 2500);
109
107
 
@@ -138,6 +136,7 @@ REQUIRED JSON STRUCTURE:
138
136
  await eventLogger.info('Thinking', `Analyzing email: ${context.subject}`, {
139
137
  model,
140
138
  provider,
139
+ is_fallback: isDefaultFallback,
141
140
  content_preview: cleanedContent
142
141
  }, emailId);
143
142
  }
@@ -151,14 +150,24 @@ REQUIRED JSON STRUCTURE:
151
150
  const rawResponse = response.response?.content || '';
152
151
  const validated = this.parseRobustJSON<EmailAnalysis>(rawResponse, EmailAnalysisSchema);
153
152
 
154
- if (eventLogger && emailId && validated) {
153
+ const result = validated ? {
154
+ ...validated,
155
+ _metadata: {
156
+ provider,
157
+ model,
158
+ is_fallback: isDefaultFallback,
159
+ timestamp: new Date().toISOString()
160
+ }
161
+ } : null;
162
+
163
+ if (eventLogger && emailId && result) {
155
164
  await eventLogger.analysis('Decided', emailId, {
156
- ...validated,
165
+ ...result,
157
166
  _raw_response: rawResponse
158
167
  });
159
168
  }
160
169
 
161
- return validated;
170
+ return result;
162
171
  } catch (error: any) {
163
172
  logger.error('Analysis failed', error);
164
173
  if (eventLogger) await eventLogger.error('Error', error.message, emailId);
@@ -200,11 +209,11 @@ REQUIRED JSON STRUCTURE:
200
209
  compiledRulesContext: string | RuleContext[],
201
210
  eventLogger?: EventLogger,
202
211
  emailId?: string
203
- ): Promise<ContextAwareAnalysis | null> {
212
+ ): Promise<(ContextAwareAnalysis & { _metadata?: any }) | null> {
204
213
  const sdk = SDKService.getSDK();
205
214
  if (!sdk) return null;
206
215
 
207
- const { provider, model } = await SDKService.resolveChatProvider({});
216
+ const { provider, model, isDefaultFallback } = await SDKService.resolveChatProvider({});
208
217
  const cleanedContent = ContentCleaner.cleanEmailBody(content).substring(0, 2500);
209
218
 
210
219
  let rulesContext: string;
@@ -217,7 +226,11 @@ REQUIRED JSON STRUCTURE:
217
226
  const systemPrompt = `You are an AI Automation Agent. Match email against these rules:\n${rulesContext}\n\nReturn JSON with matched_rule, actions_to_execute, and draft_content.`;
218
227
 
219
228
  if (eventLogger) {
220
- await eventLogger.info('Thinking', `Context-aware analysis: ${context.subject}`, { model, provider }, emailId);
229
+ await eventLogger.info('Thinking', `Context-aware analysis: ${context.subject}`, {
230
+ model,
231
+ provider,
232
+ is_fallback: isDefaultFallback
233
+ }, emailId);
221
234
  }
222
235
 
223
236
  try {
@@ -229,14 +242,24 @@ REQUIRED JSON STRUCTURE:
229
242
  const rawResponse = response.response?.content || '';
230
243
  const validated = this.parseRobustJSON<ContextAwareAnalysis>(rawResponse, ContextAwareAnalysisSchema);
231
244
 
232
- if (eventLogger && emailId && validated) {
245
+ const result = validated ? {
246
+ ...validated,
247
+ _metadata: {
248
+ provider,
249
+ model,
250
+ is_fallback: isDefaultFallback,
251
+ timestamp: new Date().toISOString()
252
+ }
253
+ } : null;
254
+
255
+ if (eventLogger && emailId && result) {
233
256
  await eventLogger.analysis('Decided', emailId, {
234
- ...validated,
257
+ ...result,
235
258
  _raw_response: rawResponse
236
259
  });
237
260
  }
238
261
 
239
- return validated;
262
+ return result;
240
263
  } catch (error: any) {
241
264
  logger.error('Rule analysis failed', error);
242
265
  if (eventLogger) await eventLogger.error('Error', error.message, emailId);
@@ -244,12 +267,15 @@ REQUIRED JSON STRUCTURE:
244
267
  }
245
268
  }
246
269
 
247
- async testConnection(): Promise<{ success: boolean; message: string }> {
270
+ async testConnection(overrides?: { provider?: string; model?: string }): Promise<{ success: boolean; message: string }> {
248
271
  const sdk = SDKService.getSDK();
249
272
  if (!sdk) return { success: false, message: 'SDK not linked' };
250
273
 
251
274
  try {
252
- const { provider, model } = await SDKService.resolveChatProvider({});
275
+ const { provider, model } = await SDKService.resolveChatProvider({
276
+ llm_provider: overrides?.provider,
277
+ llm_model: overrides?.model
278
+ });
253
279
  await sdk.llm.chat([{ role: 'user', content: 'Say OK' }], { provider, model });
254
280
  return { success: true, message: `Connected to ${provider}/${model}` };
255
281
  } catch (error: any) {
@@ -272,7 +298,7 @@ REQUIRED JSON STRUCTURE:
272
298
 
273
299
  let defaultInstance: IntelligenceService | null = null;
274
300
 
275
- export function getIntelligenceService(overrides?: any): IntelligenceService {
301
+ export function getIntelligenceService(): IntelligenceService {
276
302
  if (!defaultInstance) {
277
303
  defaultInstance = new IntelligenceService();
278
304
  }
@@ -1,11 +1,12 @@
1
1
  import { Router } from 'express';
2
2
  import { SDKService } from '../services/SDKService.js';
3
+ import { apiRateLimit } from '../middleware/rateLimit.js';
3
4
  const router = Router();
4
5
  /**
5
6
  * GET /api/sdk/providers/chat
6
7
  * Returns available chat providers and their models
7
8
  */
8
- router.get('/providers/chat', async (req, res) => {
9
+ router.get('/providers/chat', apiRateLimit, async (req, res) => {
9
10
  try {
10
11
  const sdk = SDKService.getSDK();
11
12
  if (!sdk) {
@@ -15,7 +16,6 @@ router.get('/providers/chat', async (req, res) => {
15
16
  res.json({ success: true, providers: providers || [] });
16
17
  }
17
18
  catch (error) {
18
- console.warn('[SDK API] Failed to fetch chat providers:', error.message);
19
19
  res.json({ success: false, providers: [], message: error.message });
20
20
  }
21
21
  });
@@ -23,7 +23,7 @@ router.get('/providers/chat', async (req, res) => {
23
23
  * GET /api/sdk/providers/embed
24
24
  * Returns available embedding providers and their models
25
25
  */
26
- router.get('/providers/embed', async (req, res) => {
26
+ router.get('/providers/embed', apiRateLimit, async (req, res) => {
27
27
  try {
28
28
  const sdk = SDKService.getSDK();
29
29
  if (!sdk) {
@@ -33,7 +33,6 @@ router.get('/providers/embed', async (req, res) => {
33
33
  res.json({ success: true, providers: providers || [] });
34
34
  }
35
35
  catch (error) {
36
- console.warn('[SDK API] Failed to fetch embed providers:', error.message);
37
36
  res.json({ success: false, providers: [], message: error.message });
38
37
  }
39
38
  });
@@ -129,9 +129,13 @@ router.patch('/', apiRateLimit, authMiddleware, validateBody(schemas.updateSetti
129
129
  }));
130
130
  // Test LLM Connection
131
131
  router.post('/test-llm', apiRateLimit, authMiddleware, asyncHandler(async (req, res) => {
132
+ const { llm_provider, llm_model } = req.body;
132
133
  const { getIntelligenceService } = await import('../services/intelligence.js');
133
134
  const intelligence = getIntelligenceService();
134
- const result = await intelligence.testConnection();
135
+ const result = await intelligence.testConnection({
136
+ provider: llm_provider,
137
+ model: llm_model
138
+ });
135
139
  res.json(result);
136
140
  }));
137
141
  // Get analytics/stats
@@ -1,4 +1,6 @@
1
1
  import { RealtimeXSDK } from '@realtimex/sdk';
2
+ import { createLogger } from '../utils/logger.js';
3
+ const logger = createLogger('SDKService');
2
4
  /**
3
5
  * Centralized SDK Service
4
6
  * Manages RealTimeX SDK initialization and provides singleton access
@@ -6,6 +8,12 @@ import { RealtimeXSDK } from '@realtimex/sdk';
6
8
  export class SDKService {
7
9
  static instance = null;
8
10
  static initAttempted = false;
11
+ // Default provider/model configuration
12
+ // realtimexai routes through RealTimeX Desktop to user's configured providers
13
+ static DEFAULT_LLM_PROVIDER = 'realtimexai';
14
+ static DEFAULT_LLM_MODEL = 'gpt-4o-mini';
15
+ static DEFAULT_EMBED_PROVIDER = 'realtimexai';
16
+ static DEFAULT_EMBED_MODEL = 'text-embedding-3-small';
9
17
  /**
10
18
  * Initialize SDK with required permissions
11
19
  * Safe to call multiple times - returns existing instance
@@ -33,15 +41,15 @@ export class SDKService {
33
41
  'vectors.write', // Store vectors
34
42
  ],
35
43
  });
36
- console.log('[SDKService] RealTimeX SDK initialized successfully');
44
+ logger.info('RealTimeX SDK initialized successfully');
37
45
  // Verify connection with Desktop App
38
46
  // @ts-ignore - Dev Mode feature
39
47
  this.instance.ping()
40
- .then((status) => console.log('[SDKService] Desktop App Connection:', status))
41
- .catch((err) => console.warn('[SDKService] Desktop App Connection failed:', err.message));
48
+ .then((status) => logger.debug('Desktop App Connection:', { status }))
49
+ .catch((err) => logger.warn('Desktop App Connection failed', { error: err.message }));
42
50
  }
43
51
  catch (error) {
44
- console.warn('[SDKService] Failed to initialize SDK:', error.message);
52
+ logger.error('Failed to initialize SDK', error);
45
53
  this.instance = null;
46
54
  }
47
55
  }
@@ -77,7 +85,7 @@ export class SDKService {
77
85
  }
78
86
  }
79
87
  catch (error) {
80
- console.warn('[SDKService] SDK not available:', error);
88
+ logger.warn('SDK not available', { error: error.message });
81
89
  return false;
82
90
  }
83
91
  }
@@ -118,22 +126,38 @@ export class SDKService {
118
126
  if (!providers || providers.length === 0) {
119
127
  throw new Error('No LLM providers available. Please configure a provider in RealTimeX Desktop.');
120
128
  }
121
- // Find first provider with available models
129
+ // 1. Try to find the preferred default provider (realtimexai)
130
+ const preferredProvider = providers.find(p => p.provider === this.DEFAULT_LLM_PROVIDER);
131
+ if (preferredProvider && preferredProvider.models && preferredProvider.models.length > 0) {
132
+ // Try to find the preferred default model (gpt-4o-mini) within it
133
+ const preferredModel = preferredProvider.models.find(m => m.id === this.DEFAULT_LLM_MODEL) || preferredProvider.models[0];
134
+ this.defaultChatProvider = {
135
+ provider: preferredProvider.provider,
136
+ model: preferredModel.id
137
+ };
138
+ logger.info(`Using preferred default chat provider: ${this.defaultChatProvider.provider}/${this.defaultChatProvider.model}`);
139
+ return this.defaultChatProvider;
140
+ }
141
+ // 2. Fallback to the first provider with available models
122
142
  for (const p of providers) {
123
143
  if (p.models && p.models.length > 0) {
124
144
  this.defaultChatProvider = {
125
145
  provider: p.provider,
126
146
  model: p.models[0].id
127
147
  };
128
- console.log(`[SDKService] Default chat provider: ${this.defaultChatProvider.provider}/${this.defaultChatProvider.model}`);
148
+ logger.info(`Defaulting to first available chat provider: ${this.defaultChatProvider.provider}/${this.defaultChatProvider.model}`);
129
149
  return this.defaultChatProvider;
130
150
  }
131
151
  }
132
152
  throw new Error('No LLM models available. Please configure a model in RealTimeX Desktop.');
133
153
  }
134
154
  catch (error) {
135
- console.error('[SDKService] Failed to get default chat provider:', error.message);
136
- throw error;
155
+ logger.warn('Failed to get default chat provider from SDK. Using hardcoded fallback.', error);
156
+ return {
157
+ provider: this.DEFAULT_LLM_PROVIDER,
158
+ model: this.DEFAULT_LLM_MODEL,
159
+ isDefaultFallback: true
160
+ };
137
161
  }
138
162
  }
139
163
  /**
@@ -161,23 +185,21 @@ export class SDKService {
161
185
  provider: p.provider,
162
186
  model: p.models[0].id
163
187
  };
164
- console.log(`[SDKService] Default embed provider: ${this.defaultEmbedProvider.provider}/${this.defaultEmbedProvider.model}`);
188
+ logger.info(`Default embed provider: ${this.defaultEmbedProvider.provider}/${this.defaultEmbedProvider.model}`);
165
189
  return this.defaultEmbedProvider;
166
190
  }
167
191
  }
168
192
  throw new Error('No embedding models available. Please configure a model in RealTimeX Desktop.');
169
193
  }
170
194
  catch (error) {
171
- console.error('[SDKService] Failed to get default embed provider:', error.message);
172
- throw error;
195
+ logger.warn('Failed to get default embed provider from SDK. Using hardcoded fallback.', error);
196
+ return {
197
+ provider: this.DEFAULT_EMBED_PROVIDER,
198
+ model: this.DEFAULT_EMBED_MODEL,
199
+ isDefaultFallback: true
200
+ };
173
201
  }
174
202
  }
175
- // Default provider/model configuration
176
- // realtimexai routes through RealTimeX Desktop to user's configured providers
177
- static DEFAULT_LLM_PROVIDER = 'realtimexai';
178
- static DEFAULT_LLM_MODEL = 'gpt-4o-mini';
179
- static DEFAULT_EMBED_PROVIDER = 'realtimexai';
180
- static DEFAULT_EMBED_MODEL = 'text-embedding-3-small';
181
203
  /**
182
204
  * Resolve LLM provider/model - use settings if available, otherwise use defaults
183
205
  */
@@ -187,14 +209,7 @@ export class SDKService {
187
209
  return { provider: settings.llm_provider, model: settings.llm_model };
188
210
  }
189
211
  // Try to get from SDK discovery first
190
- try {
191
- return await this.getDefaultChatProvider();
192
- }
193
- catch (error) {
194
- // Fallback to hardcoded defaults if SDK discovery fails
195
- console.log(`[SDKService] Using default LLM: ${this.DEFAULT_LLM_PROVIDER}/${this.DEFAULT_LLM_MODEL}`);
196
- return { provider: this.DEFAULT_LLM_PROVIDER, model: this.DEFAULT_LLM_MODEL };
197
- }
212
+ return await this.getDefaultChatProvider();
198
213
  }
199
214
  /**
200
215
  * Resolve embedding provider/model - use settings if available, otherwise use defaults
@@ -205,14 +220,7 @@ export class SDKService {
205
220
  return { provider: settings.embedding_provider, model: settings.embedding_model };
206
221
  }
207
222
  // Try to get from SDK discovery first
208
- try {
209
- return await this.getDefaultEmbedProvider();
210
- }
211
- catch (error) {
212
- // Fallback to hardcoded defaults if SDK discovery fails
213
- console.log(`[SDKService] Using default embedding: ${this.DEFAULT_EMBED_PROVIDER}/${this.DEFAULT_EMBED_MODEL}`);
214
- return { provider: this.DEFAULT_EMBED_PROVIDER, model: this.DEFAULT_EMBED_MODEL };
215
- }
223
+ return await this.getDefaultEmbedProvider();
216
224
  }
217
225
  /**
218
226
  * Clear provider cache (useful when providers change)