ui-nyla/src/pages/admin/ChatbotConfigSection.tsx
2026-04-11 19:44:52 +02:00

169 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ChatbotConfigSection Component
*
* Displays chatbot-specific configuration fields (connector, system prompt)
* Only shown when featureCode is "chatbot"
*/
import React, { useEffect } from 'react';
import { TextField } from '../../components/UiComponents/TextField';
import { useBilling } from '../../hooks/useBilling';
import styles from './Admin.module.css';
import { useLanguage } from '../../providers/language/LanguageContext';
const PROVIDER_LABELS: Record<string, string> = {
anthropic: 'Anthropic (Claude)',
openai: 'OpenAI (GPT)',
perplexity: 'Perplexity',
tavily: 'Tavily (Web Search)',
privatellm: 'Private LLM',
internal: 'Internal',
};
export interface ChatbotConfig {
connector: string;
systemPrompt: string;
}
export interface ChatbotConfigSectionProps {
connectors: string[]; // Array of selected connector types (database connectors only)
systemPrompt: string;
enableWebResearch: boolean; // Enable Tavily web research
allowedProviders: string[]; // Selected LLM providers (empty = all allowed)
onConnectorsChange: (connectors: string[]) => void;
onSystemPromptChange: (prompt: string) => void;
onEnableWebResearchChange: (enabled: boolean) => void;
onAllowedProvidersChange: (providers: string[]) => void;
}
export const ChatbotConfigSection: React.FC<ChatbotConfigSectionProps> = ({ connectors,
systemPrompt,
enableWebResearch,
allowedProviders,
onConnectorsChange,
onSystemPromptChange,
onEnableWebResearchChange,
onAllowedProvidersChange,
}) => {
const { t } = useLanguage();
const { allowedProviders: availableProviders, loadAllowedProviders, loading: providersLoading } = useBilling();
useEffect(() => {
if (availableProviders.length === 0 && !providersLoading) {
loadAllowedProviders();
}
}, []);
const availableConnectors = [
{ id: 'preprocessor', label: t('Althaus Preprocessor'), value: 'preprocessor' }
];
const handleConnectorToggle = (connectorValue: string) => {
if (connectors.includes(connectorValue)) {
onConnectorsChange(connectors.filter(c => c !== connectorValue));
} else {
onConnectorsChange([...connectors, connectorValue]);
}
};
const handleProviderToggle = (provider: string) => {
if (allowedProviders.includes(provider)) {
onAllowedProvidersChange(allowedProviders.filter(p => p !== provider));
} else {
onAllowedProvidersChange([...allowedProviders, provider]);
}
};
return (
<div className={styles.chatbotConfigSection}>
<div className={styles.configField}>
<label className={styles.configLabel}>{t('Connector')}:</label>
<div className={styles.multiselectContainer}>
{availableConnectors.map(connector => {
const isSelected = connectors.includes(connector.value);
return (
<label key={connector.id} className={styles.multiselectOption}>
<input
type="checkbox"
checked={isSelected}
onChange={() => handleConnectorToggle(connector.value)}
className={styles.multiselectCheckbox}
/>
<span className={styles.multiselectLabel}>{connector.label}</span>
</label>
);
})}
</div>
{connectors.length === 0 && (
<p style={{ fontSize: '0.75rem', color: 'var(--text-secondary)', marginTop: '0.5rem' }}>
{t('Ohne Connector werden keine SQL-Abfragen unterstützt.')}
</p>
)}
</div>
<div className={styles.configField}>
<label className={styles.configLabel} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<input
type="checkbox"
checked={enableWebResearch}
onChange={(e) => onEnableWebResearchChange(e.target.checked)}
className={styles.multiselectCheckbox}
/>
<span>{t('Web Research aktivieren (Tavily)')}</span>
</label>
<p className={styles.configHelpText}>
{t('Wenn aktiviert, führt der Chatbot zusätzlich Web-Recherchen mit Tavily durch, um aktuelle Informationen aus dem Internet zu finden.')}
</p>
</div>
<div className={styles.configField}>
<label className={styles.configLabel}>{t('LLM-Anbieter')}:</label>
<div className={styles.multiselectContainer}>
{providersLoading ? (
<span style={{ fontSize: '0.85rem', color: 'var(--text-secondary)' }}>{t('Anbieter laden')}</span>
) : (
availableProviders.map(provider => (
<label key={provider} className={styles.multiselectOption}>
<input
type="checkbox"
checked={allowedProviders.includes(provider)}
onChange={() => handleProviderToggle(provider)}
className={styles.multiselectCheckbox}
/>
<span className={styles.multiselectLabel}>
{PROVIDER_LABELS[provider] || provider}
</span>
</label>
))
)}
</div>
{allowedProviders.length === 0 && !providersLoading && (
<p style={{ fontSize: '0.75rem', color: 'var(--text-secondary)', marginTop: '0.5rem' }}>
{t('Keine Einschränkung alle verfügbaren Anbieter werden verwendet.')}
</p>
)}
</div>
<div className={styles.configField}>
<label className={styles.configLabel}>
{t('System Prompt')}: <span style={{ color: 'var(--error-color)' }}>*</span>
</label>
<TextField
type="text"
value={systemPrompt}
onChange={onSystemPromptChange}
placeholder={t('Benutzerdefinierter Systemprompt für den Chatbot')}
className={styles.configTextArea}
size="md"
rows={6}
required={true}
/>
<p className={styles.configHelpText}>
{t('Dieser Prompt wird für Analyse und Antwort-Generierung verwendet (erforderlich).')}{' '}
{t('Platzhalter')}: {'{userPrompt}'}, {'{context}'}, {'{db_results_part}'}, {'{web_results_part}'}
</p>
</div>
</div>
);
};