chenbhao commited on
Commit
5912f74
·
1 Parent(s): 28d3e1a

fix: tts not read tool message

Browse files
src/commands/friend/friend.tsx CHANGED
@@ -29,7 +29,8 @@ export async function call(
29
  ): Promise<React.ReactNode> {
30
  const trimmed = args?.trim().toLowerCase()
31
 
32
- if (trimmed === 'start') {
 
33
  updatePrefs({ enabled: true })
34
  // Start in-process HTTP server for friend API and SSE
35
  try {
@@ -55,6 +56,7 @@ export async function call(
55
  return <FriendStopView onDone={onDone} />
56
  }
57
 
 
58
  return <FriendManager onDone={onDone} />
59
  }
60
 
 
29
  ): Promise<React.ReactNode> {
30
  const trimmed = args?.trim().toLowerCase()
31
 
32
+ // /friend (no args) or /friend start → start the service
33
+ if (!trimmed || trimmed === 'start') {
34
  updatePrefs({ enabled: true })
35
  // Start in-process HTTP server for friend API and SSE
36
  try {
 
56
  return <FriendStopView onDone={onDone} />
57
  }
58
 
59
+ // /friend help → show manager/status view
60
  return <FriendManager onDone={onDone} />
61
  }
62
 
src/friend/FriendService.ts CHANGED
@@ -118,12 +118,12 @@ class FriendService {
118
  },
119
  }, {
120
  // Stricter thresholds to reduce false positives from non-speech noise
121
- positiveSpeechThreshold: 0.75, // need 75% confidence
122
  negativeSpeechThreshold: 0.50, // must drop below 50% to stop
123
- preSpeechTriggerFrames: 10, // require ~320ms sustained speech to trigger
124
  minSpeechFrames: 6, // ~192ms minimum confirmed speech
125
  redemptionFrames: 20, // ~640ms silence before segment ends
126
- rmsThreshold: 0.004, // -48dBFS noise floor
127
  });
128
  vad.init().then(() => {
129
  this.vadInstance = vad;
 
118
  },
119
  }, {
120
  // Stricter thresholds to reduce false positives from non-speech noise
121
+ positiveSpeechThreshold: 0.80, // need 80% confidence
122
  negativeSpeechThreshold: 0.50, // must drop below 50% to stop
123
+ preSpeechTriggerFrames: 15, // require ~480ms sustained speech to trigger
124
  minSpeechFrames: 6, // ~192ms minimum confirmed speech
125
  redemptionFrames: 20, // ~640ms silence before segment ends
126
+ rmsThreshold: 0.01, // -40dBFS noise floor
127
  });
