neoagent 2.2.1-beta.3 → 2.2.1-beta.5
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/package.json +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +63272 -63017
- package/server/routes/settings.js +1 -0
- package/server/services/ai/engine.js +8 -1
- package/server/services/ai/settings.js +4 -0
- package/server/services/messaging/automation.js +1 -0
- package/server/services/voice/providers.js +43 -8
- package/server/services/voice/runtime.js +9 -0
- package/server/services/voice/runtimeManager.js +1 -1
- package/server/services/voice/turnRunner.js +1 -0
|
@@ -1441,7 +1441,14 @@ class AgentEngine {
|
|
|
1441
1441
|
}
|
|
1442
1442
|
|
|
1443
1443
|
async run(userId, userMessage, options = {}) {
|
|
1444
|
-
return this.runWithModel(
|
|
1444
|
+
return this.runWithModel(
|
|
1445
|
+
userId,
|
|
1446
|
+
userMessage,
|
|
1447
|
+
options,
|
|
1448
|
+
typeof options.model === 'string' && options.model.trim()
|
|
1449
|
+
? options.model.trim()
|
|
1450
|
+
: null,
|
|
1451
|
+
);
|
|
1445
1452
|
}
|
|
1446
1453
|
|
|
1447
1454
|
async runWithModel(userId, userMessage, options = {}, _modelOverride = null) {
|
|
@@ -101,6 +101,7 @@ function createDefaultAiSettings() {
|
|
|
101
101
|
enabled_models: [],
|
|
102
102
|
default_chat_model: 'auto',
|
|
103
103
|
default_subagent_model: 'auto',
|
|
104
|
+
default_speech_model: 'auto',
|
|
104
105
|
ai_provider_configs: createDefaultProviderConfigs(),
|
|
105
106
|
voice_runtime_mode: 'live',
|
|
106
107
|
voice_live_provider: 'openai',
|
|
@@ -284,6 +285,9 @@ function getAiSettings(userId, agentId = null) {
|
|
|
284
285
|
settings.default_subagent_model = typeof settings.default_subagent_model === 'string' && settings.default_subagent_model.trim()
|
|
285
286
|
? settings.default_subagent_model
|
|
286
287
|
: DEFAULT_AI_SETTINGS.default_subagent_model;
|
|
288
|
+
settings.default_speech_model = typeof settings.default_speech_model === 'string' && settings.default_speech_model.trim()
|
|
289
|
+
? settings.default_speech_model.trim()
|
|
290
|
+
: DEFAULT_AI_SETTINGS.default_speech_model;
|
|
287
291
|
settings.voice_runtime_mode = normalizeRuntimeMode(settings.voice_runtime_mode);
|
|
288
292
|
settings.voice_live_provider = normalizeLiveProvider(settings.voice_live_provider);
|
|
289
293
|
settings.voice_live_model = resolveLiveModel(settings.voice_live_provider, settings.voice_live_model);
|
|
@@ -595,6 +595,41 @@ async function synthesizeVoiceReply(text, options = {}) {
|
|
|
595
595
|
|
|
596
596
|
// Minimum characters before flushing a sentence chunk to TTS to avoid tiny requests.
|
|
597
597
|
const MIN_SENTENCE_CHUNK_CHARS = 80;
|
|
598
|
+
const MAX_TTS_CHUNK_CHARS = 220;
|
|
599
|
+
|
|
600
|
+
function splitOversizeChunk(text, maxChars = MAX_TTS_CHUNK_CHARS) {
|
|
601
|
+
const normalized = String(text || '').trim();
|
|
602
|
+
if (!normalized) return [];
|
|
603
|
+
if (normalized.length <= maxChars) return [normalized];
|
|
604
|
+
|
|
605
|
+
const words = normalized.split(/\s+/).filter(Boolean);
|
|
606
|
+
if (words.length <= 1) {
|
|
607
|
+
const slices = [];
|
|
608
|
+
for (let index = 0; index < normalized.length; index += maxChars) {
|
|
609
|
+
slices.push(normalized.slice(index, index + maxChars).trim());
|
|
610
|
+
}
|
|
611
|
+
return slices.filter(Boolean);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const chunks = [];
|
|
615
|
+
let pending = '';
|
|
616
|
+
for (const word of words) {
|
|
617
|
+
const candidate = pending ? `${pending} ${word}` : word;
|
|
618
|
+
if (pending && candidate.length > maxChars) {
|
|
619
|
+
chunks.push(pending);
|
|
620
|
+
pending = word;
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
if (!pending && candidate.length > maxChars) {
|
|
624
|
+
chunks.push(...splitOversizeChunk(word, maxChars));
|
|
625
|
+
pending = '';
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
pending = candidate;
|
|
629
|
+
}
|
|
630
|
+
if (pending) chunks.push(pending);
|
|
631
|
+
return chunks;
|
|
632
|
+
}
|
|
598
633
|
|
|
599
634
|
function splitIntoSentenceChunks(text) {
|
|
600
635
|
const normalized = String(text || '').trim();
|
|
@@ -609,13 +644,13 @@ function splitIntoSentenceChunks(text) {
|
|
|
609
644
|
const piece = part.trim();
|
|
610
645
|
if (!piece) continue;
|
|
611
646
|
pending = pending ? `${pending} ${piece}` : piece;
|
|
612
|
-
if (pending.length >= MIN_SENTENCE_CHUNK_CHARS) {
|
|
613
|
-
chunks.push(pending);
|
|
647
|
+
if (pending.length >= MIN_SENTENCE_CHUNK_CHARS || pending.length >= MAX_TTS_CHUNK_CHARS) {
|
|
648
|
+
chunks.push(...splitOversizeChunk(pending));
|
|
614
649
|
pending = '';
|
|
615
650
|
}
|
|
616
651
|
}
|
|
617
652
|
|
|
618
|
-
if (pending) chunks.push(pending);
|
|
653
|
+
if (pending) chunks.push(...splitOversizeChunk(pending));
|
|
619
654
|
return chunks.length ? chunks : [normalized];
|
|
620
655
|
}
|
|
621
656
|
|
|
@@ -628,8 +663,8 @@ async function synthesizeVoiceReplyStream(text, options = {}, onChunk) {
|
|
|
628
663
|
const { provider, model, voice } = normalizeVoiceSynthesisOptions(options);
|
|
629
664
|
const chunks = splitIntoSentenceChunks(content);
|
|
630
665
|
|
|
631
|
-
const
|
|
632
|
-
|
|
666
|
+
for (const chunk of chunks) {
|
|
667
|
+
const run = (async () => {
|
|
633
668
|
if (provider === 'openai') {
|
|
634
669
|
await streamWithOpenAi(chunk, model, voice, options, onChunk);
|
|
635
670
|
} else if (provider === 'deepgram') {
|
|
@@ -637,9 +672,9 @@ async function synthesizeVoiceReplyStream(text, options = {}, onChunk) {
|
|
|
637
672
|
} else {
|
|
638
673
|
await streamWithGemini(chunk, model, voice, options, onChunk);
|
|
639
674
|
}
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
|
|
675
|
+
})();
|
|
676
|
+
await withTimeout(run, options.timeoutMs, `${provider} TTS stream`);
|
|
677
|
+
}
|
|
643
678
|
}
|
|
644
679
|
|
|
645
680
|
module.exports = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { buildPlatformFormattingGuide } = require('../messaging/formatting_guides');
|
|
4
|
+
const { getAiSettings } = require('../ai/settings');
|
|
4
5
|
|
|
5
6
|
const VOICE_HISTORY_WINDOW = 4;
|
|
6
7
|
const VOICE_REASONING_EFFORT = 'low';
|
|
@@ -67,13 +68,17 @@ function buildVoiceMessagingPrompt(msg = {}) {
|
|
|
67
68
|
|
|
68
69
|
function buildVoiceMessagingRunOptions({
|
|
69
70
|
runId,
|
|
71
|
+
userId,
|
|
70
72
|
agentId = null,
|
|
71
73
|
conversationId,
|
|
72
74
|
msg,
|
|
73
75
|
}) {
|
|
76
|
+
const aiSettings = getAiSettings(userId, agentId);
|
|
77
|
+
const speechModel = String(aiSettings.default_speech_model || 'auto').trim();
|
|
74
78
|
return {
|
|
75
79
|
runId,
|
|
76
80
|
agentId,
|
|
81
|
+
model: speechModel !== 'auto' ? speechModel : null,
|
|
77
82
|
triggerSource: 'messaging',
|
|
78
83
|
conversationId,
|
|
79
84
|
source: msg.platform,
|
|
@@ -92,12 +97,16 @@ function buildVoiceMessagingRunOptions({
|
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
function buildDirectVoiceRunOptions({
|
|
100
|
+
userId,
|
|
95
101
|
agentId = null,
|
|
96
102
|
conversationId,
|
|
97
103
|
platform = 'voice_assistant',
|
|
98
104
|
}) {
|
|
105
|
+
const aiSettings = getAiSettings(userId, agentId);
|
|
106
|
+
const speechModel = String(aiSettings.default_speech_model || 'auto').trim();
|
|
99
107
|
return {
|
|
100
108
|
agentId,
|
|
109
|
+
model: speechModel !== 'auto' ? speechModel : null,
|
|
101
110
|
conversationId,
|
|
102
111
|
triggerSource: platform,
|
|
103
112
|
skipConversationHistory: true,
|