@aituber-onair/manneri 0.1.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.
Files changed (48) hide show
  1. package/README.ja.md +609 -0
  2. package/README.md +600 -0
  3. package/dist/analyzers/KeywordExtractor.d.ts +48 -0
  4. package/dist/analyzers/KeywordExtractor.js +253 -0
  5. package/dist/analyzers/KeywordExtractor.js.map +1 -0
  6. package/dist/analyzers/PatternDetector.d.ts +38 -0
  7. package/dist/analyzers/PatternDetector.js +244 -0
  8. package/dist/analyzers/PatternDetector.js.map +1 -0
  9. package/dist/analyzers/SimilarityAnalyzer.d.ts +23 -0
  10. package/dist/analyzers/SimilarityAnalyzer.js +153 -0
  11. package/dist/analyzers/SimilarityAnalyzer.js.map +1 -0
  12. package/dist/config/defaultPrompts.d.ts +5 -0
  13. package/dist/config/defaultPrompts.js +22 -0
  14. package/dist/config/defaultPrompts.js.map +1 -0
  15. package/dist/core/ConversationAnalyzer.d.ts +51 -0
  16. package/dist/core/ConversationAnalyzer.js +213 -0
  17. package/dist/core/ConversationAnalyzer.js.map +1 -0
  18. package/dist/core/ManneriDetector.d.ts +64 -0
  19. package/dist/core/ManneriDetector.js +251 -0
  20. package/dist/core/ManneriDetector.js.map +1 -0
  21. package/dist/generators/PromptGenerator.d.ts +15 -0
  22. package/dist/generators/PromptGenerator.js +45 -0
  23. package/dist/generators/PromptGenerator.js.map +1 -0
  24. package/dist/index.d.ts +21 -0
  25. package/dist/index.js +27 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/persistence/LocalStoragePersistenceProvider.d.ts +45 -0
  28. package/dist/persistence/LocalStoragePersistenceProvider.js +102 -0
  29. package/dist/persistence/LocalStoragePersistenceProvider.js.map +1 -0
  30. package/dist/persistence/index.d.ts +5 -0
  31. package/dist/persistence/index.js +5 -0
  32. package/dist/persistence/index.js.map +1 -0
  33. package/dist/types/index.d.ts +114 -0
  34. package/dist/types/index.js +28 -0
  35. package/dist/types/index.js.map +1 -0
  36. package/dist/types/persistence.d.ts +78 -0
  37. package/dist/types/persistence.js +2 -0
  38. package/dist/types/persistence.js.map +1 -0
  39. package/dist/types/prompts.d.ts +22 -0
  40. package/dist/types/prompts.js +36 -0
  41. package/dist/types/prompts.js.map +1 -0
  42. package/dist/utils/browserUtils.d.ts +20 -0
  43. package/dist/utils/browserUtils.js +206 -0
  44. package/dist/utils/browserUtils.js.map +1 -0
  45. package/dist/utils/textUtils.d.ts +10 -0
  46. package/dist/utils/textUtils.js +269 -0
  47. package/dist/utils/textUtils.js.map +1 -0
  48. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,600 @@