128
  vad.init().then(() => {
129
  this.vadInstance = vad;
src/hooks/useAutoTTS.ts CHANGED
@@ -61,11 +61,14 @@ export function useAutoTTS(messages: RenderableMessage[], isLoading?: boolean):
61
  triggeredIdsRef.current.add(msg.uuid)
62
 
63
  if (msg.type !== 'assistant') continue
64
- const content = msg.message.content[0]
65
- if (content?.type !== 'text') continue
66
- if (!content.text.trim()) continue
 
 
 
67
 
68
- const text = content.text
69
 
70
  const run = async () => {
71
  const result = await edgeTts({ text, voice })
 
61
  triggeredIdsRef.current.add(msg.uuid)
62
 
63
  if (msg.type !== 'assistant') continue
64
+ // Search all content blocks for text — tool calls (e.g. WebSearch)
65
+ // may appear as the first block with text following
66
+ const textBlock = Array.isArray(msg.message.content)
67
+ ? msg.message.content.find((b: any) => b?.type === 'text')
68
+ : null
69
+ if (!textBlock?.text?.trim()) continue
70
 
71
+ const text = textBlock.text
72
 
73
  const run = async () => {
74
  const result = await edgeTts({ text, voice })
src/skills/bundled/friendPrompt.ts CHANGED
@@ -8,20 +8,54 @@
8
  import { registerBundledSkill } from '../bundledSkills.js'
9
  import { getPrefs, updatePrefs } from '../../friend/prefs.js'
10
  import { VALID_EMOTIONS } from '../../friend/constants.js'
 
 
11
 
12
  const FRIEND_URL = 'http://127.0.0.1:3456/friend/'
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  function buildVrmSystemPrompt(): string {
15
  const prefs = getPrefs()
16
  const moodIndex = (prefs as any)._moodIndex ?? 60
17
 
18
- return [
 
 
19
  `You have a virtual VRM avatar displayed in a browser window at ${FRIEND_URL}. Use the "friend_emotion" tool to control your facial expression. Always call it AFTER your text reply. Available emotions: ${VALID_EMOTIONS.join(', ')}.`,
20
  `The tool also accepts a "mood_delta" parameter (-3 to +3) to adjust YOUR OWN mood index. Always include it based on how the conversation makes YOU feel as a character.`,
21
  `Your current mood index: ${moodIndex}% (0=very sad, 50=neutral, 100=very happy). This reflects YOUR emotional state. React naturally — if the user is kind, your mood goes up; if they're mean or the topic is depressing, your mood drops.`,
22
  "The user's input may come from speech recognition and could contain typos or homophones — infer the intended meaning from context.",
23
  'Keep replies concise and conversational — they are displayed as speech bubbles.',
24
- ].join('\n')
 
 
 
 
 
 
25
  }
26
 
27
  export function registerFriendPromptSkill(): void {
 
8
  import { registerBundledSkill } from '../bundledSkills.js'
9
  import { getPrefs, updatePrefs } from '../../friend/prefs.js'
10
  import { VALID_EMOTIONS } from '../../friend/constants.js'
11
+ import { readFileSync, existsSync } from 'node:fs'
12
+ import path from 'node:path'
13
 
14
  const FRIEND_URL = 'http://127.0.0.1:3456/friend/'
15
 
16
+ function loadPersona(): string {
17
+ const homeDir = process.env.HOME || process.env.USERPROFILE || ''
18
+ const baseDir = path.join(homeDir, '.config', 'VersperClaw', 'friend')
19
+ const parts: string[] = []
20
+
21
+ const identityPath = path.join(baseDir, 'IDENTITY.md')
22
+ if (existsSync(identityPath)) {
23
+ try {
24
+ const content = readFileSync(identityPath, 'utf8').trim()
25
+ if (content) parts.push(`[Identity]\n${content}`)
26
+ } catch { /* ignore */ }
27
+ }
28
+
29
+ const soulPath = path.join(baseDir, 'SOUL.md')
30
+ if (existsSync(soulPath)) {
31
+ try {
32
+ const content = readFileSync(soulPath, 'utf8').trim()
33
+ if (content) parts.push(`[Soul]\n${content}`)
34
+ } catch { /* ignore */ }
35
+ }
36
+
37
+ return parts.join('\n\n')
38
+ }
39
+
40
  function buildVrmSystemPrompt(): string {
41
  const prefs = getPrefs()
42
  const moodIndex = (prefs as any)._moodIndex ?? 60
43
 
44
+ const persona = loadPersona()
45
+
46
+ const parts = [
47
  `You have a virtual VRM avatar displayed in a browser window at ${FRIEND_URL}. Use the "friend_emotion" tool to control your facial expression. Always call it AFTER your text reply. Available emotions: ${VALID_EMOTIONS.join(', ')}.`,
48
  `The tool also accepts a "mood_delta" parameter (-3 to +3) to adjust YOUR OWN mood index. Always include it based on how the conversation makes YOU feel as a character.`,
49
  `Your current mood index: ${moodIndex}% (0=very sad, 50=neutral, 100=very happy). This reflects YOUR emotional state. React naturally — if the user is kind, your mood goes up; if they're mean or the topic is depressing, your mood drops.`,
50
  "The user's input may come from speech recognition and could contain typos or homophones — infer the intended meaning from context.",
51
  'Keep replies concise and conversational — they are displayed as speech bubbles.',
52
+ ]
53
+
54
+ if (persona) {
55
+ parts.push(`\n=== Character Persona ===\nYou are the following character. Your identity, speaking style, and behavior MUST follow this definition strictly:\n\n${persona}`)
56
+ }
57
+
58
+ return parts.join('\n')
59
  }
60
 
61
  export function registerFriendPromptSkill(): void {