feat(s2s-text): dedicated text-mode prompt + Markdown rendering
Architecture fix: voice and text mode now have completely separate prompts. Backend: - VoiceAssistantProfileSupport.buildTextSystemRole: dedicated text-mode system role that inherits all business rules (identity, KB-first, sensitive topics, sales guidance, personal info) but removes voice-specific constraints (short sentences, colloquial, single-line conclusion). - DEFAULT_TEXT_SPEAKING_STYLE: text-specific style demanding detailed, structured, Markdown-formatted answers with complete information. - VoiceGatewayService.handleStart: switch between voice/text system role and speaking style based on state.textMode. - VoiceGatewayService.buildStartSessionPayload: preserve Markdown in text mode (voice mode still strips asterisks/backticks via normalizeTextForSpeech to avoid TTS pronouncing format chars). Frontend: - Added react-markdown@9 + remark-gfm@4 dependencies. - ChatPanel renders assistant messages (non-voice) with ReactMarkdown: headings, lists (ul/ol), bold, italic, inline/block code, tables, blockquote, links, horizontal rules — all styled with Tailwind classes matching the dark theme. - User messages and voice-handoff messages remain plain text. Verification: mvn test VoiceGatewaySmokeTest 20/20 pass, vite build succeeds.
This commit is contained in:
1477
test2/client/package-lock.json
generated
1477
test2/client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,9 @@
|
||||
"axios": "^1.6.2",
|
||||
"lucide-react": "^0.344.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^9.1.0",
|
||||
"remark-gfm": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { Send, Bot, User, Loader2, ArrowLeft, Sparkles, Wrench, StopCircle } from 'lucide-react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { startChatSession, sendMessageStream } from '../services/chatApi';
|
||||
import { getSessionHistory } from '../services/voiceApi';
|
||||
import { NativeVoiceService } from '../services/nativeVoiceService';
|
||||
@@ -368,7 +370,38 @@ export default function ChatPanel({ sessionId, voiceSubtitles, settings, onBack,
|
||||
: 'bg-slate-700/50 text-slate-200 rounded-tl-sm'
|
||||
}`}
|
||||
>
|
||||
{msg.content}
|
||||
{msg.role === 'assistant' && !msg.fromVoice ? (
|
||||
<div className="markdown-body">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
p: ({ node, ...props }) => <p className="mb-2 last:mb-0" {...props} />,
|
||||
ul: ({ node, ...props }) => <ul className="list-disc pl-5 mb-2 space-y-0.5" {...props} />,
|
||||
ol: ({ node, ...props }) => <ol className="list-decimal pl-5 mb-2 space-y-0.5" {...props} />,
|
||||
li: ({ node, ...props }) => <li className="leading-relaxed" {...props} />,
|
||||
h1: ({ node, ...props }) => <h1 className="text-base font-bold mt-2 mb-1.5" {...props} />,
|
||||
h2: ({ node, ...props }) => <h2 className="text-sm font-bold mt-2 mb-1.5" {...props} />,
|
||||
h3: ({ node, ...props }) => <h3 className="text-sm font-semibold mt-2 mb-1" {...props} />,
|
||||
strong: ({ node, ...props }) => <strong className="font-semibold text-white" {...props} />,
|
||||
em: ({ node, ...props }) => <em className="italic" {...props} />,
|
||||
code: ({ node, inline, ...props }) => inline
|
||||
? <code className="px-1 py-0.5 rounded bg-slate-900/60 text-violet-300 text-[12px]" {...props} />
|
||||
: <code className="block px-2 py-1.5 rounded bg-slate-900/80 text-violet-200 text-[12px] overflow-x-auto my-1.5" {...props} />,
|
||||
pre: ({ node, ...props }) => <pre className="my-1.5" {...props} />,
|
||||
table: ({ node, ...props }) => <div className="overflow-x-auto my-2"><table className="min-w-full text-xs border-collapse" {...props} /></div>,
|
||||
th: ({ node, ...props }) => <th className="border border-slate-600/40 px-2 py-1 bg-slate-900/40 font-semibold text-left" {...props} />,
|
||||
td: ({ node, ...props }) => <td className="border border-slate-600/40 px-2 py-1" {...props} />,
|
||||
blockquote: ({ node, ...props }) => <blockquote className="border-l-2 border-violet-500/50 pl-2 italic text-slate-300 my-1.5" {...props} />,
|
||||
a: ({ node, ...props }) => <a className="text-violet-400 hover:text-violet-300 underline" target="_blank" rel="noopener noreferrer" {...props} />,
|
||||
hr: () => <hr className="my-2 border-slate-600/30" />,
|
||||
}}
|
||||
>
|
||||
{msg.content || ''}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
) : (
|
||||
msg.content
|
||||
)}
|
||||
{msg.fromVoice && (
|
||||
<span className="ml-1.5 text-[9px] text-slate-600 align-middle">🎙</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user