1
+ # @aituber-onair/manneri
2
+
3
+ ![AITuber OnAir Manneri - logo](./images/aituber-onair-manneri.png)
4
+
5
+ **Manneri** is a simple JavaScript library designed to detect repetitive conversation patterns in AI chatbots and provide topic diversification prompts for more engaging conversations.
6
+
7
+ [日本語版README](README.ja.md) | [English README](README.md)
8
+
9
+ ## Features
10
+
11
+ - 🔍 **Conversation Similarity Analysis**: Calculate text similarity to detect repetitive patterns
12
+ - 📊 **Pattern Detection**: Identify structural patterns in conversations
13
+ - 🎯 **Keyword Analysis**: Detect overused vocabulary and topic bias
14
+ - 💡 **Automatic Prompt Generation**: Generate appropriate prompts for topic diversification
15
+ - 🌐 **Frontend-Only**: Lightweight operation optimized for browser environments
16
+ - 🌍 **Multi-Language Support**: Built-in support for Japanese and English, with easy extension to any language via custom prompts
17
+ - 🎨 **Customizable Prompts**: Configure intervention messages and recommendations for any language
18
+ - 🇯🇵 **Japanese Language Support**: Proper handling of Japanese text (Hiragana, Katakana, Kanji)
19
+ - 💾 **Flexible Persistence**: Configurable data persistence with support for multiple storage backends
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @aituber-onair/manneri
25
+ ```
26
+
27
+ ## Basic Usage
28
+
29
+ ```typescript
30
+ import { ManneriDetector, LocalStoragePersistenceProvider } from '@aituber-onair/manneri';
31
+
32
+ // Create ManneriDetector with default settings
33
+ const detector = new ManneriDetector();
34
+
35
+ // Define message array
36
+ const messages = [
37
+ { role: 'user', content: 'Hello' },
38
+ { role: 'assistant', content: 'Hello! How can I help you today?' },
39
+ { role: 'user', content: 'Hello' },
40
+ { role: 'assistant', content: 'Hello! What can I assist you with?' }
41
+ ];
42
+
43
+ // Detect repetitive patterns
44
+ if (detector.detectManneri(messages)) {
45
+ console.log('Repetitive conversation detected');
46
+ }
47
+
48
+ // Check if intervention is needed (considering cooldown)
49
+ if (detector.shouldIntervene(messages)) {
50
+ // Generate topic diversification prompt
51
+ const prompt = detector.generateDiversificationPrompt(messages);
52
+ console.log('Suggested prompt:', prompt.content);
53
+ }
54
+ ```
55
+
56
+ ## Configuration Options
57
+
58
+ ```typescript
59
+ const detector = new ManneriDetector({
60
+ similarityThreshold: 0.75, // Similarity threshold (0-1)
61
+ repetitionLimit: 3, // Number of repetitions to detect
62
+ lookbackWindow: 10, // Number of messages to analyze
63
+ interventionCooldown: 300000, // Intervention interval (milliseconds)
64
+ minMessageLength: 10, // Minimum character count for analysis
65
+ excludeKeywords: ['yes', 'no'], // Keywords to exclude
66
+ enableTopicTracking: true, // Enable topic tracking
67
+ enableKeywordAnalysis: true, // Enable keyword analysis
68
+ debugMode: false, // Debug mode
69
+ language: 'en', // Language for prompts ('ja' | 'en' | custom)
70
+ customPrompts: { // Custom intervention prompts (optional)
71
+ en: {
72
+ intervention: [
73
+ 'Please change the topic and talk about something new.',
74
+ 'Let\'s explore a different subject.',
75
+ 'How about discussing something else?'
76
+ ]
77
+ }
78
+ }
79
+ }, {
80
+ // Optional: Configure persistence provider
81
+ persistenceProvider: new LocalStoragePersistenceProvider({
82
+ storageKey: 'my_manneri_data',
83
+ version: '1.0.0'
84
+ })
85
+ });
86
+ ```
87
+
88
+ ## Integration with AITuberOnAirCore
89
+
90
+ ```typescript
91
+ import { ManneriDetector } from '@aituber-onair/manneri';
92
+
93
+ const manneriDetector = new ManneriDetector({
94
+ similarityThreshold: 0.8,
95
+ repetitionLimit: 3,
96
+ interventionCooldown: 300000
97
+ });
98
+
99
+ // Integration via AITuberOnAirCore event listener
100
+ core.on('beforeAIRequest', (requestData) => {
101
+ const chatHistory = core.getChatHistory();
102
+
103
+ if (manneriDetector.shouldIntervene(chatHistory)) {
104
+ const diversificationPrompt = manneriDetector.generateDiversificationPrompt(chatHistory);
105
+
106
+ // Add topic change instruction as system prompt
107
+ requestData.messages.unshift({
108
+ role: 'system',
109
+ content: diversificationPrompt.content
110
+ });
111
+
112
+ console.log('Applied diversification prompt:', diversificationPrompt.type);
113
+ }
114
+ });
115
+ ```
116
+
117
+ ## Event Handling
118
+
119
+ ```typescript
120
+ // Similarity calculation event
121
+ detector.on('similarity_calculated', (data) => {
122
+ console.log(`Similarity: ${data.score}, Threshold: ${data.threshold}`);
123
+ });
124
+
125
+ // Pattern detection event
126
+ detector.on('pattern_detected', (result) => {
127
+ console.log('Detected patterns:', result.patterns);
128
+ });
129
+
130
+ // Intervention triggered event
131
+ detector.on('intervention_triggered', (prompt) => {
132
+ console.log('Intervention executed:', prompt.content);
133
+ });
134
+
135
+ // Configuration update event
136
+ detector.on('config_updated', (newConfig) => {
137
+ console.log('Configuration updated:', newConfig);
138
+ });
139
+ ```
140
+
141
+ ## Detailed Analysis
142
+
143
+ ```typescript
144
+ // Perform detailed conversation analysis
145
+ const analysis = detector.analyzeConversation(messages);
146
+
147
+ console.log('Analysis result:', {
148
+ similarity: analysis.similarity,
149
+ topics: analysis.topics,
150
+ patterns: analysis.patterns,
151
+ shouldIntervene: analysis.shouldIntervene,
152
+ reason: analysis.interventionReason
153
+ });
154
+
155
+ // Get statistics
156
+ const stats = detector.getStatistics();
157
+ console.log('Statistics:', {
158
+ totalInterventions: stats.totalInterventions,
159
+ averageInterval: stats.averageInterventionInterval,
160
+ thresholds: stats.configuredThresholds
161
+ });
162
+ ```
163
+
164
+ ## Multi-Language Support
165
+
166
+ ### Language Configuration
167
+
168
+ ```typescript
169
+ // Use English prompts
170
+ const detectorEn = new ManneriDetector({
171
+ language: 'en'
172
+ });
173
+
174
+ // Use Japanese prompts (default)
175
+ const detectorJa = new ManneriDetector({
176
+ language: 'ja'
177
+ });
178
+
179
+ // Custom prompts for Spanish
180
+ const detectorSpanish = new ManneriDetector({
181
+ language: 'es',
182
+ customPrompts: {
183
+ es: {
184
+ intervention: [
185
+ 'Cambiemos de tema y hablemos de algo nuevo.',
186
+ 'Exploremos un tema diferente.',
187
+ '¿Qué tal si discutimos otra cosa?'
188
+ ]
189
+ }
190
+ }
191
+ });
192
+
193
+ // Chinese support
194
+ const detectorChinese = new ManneriDetector({
195
+ language: 'zh',
196
+ customPrompts: {
197
+ zh: {
198
+ intervention: [
199
+ '让我们换个话题,聊些新的内容。',
200
+ '我们来探讨一个不同的主题。',
201
+ '要不要讨论别的事情?'
202
+ ]
203
+ }
204
+ }
205
+ });
206
+ ```
207
+
208
+ ### Built-in Language Support
209
+
210
+ Manneri comes with built-in prompts for:
211
+ - **Japanese** (`'ja'`) - Default language
212
+ - **English** (`'en'`) - Full prompt coverage
213
+
214
+ ```typescript
215
+ import { DEFAULT_PROMPTS } from '@aituber-onair/manneri';
216
+
217
+ // View available built-in languages
218
+ console.log(Object.keys(DEFAULT_PROMPTS)); // ['ja', 'en']
219
+
220
+ // Access specific language prompts
221
+ const englishPrompts = DEFAULT_PROMPTS.en;
222
+ console.log(englishPrompts.intervention);
223
+ ```
224
+
225
+ ### Adding Any Language
226
+
227
+ **Any language can be supported** by providing custom prompts. The library accepts any language code and uses your custom prompts:
228
+
229
+ ```typescript
230
+ // Add support for any language (French example)
231
+ const detectorFrench = new ManneriDetector({
232
+ language: 'fr',
233
+ customPrompts: {
234
+ fr: {
235
+ intervention: [
236
+ 'Changeons de sujet et parlons de quelque chose de nouveau.',
237
+ 'Explorons un sujet différent.',
238
+ 'Que diriez-vous de discuter d\'autre chose?'
239
+ ]
240
+ }
241
+ }
242
+ });
243
+
244
+ // Or extend existing languages
245
+ import { overridePrompts, DEFAULT_PROMPTS } from '@aituber-onair/manneri';
246
+
247
+ const multilingualPrompts = overridePrompts(DEFAULT_PROMPTS, {
248
+ zh: {
249
+ intervention: ['让我们换个话题,聊些新的内容。']
250
+ },
251
+ ko: {
252
+ intervention: ['새로운 주제로 변경해 주세요.']
253
+ },
254
+ fr: {
255
+ intervention: ['Changeons de sujet.']
256
+ }
257
+ });
258
+ ```
259
+
260
+ ### Language-Agnostic Design
261
+
262
+ The library is designed to work with **any language** without code changes:
263
+
264
+ ```typescript
265
+ // Easy language switching
266
+ const createDetectorForLanguage = (lang: string, prompts: any) => {
267
+ return new ManneriDetector({
268
+ language: lang,
269
+ customPrompts: { [lang]: prompts }
270
+ });
271
+ };
272
+
273
+ // Support multiple languages in the same application
274
+ const detectors = {
275
+ english: createDetectorForLanguage('en', englishPrompts),
276
+ chinese: createDetectorForLanguage('zh', chinesePrompts),
277
+ korean: createDetectorForLanguage('ko', koreanPrompts),
278
+ arabic: createDetectorForLanguage('ar', arabicPrompts),
279
+ // Add any language
280
+ };
281
+
282
+ // Dynamic language detection
283
+ const getUserLanguage = () => navigator.language.split('-')[0];
284
+ const userDetector = detectors[getUserLanguage()] || detectors.english;
285
+ ```
286
+
287
+ ### Prompt Utilities
288
+
289
+ ```typescript
290
+ import { getPromptTemplate, overridePrompts } from '@aituber-onair/manneri';
291
+
292
+ // Get intervention prompt for any language
293
+ const interventionPrompt = getPromptTemplate(
294
+ myCustomPrompts,
295
+ 'zh'
296
+ );
297
+
298
+ // Override default prompts with custom ones
299
+ const globalPrompts = overridePrompts(DEFAULT_PROMPTS, {
300
+ zh: { intervention: ['换个话题吧'] },
301
+ ko: { intervention: ['주제를 바꿔주세요'] },
302
+ es: { intervention: ['Cambiemos de tema'] }
303
+ });
304
+ ```
305
+
306
+ ## Individual Feature Usage
307
+
308
+ ### Similarity Analysis
309
+
310
+ ```typescript
311
+ import { SimilarityAnalyzer } from '@aituber-onair/manneri';
312
+
313
+ const analyzer = new SimilarityAnalyzer();
314
+ const similarity = analyzer.calculateSimilarity('Hello', 'Hello, how are you?');
315
+ console.log('Similarity:', similarity); // 0.0 - 1.0
316
+ ```
317
+
318
+ ### Keyword Extraction
319
+
320
+ ```typescript
321
+ import { KeywordExtractor } from '@aituber-onair/manneri';
322
+
323
+ const extractor = new KeywordExtractor();
324
+ const keywords = extractor.extractKeywordsFromMessages(messages);
325
+ console.log('Keywords:', keywords);
326
+ ```
327
+
328
+ ### Pattern Detection
329
+
330
+ ```typescript
331
+ import { PatternDetector } from '@aituber-onair/manneri';
332
+
333
+ const detector = new PatternDetector();
334
+ const result = detector.detectPatterns(messages);
335
+ console.log('Patterns:', result.patterns);
336
+ console.log('Severity:', result.severity);
337
+ console.log('Confidence:', result.confidence);
338
+ ```
339
+
340
+ ## Data Persistence
341
+
342
+ Manneri provides flexible persistence through configurable providers. You have full control over when and how data is saved.
343
+
344
+ ### Browser Environment (LocalStorage)
345
+
346
+ ```typescript
347
+ import { ManneriDetector, LocalStoragePersistenceProvider } from '@aituber-onair/manneri';
348
+
349
+ // Configure with LocalStorage persistence
350
+ const detector = new ManneriDetector({
351
+ // ... configuration options
352
+ }, {
353
+ persistenceProvider: new LocalStoragePersistenceProvider({
354
+ storageKey: 'manneri_data', // Custom storage key
355
+ version: '1.0.0' // Data version
356
+ })
357
+ });
358
+
359
+ // Manual persistence control
360
+ await detector.save(); // Save current state
361
+ await detector.load(); // Load saved state
362
+ await detector.cleanup(); // Clean up old data
363
+
364
+ // Check if persistence is available
365
+ if (detector.hasPersistenceProvider()) {
366
+ console.log('Persistence is configured');
367
+
368
+ // Get storage info
369
+ const info = detector.getPersistenceInfo();
370
+ console.log('Storage info:', info);
371
+ }
372
+
373
+ // Event handling for persistence operations
374
+ detector.on('save_success', ({ timestamp }) => {
375
+ console.log('Data saved successfully at', new Date(timestamp));
376
+ });
377
+
378
+ detector.on('save_error', ({ error }) => {
379
+ console.error('Failed to save data:', error);
380
+ });
381
+
382
+ detector.on('load_success', ({ data, timestamp }) => {
383
+ console.log('Data loaded successfully:', data);
384
+ });
385
+
386
+ detector.on('cleanup_completed', ({ removedItems, timestamp }) => {
387
+ console.log(`Cleaned up ${removedItems} old items`);
388
+ });
389
+ ```
390
+
391
+ ### Custom Persistence Provider
392
+
393
+ For Node.js, Deno, or custom storage solutions, implement the `PersistenceProvider` interface:
394
+
395
+ ```typescript
396
+ import type { PersistenceProvider, StorageData } from '@aituber-onair/manneri';
397
+
398
+ // Example: Database persistence provider
399
+ class DatabasePersistenceProvider implements PersistenceProvider {
400
+ constructor(private dbConnection: any) {}
401
+
402
+ async save(data: StorageData): Promise<boolean> {
403
+ try {
404
+ await this.dbConnection.query(
405
+ 'INSERT OR REPLACE INTO manneri_data (id, data) VALUES (?, ?)',
406
+ [1, JSON.stringify(data)]
407
+ );
408
+ return true;
409
+ } catch (error) {
410
+ console.error('Database save failed:', error);
411
+ return false;
412
+ }
413
+ }
414
+
415
+ async load(): Promise<StorageData | null> {
416
+ try {
417
+ const result = await this.dbConnection.query(
418
+ 'SELECT data FROM manneri_data WHERE id = ?',
419
+ [1]
420
+ );
421
+ return result.length > 0 ? JSON.parse(result[0].data) : null;
422
+ } catch (error) {
423
+ console.error('Database load failed:', error);
424
+ return null;
425
+ }
426
+ }
427
+
428
+ async clear(): Promise<boolean> {
429
+ try {
430
+ await this.dbConnection.query('DELETE FROM manneri_data WHERE id = ?', [1]);
431
+ return true;
432
+ } catch (error) {
433
+ console.error('Database clear failed:', error);
434
+ return false;
435
+ }
436
+ }
437
+
438
+ async cleanup(maxAge: number): Promise<number> {
439
+ // Implement cleanup logic for your storage
440
+ // Return number of items removed
441
+ return 0;
442
+ }
443
+ }
444
+
445
+ // Use custom persistence provider
446
+ const detector = new ManneriDetector({
447
+ // ... configuration
448
+ }, {
449
+ persistenceProvider: new DatabasePersistenceProvider(dbConnection)
450
+ });
451
+ ```
452
+
453
+ ### Manual Data Management (No Persistence)
454
+
455
+ ```typescript
456
+ // Use without persistence provider
457
+ const detector = new ManneriDetector();
458
+
459
+ // Manual export/import for custom storage
460
+ const data = detector.exportData();
461
+ // Store data however you want (file, database, etc.)
462
+ await myCustomStorage.save(data);
463
+
464
+ // Restore data
465
+ const restoredData = await myCustomStorage.load();
466
+ detector.importData(restoredData);
467
+
468
+ // Clear history
469
+ detector.clearHistory();
470
+ ```
471
+
472
+ ### Environment-Specific Examples
473
+
474
+ ```typescript
475
+ // Browser-only with LocalStorage
476
+ const browserDetector = new ManneriDetector({}, {
477
+ persistenceProvider: new LocalStoragePersistenceProvider()
478
+ });
479
+
480
+ // Node.js with file storage
481
+ class FilePersistenceProvider implements PersistenceProvider {
482
+ constructor(private filePath: string) {}
483
+
484
+ save(data: StorageData): boolean {
485
+ try {
486
+ fs.writeFileSync(this.filePath, JSON.stringify(data));
487
+ return true;
488
+ } catch {
489
+ return false;
490
+ }
491
+ }
492
+
493
+ load(): StorageData | null {
494
+ try {
495
+ const data = fs.readFileSync(this.filePath, 'utf8');
496
+ return JSON.parse(data);
497
+ } catch {
498
+ return null;
499
+ }
500
+ }
501
+
502
+ clear(): boolean {
503
+ try {
504
+ fs.unlinkSync(this.filePath);
505
+ return true;
506
+ } catch {
507
+ return false;
508
+ }
509
+ }
510
+ }
511
+
512
+ const nodeDetector = new ManneriDetector({}, {
513
+ persistenceProvider: new FilePersistenceProvider('./manneri-data.json')
514
+ });
515
+
516
+ // Deno with Deno.KV
517
+ class DenoKvPersistenceProvider implements PersistenceProvider {
518
+ constructor(private kv: Deno.Kv) {}
519
+
520
+ async save(data: StorageData): Promise<boolean> {
521
+ try {
522
+ await this.kv.set(['manneri', 'data'], data);
523
+ return true;
524
+ } catch {
525
+ return false;
526
+ }
527
+ }
528
+
529
+ async load(): Promise<StorageData | null> {
530
+ try {
531
+ const result = await this.kv.get(['manneri', 'data']);
532
+ return result.value as StorageData | null;
533
+ } catch {
534
+ return null;
535
+ }
536
+ }
537
+
538
+ async clear(): Promise<boolean> {
539
+ try {
540
+ await this.kv.delete(['manneri', 'data']);
541
+ return true;
542
+ } catch {
543
+ return false;
544
+ }
545
+ }
546
+ }
547
+
548
+ const kv = await Deno.openKv();
549
+ const denoDetector = new ManneriDetector({}, {
550
+ persistenceProvider: new DenoKvPersistenceProvider(kv)
551
+ });
552
+ ```
553
+
554
+ ## TypeScript Type Definitions
555
+
556
+ ```typescript
557
+ import type {
558
+ Message,
559
+ ManneriConfig,
560
+ AnalysisResult,
561
+ DiversificationPrompt,
562
+ LocalizedPrompts,
563
+ PromptTemplates,
564
+ SupportedLanguage,
565
+ PersistenceProvider,
566
+ PersistenceConfig,
567
+ StorageData
568
+ } from '@aituber-onair/manneri';
569
+ ```
570
+
571
+ ## Browser Support
572
+
573
+ - Chrome 90+
574
+ - Firefox 88+
575
+ - Safari 14+
576
+ - Edge 90+
577
+
578
+ ## Performance
579
+
580
+ - Lightweight: < 50KB gzipped
581
+ - Fast: Real-time analysis < 100ms
582
+ - Memory efficient: Automatic cache cleanup
583
+
584
+ ## License
585
+
586
+ MIT License
587
+
588
+ ## Contributing
589
+
590
+ Pull requests and issues are welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
591
+
592
+ ## Support
593
+
594
+ - GitHub Issues: https://github.com/shinshin86/aituber-onair/issues
595
+ - Documentation: https://github.com/shinshin86/aituber-onair/tree/main/packages/manneri
596
+
597
+ ## Related Projects
598
+
599
+ - [AITuber OnAir](https://github.com/shinshin86/aituber-onair) - Main project
600
+ - [@aituber-onair/core](https://github.com/shinshin86/aituber-onair/tree/main/packages/core) - Core library
@@ -0,0 +1,48 @@
1
+ import type { Message, TopicInfo, TextAnalysisOptions } from '../types/index.js';
2
+ export interface KeywordFrequency {
3
+ keyword: string;
4
+ frequency: number;
5
+ score: number;
6
+ firstSeen: number;
7
+ lastSeen: number;
8
+ contexts: string[];
9
+ }
10
+ export interface TopicCluster {
11
+ id: string;
12
+ keywords: string[];
13
+ score: number;
14
+ messageCount: number;
15
+ firstMessage: number;
16
+ lastMessage: number;
17
+ }
18
+ export declare class KeywordExtractor {
19
+ private readonly options;
20
+ private readonly keywordFrequencies;
21
+ private readonly topicClusters;
22
+ private readonly maxKeywords;
23
+ private readonly maxContextLength;
24
+ constructor(options?: Partial<TextAnalysisOptions>);
25
+ extractKeywordsFromMessage(message: Message): string[];
26
+ extractKeywordsFromMessages(messages: Message[]): string[];
27
+ analyzeKeywordFrequencies(messages: Message[]): KeywordFrequency[];
28
+ detectTopicShift(recentMessages: Message[], historicalMessages: Message[], threshold?: number): {
29
+ hasShift: boolean;
30
+ newTopics: string[];
31
+ oldTopics: string[];
32
+ };
33
+ analyzeTopicClusters(messages: Message[]): TopicCluster[];
34
+ getTopicInfo(messages: Message[]): TopicInfo[];
35
+ findRepeatedKeywords(messages: Message[], minRepetitions?: number, windowSize?: number): Array<{
36
+ keyword: string;
37
+ positions: number[];
38
+ density: number;
39
+ }>;
40
+ private calculateKeywordScore;
41
+ private findRelatedKeywords;
42
+ private calculateSemanticSimilarity;
43
+ private generateClusterId;
44
+ private calculateClusterScore;
45
+ private categorizeKeywords;
46
+ private calculateKeywordDensity;
47
+ clearCache(): void;
48
+ }