feat: add multilingual chat support with markdown rendering
Some checks failed
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Security Scanning (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (api) (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (chat) (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (frontend) (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (worker) (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint Code (push) Has started running
CI/CD Pipeline / Generate Documentation (push) Has started running
Some checks failed
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Security Scanning (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (api) (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (chat) (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (frontend) (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (worker) (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint Code (push) Has started running
CI/CD Pipeline / Generate Documentation (push) Has started running
- Fix Socket.IO proxy configuration in nginx for chat connectivity - Add Socket.IO path routing (/socket.io/) with WebSocket upgrade support - Fix frontend healthcheck to use curl instead of wget - Add react-markdown and remark-gfm for proper markdown rendering - Implement language selector in chat interface (8 languages supported) - Add language parameter to chat agent and LLM prompts - Support English, Italian, Spanish, French, German, Portuguese, Chinese, Japanese This resolves the chat connection issues and enables users to receive AI responses in their preferred language with properly formatted markdown. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,8 @@
|
||||
"@emotion/react": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"socket.io-client": "^4.6.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"date-fns": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -3,20 +3,25 @@ import {
|
||||
AppBar, Toolbar, Typography, Container, Box, Paper,
|
||||
TextField, Button, List, ListItem, ListItemText,
|
||||
CircularProgress, Chip, Grid, Card, CardContent,
|
||||
Tabs, Tab, Divider, IconButton
|
||||
Tabs, Tab, Divider, IconButton, Select, MenuItem,
|
||||
FormControl, InputLabel
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Send as SendIcon,
|
||||
Search as SearchIcon,
|
||||
Description as DocIcon,
|
||||
Support as SupportIcon,
|
||||
CloudUpload as UploadIcon
|
||||
CloudUpload as UploadIcon,
|
||||
Language as LanguageIcon
|
||||
} from '@mui/icons-material';
|
||||
import axios from 'axios';
|
||||
import io from 'socket.io-client';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
||||
const CHAT_URL = import.meta.env.VITE_CHAT_URL || 'http://localhost:8001';
|
||||
// Use relative URLs to work with nginx proxy in production
|
||||
const API_URL = import.meta.env.VITE_API_URL || (typeof window !== 'undefined' ? window.location.origin + '/api' : 'http://localhost:8000');
|
||||
const CHAT_URL = import.meta.env.VITE_CHAT_URL || (typeof window !== 'undefined' ? window.location.origin : 'http://localhost:8001');
|
||||
|
||||
function App() {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
@@ -56,6 +61,7 @@ function ChatInterface() {
|
||||
const [input, setInput] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [socket, setSocket] = useState(null);
|
||||
const [language, setLanguage] = useState('en');
|
||||
const messagesEndRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -90,8 +96,8 @@ function ChatInterface() {
|
||||
|
||||
setMessages(prev => [...prev, userMessage]);
|
||||
setLoading(true);
|
||||
|
||||
socket.emit('chat', { message: input, history: messages });
|
||||
|
||||
socket.emit('chat', { message: input, history: messages, language: language });
|
||||
setInput('');
|
||||
};
|
||||
|
||||
@@ -99,11 +105,32 @@ function ChatInterface() {
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={8}>
|
||||
<Paper sx={{ height: '70vh', display: 'flex', flexDirection: 'column' }}>
|
||||
<Box sx={{ p: 2, bgcolor: 'primary.main', color: 'white' }}>
|
||||
<Typography variant="h6">Technical Support Chat</Typography>
|
||||
<Typography variant="caption">
|
||||
AI-powered assistant with access to datacenter documentation
|
||||
</Typography>
|
||||
<Box sx={{ p: 2, bgcolor: 'primary.main', color: 'white', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Box>
|
||||
<Typography variant="h6">Technical Support Chat</Typography>
|
||||
<Typography variant="caption">
|
||||
AI-powered assistant with access to datacenter documentation
|
||||
</Typography>
|
||||
</Box>
|
||||
<FormControl size="small" sx={{ minWidth: 120, bgcolor: 'white', borderRadius: 1 }}>
|
||||
<InputLabel id="language-select-label">Language</InputLabel>
|
||||
<Select
|
||||
labelId="language-select-label"
|
||||
value={language}
|
||||
label="Language"
|
||||
onChange={(e) => setLanguage(e.target.value)}
|
||||
startAdornment={<LanguageIcon sx={{ mr: 0.5, color: 'action.active' }} />}
|
||||
>
|
||||
<MenuItem value="en">🇬🇧 English</MenuItem>
|
||||
<MenuItem value="it">🇮🇹 Italiano</MenuItem>
|
||||
<MenuItem value="es">🇪🇸 Español</MenuItem>
|
||||
<MenuItem value="fr">🇫🇷 Français</MenuItem>
|
||||
<MenuItem value="de">🇩🇪 Deutsch</MenuItem>
|
||||
<MenuItem value="pt">🇵🇹 Português</MenuItem>
|
||||
<MenuItem value="zh">🇨🇳 中文</MenuItem>
|
||||
<MenuItem value="ja">🇯🇵 日本語</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
|
||||
@@ -117,7 +144,38 @@ function ChatInterface() {
|
||||
maxWidth: '70%',
|
||||
bgcolor: msg.role === 'user' ? 'primary.light' : 'grey.100'
|
||||
}}>
|
||||
<Typography variant="body1">{msg.content}</Typography>
|
||||
{msg.role === 'user' ? (
|
||||
<Typography variant="body1">{msg.content}</Typography>
|
||||
) : (
|
||||
<Box sx={{
|
||||
'& h1, & h2, & h3': { mt: 2, mb: 1 },
|
||||
'& h1': { fontSize: '1.5rem', fontWeight: 600 },
|
||||
'& h2': { fontSize: '1.3rem', fontWeight: 600 },
|
||||
'& h3': { fontSize: '1.1rem', fontWeight: 600 },
|
||||
'& p': { mb: 1 },
|
||||
'& ul, & ol': { pl: 2, mb: 1 },
|
||||
'& li': { mb: 0.5 },
|
||||
'& code': {
|
||||
bgcolor: 'rgba(0,0,0,0.05)',
|
||||
p: 0.5,
|
||||
borderRadius: 1,
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.9em'
|
||||
},
|
||||
'& pre': {
|
||||
bgcolor: 'rgba(0,0,0,0.05)',
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
overflow: 'auto',
|
||||
'& code': { bgcolor: 'transparent', p: 0 }
|
||||
},
|
||||
'& hr': { my: 2, border: 'none', borderTop: '1px solid rgba(0,0,0,0.12)' }
|
||||
}}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{msg.content}
|
||||
</ReactMarkdown>
|
||||
</Box>
|
||||
)}
|
||||
{msg.related_docs && (
|
||||
<Box sx={{ mt: 1 }}>
|
||||
{msg.related_docs.map((doc, i) => (
|
||||
|
||||
Reference in New Issue
Block a user