expo-ai-kit 0.3.6 → 0.4.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.
- package/README.md +68 -1106
- package/build/ExpoAiKitModule.d.ts +1 -1
- package/build/ExpoAiKitModule.d.ts.map +1 -1
- package/build/ExpoAiKitModule.js.map +1 -1
- package/build/index.d.ts +1 -436
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -737
- package/build/index.js.map +1 -1
- package/build/types.d.ts +3 -244
- package/build/types.d.ts.map +1 -1
- package/build/types.js.map +1 -1
- package/package.json +1 -1
- package/src/ExpoAiKitModule.ts +2 -2
- package/src/index.ts +1 -943
- package/src/types.ts +3 -284
- package/build/hooks.d.ts +0 -147
- package/build/hooks.d.ts.map +0 -1
- package/build/hooks.js +0 -426
- package/build/hooks.js.map +0 -1
- package/build/memory.d.ts +0 -235
- package/build/memory.d.ts.map +0 -1
- package/build/memory.js +0 -353
- package/build/memory.js.map +0 -1
- package/src/__tests__/index.test.js +0 -630
- package/src/hooks.ts +0 -502
- package/src/memory.ts +0 -394
package/build/index.js
CHANGED
|
@@ -3,8 +3,6 @@ import { Platform } from 'react-native';
|
|
|
3
3
|
import { ModelError, } from './types';
|
|
4
4
|
import { MODEL_REGISTRY, getRegistryEntry } from './models';
|
|
5
5
|
export * from './types';
|
|
6
|
-
export * from './memory';
|
|
7
|
-
export * from './hooks';
|
|
8
6
|
export * from './models';
|
|
9
7
|
const DEFAULT_SYSTEM_PROMPT = 'You are a helpful, friendly assistant. Answer the user directly and concisely.';
|
|
10
8
|
let streamIdCounter = 0;
|
|
@@ -12,87 +10,8 @@ function generateSessionId() {
|
|
|
12
10
|
return `stream_${Date.now()}_${++streamIdCounter}`;
|
|
13
11
|
}
|
|
14
12
|
// ============================================================================
|
|
15
|
-
//
|
|
13
|
+
// Inference API
|
|
16
14
|
// ============================================================================
|
|
17
|
-
const SUMMARIZE_LENGTH_INSTRUCTIONS = {
|
|
18
|
-
short: 'Keep it very brief, around 1-2 sentences.',
|
|
19
|
-
medium: 'Provide a moderate summary, around 3-5 sentences.',
|
|
20
|
-
long: 'Provide a comprehensive summary covering all main points.',
|
|
21
|
-
};
|
|
22
|
-
const SUMMARIZE_STYLE_INSTRUCTIONS = {
|
|
23
|
-
paragraph: 'Write the summary as a flowing paragraph.',
|
|
24
|
-
bullets: 'Format the summary as bullet points.',
|
|
25
|
-
tldr: 'Start with "TL;DR:" and give an extremely concise summary in 1 sentence.',
|
|
26
|
-
};
|
|
27
|
-
const TRANSLATE_TONE_INSTRUCTIONS = {
|
|
28
|
-
formal: 'Use formal language and honorifics where appropriate.',
|
|
29
|
-
informal: 'Use casual, everyday language.',
|
|
30
|
-
neutral: 'Use standard, neutral language.',
|
|
31
|
-
};
|
|
32
|
-
const REWRITE_STYLE_INSTRUCTIONS = {
|
|
33
|
-
formal: 'Rewrite in a formal, professional tone suitable for business communication.',
|
|
34
|
-
casual: 'Rewrite in a casual, conversational tone.',
|
|
35
|
-
professional: 'Rewrite in a clear, professional tone suitable for work contexts.',
|
|
36
|
-
friendly: 'Rewrite in a warm, friendly tone.',
|
|
37
|
-
concise: 'Rewrite to be as brief as possible while keeping the meaning intact.',
|
|
38
|
-
detailed: 'Expand and add more detail and explanation.',
|
|
39
|
-
simple: 'Rewrite using simple words and short sentences, easy for anyone to understand.',
|
|
40
|
-
academic: 'Rewrite in an academic style suitable for scholarly writing.',
|
|
41
|
-
};
|
|
42
|
-
const SUGGEST_TONE_INSTRUCTIONS = {
|
|
43
|
-
formal: 'Use formal, professional language.',
|
|
44
|
-
casual: 'Use casual, everyday language.',
|
|
45
|
-
professional: 'Use clear, professional language suitable for work.',
|
|
46
|
-
friendly: 'Use warm, friendly language.',
|
|
47
|
-
neutral: 'Use standard, neutral language.',
|
|
48
|
-
};
|
|
49
|
-
const ANSWER_DETAIL_INSTRUCTIONS = {
|
|
50
|
-
brief: 'Give a brief, direct answer in 1-2 sentences.',
|
|
51
|
-
medium: 'Provide a clear answer with some explanation.',
|
|
52
|
-
detailed: 'Provide a comprehensive answer with full explanation and relevant details from the context.',
|
|
53
|
-
};
|
|
54
|
-
// ============================================================================
|
|
55
|
-
// Prompt Builder Helpers
|
|
56
|
-
// ============================================================================
|
|
57
|
-
function buildSummarizePrompt(length, style) {
|
|
58
|
-
return `You are a summarization assistant. Summarize the provided text accurately and concisely. ${SUMMARIZE_LENGTH_INSTRUCTIONS[length]} ${SUMMARIZE_STYLE_INSTRUCTIONS[style]} Only output the summary, nothing else.`;
|
|
59
|
-
}
|
|
60
|
-
function buildTranslatePrompt(to, from, tone) {
|
|
61
|
-
const fromClause = from ? `from ${from} ` : '';
|
|
62
|
-
return `You are a translation assistant. Translate the provided text ${fromClause}to ${to}. ${TRANSLATE_TONE_INSTRUCTIONS[tone]} Only output the translation, nothing else. Do not include any explanations or notes.`;
|
|
63
|
-
}
|
|
64
|
-
function buildRewritePrompt(style) {
|
|
65
|
-
return `You are a writing assistant. ${REWRITE_STYLE_INSTRUCTIONS[style]} Preserve the original meaning. Only output the rewritten text, nothing else.`;
|
|
66
|
-
}
|
|
67
|
-
function buildExtractKeyPointsPrompt(maxPoints) {
|
|
68
|
-
return `You are an analysis assistant. Extract the ${maxPoints} most important key points from the provided text. Format each point as a bullet point starting with "•". Be concise and focus on the most significant information. Only output the bullet points, nothing else.`;
|
|
69
|
-
}
|
|
70
|
-
function buildSuggestPrompt(count, tone, context) {
|
|
71
|
-
const contextClause = context
|
|
72
|
-
? ` The user is writing in this context: "${context}".`
|
|
73
|
-
: '';
|
|
74
|
-
return `You are a text suggestion assistant. Given the user's partial text, generate exactly ${count} possible continuations or completions.${contextClause} ${SUGGEST_TONE_INSTRUCTIONS[tone]} Output ONLY the suggestions, one per line, numbered like "1. suggestion here". Do not include any other text or explanation.`;
|
|
75
|
-
}
|
|
76
|
-
function buildSmartReplyPrompt(count, tone, persona) {
|
|
77
|
-
const personaClause = persona ? ` You are replying as: ${persona}.` : '';
|
|
78
|
-
return `You are a smart reply assistant. Given a conversation, generate exactly ${count} short, contextually appropriate reply suggestions that the user could send as their next message.${personaClause} ${SUGGEST_TONE_INSTRUCTIONS[tone]} Each reply should be a complete, ready-to-send message. Output ONLY the replies, one per line, numbered like "1. reply here". Do not include any other text or explanation.`;
|
|
79
|
-
}
|
|
80
|
-
function buildAutocompletePrompt(count, maxWords, context) {
|
|
81
|
-
const contextClause = context
|
|
82
|
-
? ` The user is writing about: "${context}".`
|
|
83
|
-
: '';
|
|
84
|
-
return `You are an autocomplete assistant. Given the user's partial text, generate exactly ${count} natural completions of the current sentence or phrase. Each completion should be at most ${maxWords} words and should seamlessly continue from the user's text.${contextClause} Output ONLY the completions, one per line, numbered like "1. completion here". Do not repeat the user's text. Do not include any other text or explanation.`;
|
|
85
|
-
}
|
|
86
|
-
function parseSuggestions(raw) {
|
|
87
|
-
return raw
|
|
88
|
-
.split('\n')
|
|
89
|
-
.map((line) => line.replace(/^\d+[\.\)]\s*/, '').trim())
|
|
90
|
-
.filter((line) => line.length > 0)
|
|
91
|
-
.map((text) => ({ text }));
|
|
92
|
-
}
|
|
93
|
-
function buildAnswerQuestionPrompt(detail) {
|
|
94
|
-
return `You are a question-answering assistant. Answer questions based ONLY on the provided context. ${ANSWER_DETAIL_INSTRUCTIONS[detail]} If the answer cannot be found in the context, say so. Do not make up information.`;
|
|
95
|
-
}
|
|
96
15
|
/**
|
|
97
16
|
* Check if on-device AI is available on the current device.
|
|
98
17
|
* Returns false on unsupported platforms (web, etc.).
|
|
@@ -184,17 +103,6 @@ export async function sendMessage(messages, options) {
|
|
|
184
103
|
* // Cancel after 5 seconds
|
|
185
104
|
* setTimeout(() => stop(), 5000);
|
|
186
105
|
* ```
|
|
187
|
-
*
|
|
188
|
-
* @example
|
|
189
|
-
* ```ts
|
|
190
|
-
* // React state update pattern
|
|
191
|
-
* const [text, setText] = useState('');
|
|
192
|
-
*
|
|
193
|
-
* streamMessage(
|
|
194
|
-
* [{ role: 'user', content: 'Hello!' }],
|
|
195
|
-
* (event) => setText(event.accumulatedText)
|
|
196
|
-
* );
|
|
197
|
-
* ```
|
|
198
106
|
*/
|
|
199
107
|
export function streamMessage(messages, onToken, options) {
|
|
200
108
|
// Handle unsupported platforms
|
|
@@ -250,650 +158,6 @@ export function streamMessage(messages, onToken, options) {
|
|
|
250
158
|
return { promise, stop };
|
|
251
159
|
}
|
|
252
160
|
// ============================================================================
|
|
253
|
-
// Prompt Helpers
|
|
254
|
-
// ============================================================================
|
|
255
|
-
/**
|
|
256
|
-
* Summarize text content using on-device AI.
|
|
257
|
-
*
|
|
258
|
-
* @param text - The text to summarize
|
|
259
|
-
* @param options - Optional settings for summary style and length
|
|
260
|
-
* @returns Promise with the generated summary
|
|
261
|
-
*
|
|
262
|
-
* @example
|
|
263
|
-
* ```ts
|
|
264
|
-
* // Basic summarization
|
|
265
|
-
* const result = await summarize(longArticle);
|
|
266
|
-
* console.log(result.text);
|
|
267
|
-
* ```
|
|
268
|
-
*
|
|
269
|
-
* @example
|
|
270
|
-
* ```ts
|
|
271
|
-
* // Short bullet-point summary
|
|
272
|
-
* const result = await summarize(longArticle, {
|
|
273
|
-
* length: 'short',
|
|
274
|
-
* style: 'bullets'
|
|
275
|
-
* });
|
|
276
|
-
* ```
|
|
277
|
-
*
|
|
278
|
-
* @example
|
|
279
|
-
* ```ts
|
|
280
|
-
* // TL;DR style
|
|
281
|
-
* const result = await summarize(longArticle, {
|
|
282
|
-
* style: 'tldr'
|
|
283
|
-
* });
|
|
284
|
-
* ```
|
|
285
|
-
*/
|
|
286
|
-
export async function summarize(text, options) {
|
|
287
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
288
|
-
return { text: '' };
|
|
289
|
-
}
|
|
290
|
-
if (!text || text.trim().length === 0) {
|
|
291
|
-
throw new Error('text cannot be empty');
|
|
292
|
-
}
|
|
293
|
-
const length = options?.length ?? 'medium';
|
|
294
|
-
const style = options?.style ?? 'paragraph';
|
|
295
|
-
const systemPrompt = buildSummarizePrompt(length, style);
|
|
296
|
-
return sendMessage([{ role: 'user', content: text }], { systemPrompt });
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Summarize text with streaming output.
|
|
300
|
-
*
|
|
301
|
-
* @param text - The text to summarize
|
|
302
|
-
* @param onToken - Callback for each token received
|
|
303
|
-
* @param options - Optional settings for summary style and length
|
|
304
|
-
* @returns Object with stop() function and promise
|
|
305
|
-
*
|
|
306
|
-
* @example
|
|
307
|
-
* ```ts
|
|
308
|
-
* const { promise } = streamSummarize(
|
|
309
|
-
* longArticle,
|
|
310
|
-
* (event) => setSummary(event.accumulatedText),
|
|
311
|
-
* { style: 'bullets' }
|
|
312
|
-
* );
|
|
313
|
-
* await promise;
|
|
314
|
-
* ```
|
|
315
|
-
*/
|
|
316
|
-
export function streamSummarize(text, onToken, options) {
|
|
317
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
318
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
319
|
-
}
|
|
320
|
-
if (!text || text.trim().length === 0) {
|
|
321
|
-
return {
|
|
322
|
-
promise: Promise.reject(new Error('text cannot be empty')),
|
|
323
|
-
stop: () => { },
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
const length = options?.length ?? 'medium';
|
|
327
|
-
const style = options?.style ?? 'paragraph';
|
|
328
|
-
const systemPrompt = buildSummarizePrompt(length, style);
|
|
329
|
-
return streamMessage([{ role: 'user', content: text }], onToken, {
|
|
330
|
-
systemPrompt,
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Translate text to another language using on-device AI.
|
|
335
|
-
*
|
|
336
|
-
* @param text - The text to translate
|
|
337
|
-
* @param options - Translation options including target language
|
|
338
|
-
* @returns Promise with the translated text
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* ```ts
|
|
342
|
-
* // Basic translation
|
|
343
|
-
* const result = await translate('Hello, world!', { to: 'Spanish' });
|
|
344
|
-
* console.log(result.text); // "¡Hola, mundo!"
|
|
345
|
-
* ```
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* ```ts
|
|
349
|
-
* // Formal translation with source language
|
|
350
|
-
* const result = await translate('Hey, what\'s up?', {
|
|
351
|
-
* to: 'French',
|
|
352
|
-
* from: 'English',
|
|
353
|
-
* tone: 'formal'
|
|
354
|
-
* });
|
|
355
|
-
* ```
|
|
356
|
-
*/
|
|
357
|
-
export async function translate(text, options) {
|
|
358
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
359
|
-
return { text: '' };
|
|
360
|
-
}
|
|
361
|
-
if (!text || text.trim().length === 0) {
|
|
362
|
-
throw new Error('text cannot be empty');
|
|
363
|
-
}
|
|
364
|
-
const { to, from, tone = 'neutral' } = options;
|
|
365
|
-
const systemPrompt = buildTranslatePrompt(to, from, tone);
|
|
366
|
-
return sendMessage([{ role: 'user', content: text }], { systemPrompt });
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Translate text with streaming output.
|
|
370
|
-
*
|
|
371
|
-
* @param text - The text to translate
|
|
372
|
-
* @param onToken - Callback for each token received
|
|
373
|
-
* @param options - Translation options including target language
|
|
374
|
-
* @returns Object with stop() function and promise
|
|
375
|
-
*
|
|
376
|
-
* @example
|
|
377
|
-
* ```ts
|
|
378
|
-
* const { promise } = streamTranslate(
|
|
379
|
-
* 'Hello, world!',
|
|
380
|
-
* (event) => setTranslation(event.accumulatedText),
|
|
381
|
-
* { to: 'Japanese' }
|
|
382
|
-
* );
|
|
383
|
-
* await promise;
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
|
-
export function streamTranslate(text, onToken, options) {
|
|
387
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
388
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
389
|
-
}
|
|
390
|
-
if (!text || text.trim().length === 0) {
|
|
391
|
-
return {
|
|
392
|
-
promise: Promise.reject(new Error('text cannot be empty')),
|
|
393
|
-
stop: () => { },
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
const { to, from, tone = 'neutral' } = options;
|
|
397
|
-
const systemPrompt = buildTranslatePrompt(to, from, tone);
|
|
398
|
-
return streamMessage([{ role: 'user', content: text }], onToken, {
|
|
399
|
-
systemPrompt,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Rewrite text in a different style using on-device AI.
|
|
404
|
-
*
|
|
405
|
-
* @param text - The text to rewrite
|
|
406
|
-
* @param options - Rewrite options specifying the target style
|
|
407
|
-
* @returns Promise with the rewritten text
|
|
408
|
-
*
|
|
409
|
-
* @example
|
|
410
|
-
* ```ts
|
|
411
|
-
* // Make text more formal
|
|
412
|
-
* const result = await rewrite('hey can u help me out?', {
|
|
413
|
-
* style: 'formal'
|
|
414
|
-
* });
|
|
415
|
-
* console.log(result.text); // "Would you be able to assist me?"
|
|
416
|
-
* ```
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* ```ts
|
|
420
|
-
* // Simplify complex text
|
|
421
|
-
* const result = await rewrite(technicalText, { style: 'simple' });
|
|
422
|
-
* ```
|
|
423
|
-
*/
|
|
424
|
-
export async function rewrite(text, options) {
|
|
425
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
426
|
-
return { text: '' };
|
|
427
|
-
}
|
|
428
|
-
if (!text || text.trim().length === 0) {
|
|
429
|
-
throw new Error('text cannot be empty');
|
|
430
|
-
}
|
|
431
|
-
const { style } = options;
|
|
432
|
-
const systemPrompt = buildRewritePrompt(style);
|
|
433
|
-
return sendMessage([{ role: 'user', content: text }], { systemPrompt });
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* Rewrite text with streaming output.
|
|
437
|
-
*
|
|
438
|
-
* @param text - The text to rewrite
|
|
439
|
-
* @param onToken - Callback for each token received
|
|
440
|
-
* @param options - Rewrite options specifying the target style
|
|
441
|
-
* @returns Object with stop() function and promise
|
|
442
|
-
*
|
|
443
|
-
* @example
|
|
444
|
-
* ```ts
|
|
445
|
-
* const { promise } = streamRewrite(
|
|
446
|
-
* 'hey whats up',
|
|
447
|
-
* (event) => setRewritten(event.accumulatedText),
|
|
448
|
-
* { style: 'professional' }
|
|
449
|
-
* );
|
|
450
|
-
* await promise;
|
|
451
|
-
* ```
|
|
452
|
-
*/
|
|
453
|
-
export function streamRewrite(text, onToken, options) {
|
|
454
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
455
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
456
|
-
}
|
|
457
|
-
if (!text || text.trim().length === 0) {
|
|
458
|
-
return {
|
|
459
|
-
promise: Promise.reject(new Error('text cannot be empty')),
|
|
460
|
-
stop: () => { },
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
const { style } = options;
|
|
464
|
-
const systemPrompt = buildRewritePrompt(style);
|
|
465
|
-
return streamMessage([{ role: 'user', content: text }], onToken, {
|
|
466
|
-
systemPrompt,
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Extract key points from text using on-device AI.
|
|
471
|
-
*
|
|
472
|
-
* @param text - The text to extract key points from
|
|
473
|
-
* @param options - Optional settings for extraction
|
|
474
|
-
* @returns Promise with the key points as text
|
|
475
|
-
*
|
|
476
|
-
* @example
|
|
477
|
-
* ```ts
|
|
478
|
-
* // Extract key points from an article
|
|
479
|
-
* const result = await extractKeyPoints(article);
|
|
480
|
-
* console.log(result.text);
|
|
481
|
-
* // "• Point 1\n• Point 2\n• Point 3"
|
|
482
|
-
* ```
|
|
483
|
-
*
|
|
484
|
-
* @example
|
|
485
|
-
* ```ts
|
|
486
|
-
* // Limit to 3 key points
|
|
487
|
-
* const result = await extractKeyPoints(article, { maxPoints: 3 });
|
|
488
|
-
* ```
|
|
489
|
-
*/
|
|
490
|
-
export async function extractKeyPoints(text, options) {
|
|
491
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
492
|
-
return { text: '' };
|
|
493
|
-
}
|
|
494
|
-
if (!text || text.trim().length === 0) {
|
|
495
|
-
throw new Error('text cannot be empty');
|
|
496
|
-
}
|
|
497
|
-
const maxPoints = options?.maxPoints ?? 5;
|
|
498
|
-
const systemPrompt = buildExtractKeyPointsPrompt(maxPoints);
|
|
499
|
-
return sendMessage([{ role: 'user', content: text }], { systemPrompt });
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Extract key points with streaming output.
|
|
503
|
-
*
|
|
504
|
-
* @param text - The text to extract key points from
|
|
505
|
-
* @param onToken - Callback for each token received
|
|
506
|
-
* @param options - Optional settings for extraction
|
|
507
|
-
* @returns Object with stop() function and promise
|
|
508
|
-
*
|
|
509
|
-
* @example
|
|
510
|
-
* ```ts
|
|
511
|
-
* const { promise } = streamExtractKeyPoints(
|
|
512
|
-
* article,
|
|
513
|
-
* (event) => setKeyPoints(event.accumulatedText),
|
|
514
|
-
* { maxPoints: 5 }
|
|
515
|
-
* );
|
|
516
|
-
* await promise;
|
|
517
|
-
* ```
|
|
518
|
-
*/
|
|
519
|
-
export function streamExtractKeyPoints(text, onToken, options) {
|
|
520
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
521
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
522
|
-
}
|
|
523
|
-
if (!text || text.trim().length === 0) {
|
|
524
|
-
return {
|
|
525
|
-
promise: Promise.reject(new Error('text cannot be empty')),
|
|
526
|
-
stop: () => { },
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
const maxPoints = options?.maxPoints ?? 5;
|
|
530
|
-
const systemPrompt = buildExtractKeyPointsPrompt(maxPoints);
|
|
531
|
-
return streamMessage([{ role: 'user', content: text }], onToken, {
|
|
532
|
-
systemPrompt,
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Answer a question based on provided context using on-device AI.
|
|
537
|
-
*
|
|
538
|
-
* @param question - The question to answer
|
|
539
|
-
* @param context - The context/document to base the answer on
|
|
540
|
-
* @param options - Optional settings for the answer
|
|
541
|
-
* @returns Promise with the answer
|
|
542
|
-
*
|
|
543
|
-
* @example
|
|
544
|
-
* ```ts
|
|
545
|
-
* // Answer a question about a document
|
|
546
|
-
* const result = await answerQuestion(
|
|
547
|
-
* 'What is the main topic?',
|
|
548
|
-
* documentText
|
|
549
|
-
* );
|
|
550
|
-
* console.log(result.text);
|
|
551
|
-
* ```
|
|
552
|
-
*
|
|
553
|
-
* @example
|
|
554
|
-
* ```ts
|
|
555
|
-
* // Get a detailed answer
|
|
556
|
-
* const result = await answerQuestion(
|
|
557
|
-
* 'Explain the methodology',
|
|
558
|
-
* researchPaper,
|
|
559
|
-
* { detail: 'detailed' }
|
|
560
|
-
* );
|
|
561
|
-
* ```
|
|
562
|
-
*/
|
|
563
|
-
export async function answerQuestion(question, context, options) {
|
|
564
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
565
|
-
return { text: '' };
|
|
566
|
-
}
|
|
567
|
-
if (!question || question.trim().length === 0) {
|
|
568
|
-
throw new Error('question cannot be empty');
|
|
569
|
-
}
|
|
570
|
-
if (!context || context.trim().length === 0) {
|
|
571
|
-
throw new Error('context cannot be empty');
|
|
572
|
-
}
|
|
573
|
-
const detail = options?.detail ?? 'medium';
|
|
574
|
-
const systemPrompt = buildAnswerQuestionPrompt(detail);
|
|
575
|
-
const userContent = `Context:\n${context}\n\nQuestion: ${question}`;
|
|
576
|
-
return sendMessage([{ role: 'user', content: userContent }], { systemPrompt });
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Answer a question with streaming output.
|
|
580
|
-
*
|
|
581
|
-
* @param question - The question to answer
|
|
582
|
-
* @param context - The context/document to base the answer on
|
|
583
|
-
* @param onToken - Callback for each token received
|
|
584
|
-
* @param options - Optional settings for the answer
|
|
585
|
-
* @returns Object with stop() function and promise
|
|
586
|
-
*
|
|
587
|
-
* @example
|
|
588
|
-
* ```ts
|
|
589
|
-
* const { promise } = streamAnswerQuestion(
|
|
590
|
-
* 'What are the key findings?',
|
|
591
|
-
* documentText,
|
|
592
|
-
* (event) => setAnswer(event.accumulatedText),
|
|
593
|
-
* { detail: 'detailed' }
|
|
594
|
-
* );
|
|
595
|
-
* await promise;
|
|
596
|
-
* ```
|
|
597
|
-
*/
|
|
598
|
-
export function streamAnswerQuestion(question, context, onToken, options) {
|
|
599
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
600
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
601
|
-
}
|
|
602
|
-
if (!question || question.trim().length === 0) {
|
|
603
|
-
return {
|
|
604
|
-
promise: Promise.reject(new Error('question cannot be empty')),
|
|
605
|
-
stop: () => { },
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
if (!context || context.trim().length === 0) {
|
|
609
|
-
return {
|
|
610
|
-
promise: Promise.reject(new Error('context cannot be empty')),
|
|
611
|
-
stop: () => { },
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
const detail = options?.detail ?? 'medium';
|
|
615
|
-
const systemPrompt = buildAnswerQuestionPrompt(detail);
|
|
616
|
-
const userContent = `Context:\n${context}\n\nQuestion: ${question}`;
|
|
617
|
-
return streamMessage([{ role: 'user', content: userContent }], onToken, {
|
|
618
|
-
systemPrompt,
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
// ============================================================================
|
|
622
|
-
// Smart Suggestions
|
|
623
|
-
// ============================================================================
|
|
624
|
-
/**
|
|
625
|
-
* Generate text suggestions based on partial input using on-device AI.
|
|
626
|
-
*
|
|
627
|
-
* Useful for text completion, writing assistance, and predictive text features.
|
|
628
|
-
*
|
|
629
|
-
* @param partialText - The text the user has typed so far
|
|
630
|
-
* @param options - Optional settings for suggestions
|
|
631
|
-
* @returns Promise with an array of suggestions
|
|
632
|
-
*
|
|
633
|
-
* @example
|
|
634
|
-
* ```ts
|
|
635
|
-
* // Basic suggestions
|
|
636
|
-
* const result = await suggest('I think we should');
|
|
637
|
-
* result.suggestions.forEach(s => console.log(s.text));
|
|
638
|
-
* // "schedule a meeting to discuss this further"
|
|
639
|
-
* // "consider an alternative approach"
|
|
640
|
-
* // "move forward with the plan"
|
|
641
|
-
* ```
|
|
642
|
-
*
|
|
643
|
-
* @example
|
|
644
|
-
* ```ts
|
|
645
|
-
* // With context and tone
|
|
646
|
-
* const result = await suggest('Dear Mr. Johnson,', {
|
|
647
|
-
* count: 5,
|
|
648
|
-
* context: 'writing a business email about project delays',
|
|
649
|
-
* tone: 'formal'
|
|
650
|
-
* });
|
|
651
|
-
* ```
|
|
652
|
-
*/
|
|
653
|
-
export async function suggest(partialText, options) {
|
|
654
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
655
|
-
return { suggestions: [], raw: '' };
|
|
656
|
-
}
|
|
657
|
-
if (!partialText || partialText.trim().length === 0) {
|
|
658
|
-
throw new Error('partialText cannot be empty');
|
|
659
|
-
}
|
|
660
|
-
const count = options?.count ?? 3;
|
|
661
|
-
const tone = options?.tone ?? 'neutral';
|
|
662
|
-
const systemPrompt = buildSuggestPrompt(count, tone, options?.context);
|
|
663
|
-
const response = await sendMessage([{ role: 'user', content: partialText }], { systemPrompt });
|
|
664
|
-
return {
|
|
665
|
-
suggestions: parseSuggestions(response.text),
|
|
666
|
-
raw: response.text,
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Generate text suggestions with streaming output.
|
|
671
|
-
*
|
|
672
|
-
* @param partialText - The text the user has typed so far
|
|
673
|
-
* @param onToken - Callback for each token received
|
|
674
|
-
* @param options - Optional settings for suggestions
|
|
675
|
-
* @returns Object with stop() function and promise
|
|
676
|
-
*
|
|
677
|
-
* @example
|
|
678
|
-
* ```ts
|
|
679
|
-
* const { promise } = streamSuggest(
|
|
680
|
-
* 'The best way to',
|
|
681
|
-
* (event) => setRawSuggestions(event.accumulatedText)
|
|
682
|
-
* );
|
|
683
|
-
* const result = await promise;
|
|
684
|
-
* // Parse suggestions from result.text
|
|
685
|
-
* ```
|
|
686
|
-
*/
|
|
687
|
-
export function streamSuggest(partialText, onToken, options) {
|
|
688
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
689
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
690
|
-
}
|
|
691
|
-
if (!partialText || partialText.trim().length === 0) {
|
|
692
|
-
return {
|
|
693
|
-
promise: Promise.reject(new Error('partialText cannot be empty')),
|
|
694
|
-
stop: () => { },
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
const count = options?.count ?? 3;
|
|
698
|
-
const tone = options?.tone ?? 'neutral';
|
|
699
|
-
const systemPrompt = buildSuggestPrompt(count, tone, options?.context);
|
|
700
|
-
return streamMessage([{ role: 'user', content: partialText }], onToken, {
|
|
701
|
-
systemPrompt,
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
/**
|
|
705
|
-
* Generate smart reply suggestions for a conversation using on-device AI.
|
|
706
|
-
*
|
|
707
|
-
* Analyzes the conversation history and generates contextually appropriate
|
|
708
|
-
* reply options, similar to Gmail/Messages smart replies.
|
|
709
|
-
*
|
|
710
|
-
* @param messages - The conversation history to generate replies for
|
|
711
|
-
* @param options - Optional settings for reply generation
|
|
712
|
-
* @returns Promise with an array of reply suggestions
|
|
713
|
-
*
|
|
714
|
-
* @example
|
|
715
|
-
* ```ts
|
|
716
|
-
* // Basic smart replies
|
|
717
|
-
* const result = await smartReply([
|
|
718
|
-
* { role: 'user', content: 'Hey, are you free for lunch tomorrow?' }
|
|
719
|
-
* ]);
|
|
720
|
-
* result.suggestions.forEach(s => console.log(s.text));
|
|
721
|
-
* // "Sure, what time works for you?"
|
|
722
|
-
* // "Sorry, I already have plans."
|
|
723
|
-
* // "Let me check my schedule and get back to you."
|
|
724
|
-
* ```
|
|
725
|
-
*
|
|
726
|
-
* @example
|
|
727
|
-
* ```ts
|
|
728
|
-
* // With persona and tone
|
|
729
|
-
* const result = await smartReply(
|
|
730
|
-
* [
|
|
731
|
-
* { role: 'user', content: 'We need the report by Friday.' },
|
|
732
|
-
* { role: 'assistant', content: 'I will do my best.' },
|
|
733
|
-
* { role: 'user', content: 'Can you confirm the deadline works?' }
|
|
734
|
-
* ],
|
|
735
|
-
* { tone: 'professional', persona: 'project manager', count: 4 }
|
|
736
|
-
* );
|
|
737
|
-
* ```
|
|
738
|
-
*/
|
|
739
|
-
export async function smartReply(messages, options) {
|
|
740
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
741
|
-
return { suggestions: [], raw: '' };
|
|
742
|
-
}
|
|
743
|
-
if (!messages || messages.length === 0) {
|
|
744
|
-
throw new Error('messages array cannot be empty');
|
|
745
|
-
}
|
|
746
|
-
const count = options?.count ?? 3;
|
|
747
|
-
const tone = options?.tone ?? 'neutral';
|
|
748
|
-
const systemPrompt = buildSmartReplyPrompt(count, tone, options?.persona);
|
|
749
|
-
const conversation = messages
|
|
750
|
-
.map((m) => `${m.role === 'user' ? 'Them' : 'Me'}: ${m.content}`)
|
|
751
|
-
.join('\n');
|
|
752
|
-
const response = await sendMessage([{ role: 'user', content: `Conversation:\n${conversation}\n\nGenerate ${count} reply suggestions for my next message.` }], { systemPrompt });
|
|
753
|
-
return {
|
|
754
|
-
suggestions: parseSuggestions(response.text),
|
|
755
|
-
raw: response.text,
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Generate smart reply suggestions with streaming output.
|
|
760
|
-
*
|
|
761
|
-
* @param messages - The conversation history to generate replies for
|
|
762
|
-
* @param onToken - Callback for each token received
|
|
763
|
-
* @param options - Optional settings for reply generation
|
|
764
|
-
* @returns Object with stop() function and promise
|
|
765
|
-
*
|
|
766
|
-
* @example
|
|
767
|
-
* ```ts
|
|
768
|
-
* const { promise } = streamSmartReply(
|
|
769
|
-
* [{ role: 'user', content: 'Want to grab coffee?' }],
|
|
770
|
-
* (event) => setRawReplies(event.accumulatedText)
|
|
771
|
-
* );
|
|
772
|
-
* const result = await promise;
|
|
773
|
-
* ```
|
|
774
|
-
*/
|
|
775
|
-
export function streamSmartReply(messages, onToken, options) {
|
|
776
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
777
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
778
|
-
}
|
|
779
|
-
if (!messages || messages.length === 0) {
|
|
780
|
-
return {
|
|
781
|
-
promise: Promise.reject(new Error('messages array cannot be empty')),
|
|
782
|
-
stop: () => { },
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
const count = options?.count ?? 3;
|
|
786
|
-
const tone = options?.tone ?? 'neutral';
|
|
787
|
-
const systemPrompt = buildSmartReplyPrompt(count, tone, options?.persona);
|
|
788
|
-
const conversation = messages
|
|
789
|
-
.map((m) => `${m.role === 'user' ? 'Them' : 'Me'}: ${m.content}`)
|
|
790
|
-
.join('\n');
|
|
791
|
-
return streamMessage([{ role: 'user', content: `Conversation:\n${conversation}\n\nGenerate ${count} reply suggestions for my next message.` }], onToken, { systemPrompt });
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* Autocomplete the user's current text using on-device AI.
|
|
795
|
-
*
|
|
796
|
-
* Generates short, natural completions that seamlessly continue from
|
|
797
|
-
* the user's partial input. Ideal for real-time typing suggestions.
|
|
798
|
-
*
|
|
799
|
-
* @param partialText - The text the user has typed so far
|
|
800
|
-
* @param options - Optional settings for autocompletion
|
|
801
|
-
* @returns Promise with an array of completion suggestions
|
|
802
|
-
*
|
|
803
|
-
* @example
|
|
804
|
-
* ```ts
|
|
805
|
-
* // Basic autocomplete
|
|
806
|
-
* const result = await autocomplete('How do I');
|
|
807
|
-
* result.suggestions.forEach(s => console.log(s.text));
|
|
808
|
-
* // "reset my password"
|
|
809
|
-
* // "contact support"
|
|
810
|
-
* // "cancel my subscription"
|
|
811
|
-
* ```
|
|
812
|
-
*
|
|
813
|
-
* @example
|
|
814
|
-
* ```ts
|
|
815
|
-
* // With context for better suggestions
|
|
816
|
-
* const result = await autocomplete('The patient presents with', {
|
|
817
|
-
* context: 'medical notes',
|
|
818
|
-
* maxWords: 15,
|
|
819
|
-
* count: 5
|
|
820
|
-
* });
|
|
821
|
-
* ```
|
|
822
|
-
*/
|
|
823
|
-
export async function autocomplete(partialText, options) {
|
|
824
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
825
|
-
return { suggestions: [], raw: '' };
|
|
826
|
-
}
|
|
827
|
-
if (!partialText || partialText.trim().length === 0) {
|
|
828
|
-
throw new Error('partialText cannot be empty');
|
|
829
|
-
}
|
|
830
|
-
const count = options?.count ?? 3;
|
|
831
|
-
const maxWords = options?.maxWords ?? 10;
|
|
832
|
-
const systemPrompt = buildAutocompletePrompt(count, maxWords, options?.context);
|
|
833
|
-
const response = await sendMessage([{ role: 'user', content: partialText }], { systemPrompt });
|
|
834
|
-
return {
|
|
835
|
-
suggestions: parseSuggestions(response.text),
|
|
836
|
-
raw: response.text,
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Autocomplete text with streaming output.
|
|
841
|
-
*
|
|
842
|
-
* @param partialText - The text the user has typed so far
|
|
843
|
-
* @param onToken - Callback for each token received
|
|
844
|
-
* @param options - Optional settings for autocompletion
|
|
845
|
-
* @returns Object with stop() function and promise
|
|
846
|
-
*
|
|
847
|
-
* @example
|
|
848
|
-
* ```ts
|
|
849
|
-
* const { promise } = streamAutocomplete(
|
|
850
|
-
* 'I would like to',
|
|
851
|
-
* (event) => setRawCompletions(event.accumulatedText)
|
|
852
|
-
* );
|
|
853
|
-
* const result = await promise;
|
|
854
|
-
* ```
|
|
855
|
-
*/
|
|
856
|
-
export function streamAutocomplete(partialText, onToken, options) {
|
|
857
|
-
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
858
|
-
return { promise: Promise.resolve({ text: '' }), stop: () => { } };
|
|
859
|
-
}
|
|
860
|
-
if (!partialText || partialText.trim().length === 0) {
|
|
861
|
-
return {
|
|
862
|
-
promise: Promise.reject(new Error('partialText cannot be empty')),
|
|
863
|
-
stop: () => { },
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
const count = options?.count ?? 3;
|
|
867
|
-
const maxWords = options?.maxWords ?? 10;
|
|
868
|
-
const systemPrompt = buildAutocompletePrompt(count, maxWords, options?.context);
|
|
869
|
-
return streamMessage([{ role: 'user', content: partialText }], onToken, {
|
|
870
|
-
systemPrompt,
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Parse suggestion text from a suggest/smartReply/autocomplete response.
|
|
875
|
-
*
|
|
876
|
-
* Use this to parse the raw text from streaming responses into structured suggestions.
|
|
877
|
-
*
|
|
878
|
-
* @param raw - Raw text response from the model
|
|
879
|
-
* @returns Array of parsed suggestions
|
|
880
|
-
*
|
|
881
|
-
* @example
|
|
882
|
-
* ```ts
|
|
883
|
-
* const { promise } = streamSuggest('Hello', (event) => {
|
|
884
|
-
* setText(event.accumulatedText);
|
|
885
|
-
* });
|
|
886
|
-
* const result = await promise;
|
|
887
|
-
* const suggestions = parseSuggestResponse(result.text);
|
|
888
|
-
* ```
|
|
889
|
-
*/
|
|
890
|
-
export function parseSuggestResponse(raw) {
|
|
891
|
-
return {
|
|
892
|
-
suggestions: parseSuggestions(raw),
|
|
893
|
-
raw,
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
// ============================================================================
|
|
897
161
|
// Model Management API
|
|
898
162
|
// ============================================================================
|
|
899
163
|
/**
|