@push.rocks/smartai 0.5.11 → 0.7.0

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.
@@ -9,13 +9,22 @@ export type TChatCompletionRequestMessage = {
9
9
  };
10
10
 
11
11
  import { MultiModalModel } from './abstract.classes.multimodal.js';
12
+ import type {
13
+ ResearchOptions,
14
+ ResearchResponse,
15
+ ImageGenerateOptions,
16
+ ImageEditOptions,
17
+ ImageResponse
18
+ } from './abstract.classes.multimodal.js';
12
19
 
13
20
  export interface IOpenaiProviderOptions {
14
21
  openaiToken: string;
15
22
  chatModel?: string;
16
23
  audioModel?: string;
17
24
  visionModel?: string;
18
- // Optionally add more model options (e.g., documentModel) if needed.
25
+ researchModel?: string;
26
+ imageModel?: string;
27
+ enableWebSearch?: boolean;
19
28
  }
20
29
 
21
30
  export class OpenAiProvider extends MultiModalModel {
@@ -229,4 +238,218 @@ export class OpenAiProvider extends MultiModalModel {
229
238
  const result = await this.openAiApiClient.chat.completions.create(requestParams);
230
239
  return result.choices[0].message.content || '';
231
240
  }
241
+
242
+ public async research(optionsArg: ResearchOptions): Promise<ResearchResponse> {
243
+ // Determine which model to use - Deep Research API requires specific models
244
+ let model: string;
245
+ if (optionsArg.searchDepth === 'deep') {
246
+ model = this.options.researchModel || 'o4-mini-deep-research-2025-06-26';
247
+ } else {
248
+ // For basic/advanced, still use deep research models if web search is needed
249
+ if (optionsArg.includeWebSearch) {
250
+ model = this.options.researchModel || 'o4-mini-deep-research-2025-06-26';
251
+ } else {
252
+ model = this.options.chatModel || 'gpt-5-mini';
253
+ }
254
+ }
255
+
256
+ const systemMessage = 'You are a research assistant. Provide comprehensive answers with citations and sources when available.';
257
+
258
+ // Prepare request parameters using Deep Research API format
259
+ const requestParams: any = {
260
+ model,
261
+ instructions: systemMessage,
262
+ input: optionsArg.query
263
+ };
264
+
265
+ // Add web search tool if requested
266
+ if (optionsArg.includeWebSearch || optionsArg.searchDepth === 'deep') {
267
+ requestParams.tools = [
268
+ {
269
+ type: 'web_search_preview',
270
+ search_context_size: optionsArg.searchDepth === 'deep' ? 'high' :
271
+ optionsArg.searchDepth === 'advanced' ? 'medium' : 'low'
272
+ }
273
+ ];
274
+ }
275
+
276
+ // Add background flag for deep research
277
+ if (optionsArg.background && optionsArg.searchDepth === 'deep') {
278
+ requestParams.background = true;
279
+ }
280
+
281
+ try {
282
+ // Execute the research request using Deep Research API
283
+ const result = await this.openAiApiClient.responses.create(requestParams);
284
+
285
+ // Extract the answer from output items
286
+ let answer = '';
287
+ const sources: Array<{ url: string; title: string; snippet: string }> = [];
288
+ const searchQueries: string[] = [];
289
+
290
+ // Process output items
291
+ for (const item of result.output || []) {
292
+ // Extract message content
293
+ if (item.type === 'message' && 'content' in item) {
294
+ const messageItem = item as any;
295
+ for (const contentItem of messageItem.content || []) {
296
+ if (contentItem.type === 'output_text' && 'text' in contentItem) {
297
+ answer += contentItem.text;
298
+ }
299
+ }
300
+ }
301
+
302
+ // Extract web search queries
303
+ if (item.type === 'web_search_call' && 'action' in item) {
304
+ const searchItem = item as any;
305
+ if (searchItem.action && searchItem.action.type === 'search' && 'query' in searchItem.action) {
306
+ searchQueries.push(searchItem.action.query);
307
+ }
308
+ }
309
+ }
310
+
311
+ // Parse sources from markdown links in the answer
312
+ const urlRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
313
+ let match: RegExpExecArray | null;
314
+
315
+ while ((match = urlRegex.exec(answer)) !== null) {
316
+ sources.push({
317
+ title: match[1],
318
+ url: match[2],
319
+ snippet: ''
320
+ });
321
+ }
322
+
323
+ return {
324
+ answer,
325
+ sources,
326
+ searchQueries: searchQueries.length > 0 ? searchQueries : undefined,
327
+ metadata: {
328
+ model,
329
+ searchDepth: optionsArg.searchDepth || 'basic',
330
+ tokensUsed: result.usage?.total_tokens
331
+ }
332
+ };
333
+ } catch (error) {
334
+ console.error('Research API error:', error);
335
+ throw new Error(`Failed to perform research: ${error.message}`);
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Image generation using OpenAI's gpt-image-1 or DALL-E models
341
+ */
342
+ public async imageGenerate(optionsArg: ImageGenerateOptions): Promise<ImageResponse> {
343
+ const model = optionsArg.model || this.options.imageModel || 'gpt-image-1';
344
+
345
+ try {
346
+ const requestParams: any = {
347
+ model,
348
+ prompt: optionsArg.prompt,
349
+ n: optionsArg.n || 1,
350
+ };
351
+
352
+ // Add gpt-image-1 specific parameters
353
+ if (model === 'gpt-image-1') {
354
+ if (optionsArg.quality) requestParams.quality = optionsArg.quality;
355
+ if (optionsArg.size) requestParams.size = optionsArg.size;
356
+ if (optionsArg.background) requestParams.background = optionsArg.background;
357
+ if (optionsArg.outputFormat) requestParams.output_format = optionsArg.outputFormat;
358
+ if (optionsArg.outputCompression !== undefined) requestParams.output_compression = optionsArg.outputCompression;
359
+ if (optionsArg.moderation) requestParams.moderation = optionsArg.moderation;
360
+ if (optionsArg.stream !== undefined) requestParams.stream = optionsArg.stream;
361
+ if (optionsArg.partialImages !== undefined) requestParams.partial_images = optionsArg.partialImages;
362
+ } else if (model === 'dall-e-3') {
363
+ // DALL-E 3 specific parameters
364
+ if (optionsArg.quality) requestParams.quality = optionsArg.quality;
365
+ if (optionsArg.size) requestParams.size = optionsArg.size;
366
+ if (optionsArg.style) requestParams.style = optionsArg.style;
367
+ requestParams.response_format = 'b64_json'; // Always use base64 for consistency
368
+ } else if (model === 'dall-e-2') {
369
+ // DALL-E 2 specific parameters
370
+ if (optionsArg.size) requestParams.size = optionsArg.size;
371
+ requestParams.response_format = 'b64_json';
372
+ }
373
+
374
+ const result = await this.openAiApiClient.images.generate(requestParams);
375
+
376
+ const images = (result.data || []).map(img => ({
377
+ b64_json: img.b64_json,
378
+ url: img.url,
379
+ revisedPrompt: img.revised_prompt
380
+ }));
381
+
382
+ return {
383
+ images,
384
+ metadata: {
385
+ model,
386
+ quality: result.quality,
387
+ size: result.size,
388
+ outputFormat: result.output_format,
389
+ tokensUsed: result.usage?.total_tokens
390
+ }
391
+ };
392
+ } catch (error) {
393
+ console.error('Image generation error:', error);
394
+ throw new Error(`Failed to generate image: ${error.message}`);
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Image editing using OpenAI's gpt-image-1 or DALL-E 2 models
400
+ */
401
+ public async imageEdit(optionsArg: ImageEditOptions): Promise<ImageResponse> {
402
+ const model = optionsArg.model || this.options.imageModel || 'gpt-image-1';
403
+
404
+ try {
405
+ const requestParams: any = {
406
+ model,
407
+ image: optionsArg.image,
408
+ prompt: optionsArg.prompt,
409
+ n: optionsArg.n || 1,
410
+ };
411
+
412
+ // Add mask if provided
413
+ if (optionsArg.mask) {
414
+ requestParams.mask = optionsArg.mask;
415
+ }
416
+
417
+ // Add gpt-image-1 specific parameters
418
+ if (model === 'gpt-image-1') {
419
+ if (optionsArg.quality) requestParams.quality = optionsArg.quality;
420
+ if (optionsArg.size) requestParams.size = optionsArg.size;
421
+ if (optionsArg.background) requestParams.background = optionsArg.background;
422
+ if (optionsArg.outputFormat) requestParams.output_format = optionsArg.outputFormat;
423
+ if (optionsArg.outputCompression !== undefined) requestParams.output_compression = optionsArg.outputCompression;
424
+ if (optionsArg.stream !== undefined) requestParams.stream = optionsArg.stream;
425
+ if (optionsArg.partialImages !== undefined) requestParams.partial_images = optionsArg.partialImages;
426
+ } else if (model === 'dall-e-2') {
427
+ // DALL-E 2 specific parameters
428
+ if (optionsArg.size) requestParams.size = optionsArg.size;
429
+ requestParams.response_format = 'b64_json';
430
+ }
431
+
432
+ const result = await this.openAiApiClient.images.edit(requestParams);
433
+
434
+ const images = (result.data || []).map(img => ({
435
+ b64_json: img.b64_json,
436
+ url: img.url,
437
+ revisedPrompt: img.revised_prompt
438
+ }));
439
+
440
+ return {
441
+ images,
442
+ metadata: {
443
+ model,
444
+ quality: result.quality,
445
+ size: result.size,
446
+ outputFormat: result.output_format,
447
+ tokensUsed: result.usage?.total_tokens
448
+ }
449
+ };
450
+ } catch (error) {
451
+ console.error('Image edit error:', error);
452
+ throw new Error(`Failed to edit image: ${error.message}`);
453
+ }
454
+ }
232
455
  }
@@ -1,7 +1,16 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import * as paths from './paths.js';
3
3
  import { MultiModalModel } from './abstract.classes.multimodal.js';
4
- import type { ChatOptions, ChatResponse, ChatMessage } from './abstract.classes.multimodal.js';
4
+ import type {
5
+ ChatOptions,
6
+ ChatResponse,
7
+ ChatMessage,
8
+ ResearchOptions,
9
+ ResearchResponse,
10
+ ImageGenerateOptions,
11
+ ImageEditOptions,
12
+ ImageResponse
13
+ } from './abstract.classes.multimodal.js';
5
14
 
6
15
  export interface IPerplexityProviderOptions {
7
16
  perplexityToken: string;
@@ -168,4 +177,83 @@ export class PerplexityProvider extends MultiModalModel {
168
177
  }): Promise<{ message: any }> {
169
178
  throw new Error('Document processing is not supported by Perplexity.');
170
179
  }
180
+
181
+ public async research(optionsArg: ResearchOptions): Promise<ResearchResponse> {
182
+ // Perplexity has Sonar models that are optimized for search
183
+ // sonar models: sonar, sonar-pro
184
+ const model = optionsArg.searchDepth === 'deep' ? 'sonar-pro' : 'sonar';
185
+
186
+ try {
187
+ const response = await fetch('https://api.perplexity.ai/chat/completions', {
188
+ method: 'POST',
189
+ headers: {
190
+ 'Authorization': `Bearer ${this.options.perplexityToken}`,
191
+ 'Content-Type': 'application/json',
192
+ },
193
+ body: JSON.stringify({
194
+ model,
195
+ messages: [
196
+ {
197
+ role: 'system',
198
+ content: 'You are a helpful research assistant. Provide accurate information with sources.'
199
+ },
200
+ {
201
+ role: 'user',
202
+ content: optionsArg.query
203
+ }
204
+ ],
205
+ temperature: 0.7,
206
+ max_tokens: 4000
207
+ }),
208
+ });
209
+
210
+ if (!response.ok) {
211
+ throw new Error(`Perplexity API error: ${response.statusText}`);
212
+ }
213
+
214
+ const result = await response.json();
215
+ const answer = result.choices[0].message.content;
216
+
217
+ // Parse citations from the response
218
+ const sources: Array<{ url: string; title: string; snippet: string }> = [];
219
+
220
+ // Perplexity includes citations in the format [1], [2], etc. with sources listed
221
+ // This is a simplified parser - could be enhanced based on actual Perplexity response format
222
+ if (result.citations) {
223
+ for (const citation of result.citations) {
224
+ sources.push({
225
+ url: citation.url || '',
226
+ title: citation.title || '',
227
+ snippet: citation.snippet || ''
228
+ });
229
+ }
230
+ }
231
+
232
+ return {
233
+ answer,
234
+ sources,
235
+ metadata: {
236
+ model,
237
+ searchDepth: optionsArg.searchDepth || 'basic'
238
+ }
239
+ };
240
+ } catch (error) {
241
+ console.error('Perplexity research error:', error);
242
+ throw new Error(`Failed to perform research: ${error.message}`);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Image generation is not supported by Perplexity
248
+ */
249
+ public async imageGenerate(optionsArg: ImageGenerateOptions): Promise<ImageResponse> {
250
+ throw new Error('Image generation is not supported by Perplexity. Please use OpenAI provider for image generation.');
251
+ }
252
+
253
+ /**
254
+ * Image editing is not supported by Perplexity
255
+ */
256
+ public async imageEdit(optionsArg: ImageEditOptions): Promise<ImageResponse> {
257
+ throw new Error('Image editing is not supported by Perplexity. Please use OpenAI provider for image editing.');
258
+ }
171
259
  }
@@ -1,7 +1,16 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import * as paths from './paths.js';
3
3
  import { MultiModalModel } from './abstract.classes.multimodal.js';
4
- import type { ChatOptions, ChatResponse, ChatMessage } from './abstract.classes.multimodal.js';
4
+ import type {
5
+ ChatOptions,
6
+ ChatResponse,
7
+ ChatMessage,
8
+ ResearchOptions,
9
+ ResearchResponse,
10
+ ImageGenerateOptions,
11
+ ImageEditOptions,
12
+ ImageResponse
13
+ } from './abstract.classes.multimodal.js';
5
14
  import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
6
15
 
7
16
  export interface IXAIProviderOptions {
@@ -181,4 +190,22 @@ export class XAIProvider extends MultiModalModel {
181
190
  message: completion.choices[0]?.message?.content || ''
182
191
  };
183
192
  }
193
+
194
+ public async research(optionsArg: ResearchOptions): Promise<ResearchResponse> {
195
+ throw new Error('Research capabilities are not yet supported by xAI provider.');
196
+ }
197
+
198
+ /**
199
+ * Image generation is not supported by xAI
200
+ */
201
+ public async imageGenerate(optionsArg: ImageGenerateOptions): Promise<ImageResponse> {
202
+ throw new Error('Image generation is not supported by xAI. Please use OpenAI provider for image generation.');
203
+ }
204
+
205
+ /**
206
+ * Image editing is not supported by xAI
207
+ */
208
+ public async imageEdit(optionsArg: ImageEditOptions): Promise<ImageResponse> {
209
+ throw new Error('Image editing is not supported by xAI. Please use OpenAI provider for image editing.');
210
+ }
184
211
  }