210 lines
6.8 KiB
Text
210 lines
6.8 KiB
Text
@page "/chat"
|
|
@attribute [Authorize(Roles = "Administrator,Chat")]
|
|
@inject IChatService ChatService
|
|
@inject IOllamaService OllamaService
|
|
@inject OllamaSettings Settings
|
|
@inject ISettingsService SettingsService
|
|
@inject IAuthService AuthService
|
|
@inject NavigationManager Navigation
|
|
@inject IJSRuntime JS
|
|
|
|
@rendermode InteractiveServer
|
|
|
|
<PageTitle>Chat</PageTitle>
|
|
|
|
<div class="chat-page">
|
|
<div class="chat-header">
|
|
<h3>Chat</h3>
|
|
<div class="chat-header-actions">
|
|
<button class="btn btn-sm chat-settings-toggle" @onclick="ToggleSettings">
|
|
@(_showSettings ? "Einstellungen ausblenden" : "Einstellungen")
|
|
</button>
|
|
<button class="btn btn-sm" @onclick="ClearHistory" disabled="@(_messages.Count == 0)">Verlauf löschen</button>
|
|
</div>
|
|
</div>
|
|
|
|
@if (_showSettings)
|
|
{
|
|
<div class="chat-settings">
|
|
<div class="form-group">
|
|
<label>System-Prompt</label>
|
|
<textarea class="chat-settings-textarea" @bind="_systemPrompt" rows="3"></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="chatTemp">Temperatur: @_temperature.ToString("F2")</label>
|
|
<input id="chatTemp" type="range" min="0" max="2" step="0.05" @bind="_temperature" @bind:event="oninput" />
|
|
<div class="qm-range-labels">
|
|
<span>präzise (0)</span>
|
|
<span>kreativ (2)</span>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<button class="btn btn-sm btn-primary" @onclick="SaveSettings">System-Prompt & Temperatur speichern</button>
|
|
<button class="btn btn-sm" @onclick="ResetToDefault">Auf Standard zurücksetzen</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<div class="chat-messages" id="chat-messages">
|
|
@if (_messages.Count == 0)
|
|
{
|
|
<div class="chat-empty">Noch keine Nachrichten. Stelle eine Frage.</div>
|
|
}
|
|
@foreach (var msg in _messages)
|
|
{
|
|
<div class="chat-message chat-user">
|
|
<div class="chat-avatar">U</div>
|
|
<div class="chat-bubble">@msg.UserMessage</div>
|
|
</div>
|
|
<div class="chat-message chat-assistant">
|
|
<div class="chat-avatar">A</div>
|
|
<div class="chat-bubble">@MarkdownToHtml(msg.AssistantResponse)</div>
|
|
</div>
|
|
}
|
|
@if (_isBusy)
|
|
{
|
|
<div class="chat-message chat-assistant">
|
|
<div class="chat-avatar">A</div>
|
|
<div class="chat-bubble chat-thinking">…</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<div class="chat-input">
|
|
<textarea class="chat-textarea" @bind="_input" @bind:event="oninput" rows="3"
|
|
placeholder="Nachricht eingeben…"
|
|
@onkeydown="HandleKeyDown"
|
|
disabled="@_isBusy"></textarea>
|
|
<button class="btn btn-primary" @onclick="Send" disabled="@_sendDisabled">
|
|
Senden
|
|
</button>
|
|
</div>
|
|
|
|
@if (!string.IsNullOrEmpty(_error))
|
|
{
|
|
<div class="auth-error">@_error</div>
|
|
}
|
|
</div>
|
|
|
|
@code {
|
|
private List<ChatEntity> _messages = new();
|
|
private string _input = string.Empty;
|
|
private string _error = string.Empty;
|
|
private bool _isBusy;
|
|
private int _userId;
|
|
private bool _showSettings;
|
|
private bool _sendDisabled => _isBusy || string.IsNullOrWhiteSpace(_input);
|
|
private string _systemPrompt = string.Empty;
|
|
private double _temperature = 0.7;
|
|
private bool _needsScroll;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
var user = await AuthService.GetCurrentUserAsync();
|
|
if (user == null || (!user.HasRole(UserRole.Administrator) && !user.HasRole(UserRole.Chat)))
|
|
{
|
|
Navigation.NavigateTo("/");
|
|
return;
|
|
}
|
|
_userId = user.Id;
|
|
_messages = await ChatService.GetHistoryAsync(_userId);
|
|
_systemPrompt = await SettingsService.GetChatPromptAsync()
|
|
?? Settings.GetChatDefaultPrompt(Settings.Language);
|
|
_temperature = await SettingsService.GetChatTempAsync();
|
|
_needsScroll = true;
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (_needsScroll)
|
|
{
|
|
_needsScroll = false;
|
|
await ScrollToBottom();
|
|
}
|
|
}
|
|
|
|
private void ToggleSettings()
|
|
{
|
|
_showSettings = !_showSettings;
|
|
}
|
|
|
|
private async Task SaveSettings()
|
|
{
|
|
await SettingsService.SaveChatPromptAsync(_systemPrompt);
|
|
await SettingsService.SaveChatTempAsync(_temperature);
|
|
}
|
|
|
|
private async Task ResetToDefault()
|
|
{
|
|
_systemPrompt = Settings.GetChatDefaultPrompt(Settings.Language);
|
|
_temperature = Settings.ChatTemp;
|
|
await SaveSettings();
|
|
}
|
|
|
|
private async Task HandleKeyDown(KeyboardEventArgs e)
|
|
{
|
|
if (e.Key == "Enter" && !e.ShiftKey)
|
|
{
|
|
await Send();
|
|
}
|
|
}
|
|
|
|
private async Task Send()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_input) || _isBusy) return;
|
|
|
|
var text = _input.Trim();
|
|
_input = string.Empty;
|
|
_error = string.Empty;
|
|
_isBusy = true;
|
|
_needsScroll = true;
|
|
|
|
try
|
|
{
|
|
var response = await OllamaService.Chat(Settings.StyleModel, text, _systemPrompt, _temperature);
|
|
|
|
var msg = new ChatEntity
|
|
{
|
|
UserId = _userId,
|
|
UserMessage = text,
|
|
AssistantResponse = response,
|
|
Model = Settings.StyleModel,
|
|
Timestamp = DateTime.UtcNow
|
|
};
|
|
await ChatService.AddMessageAsync(msg);
|
|
_messages.Add(msg);
|
|
_needsScroll = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_error = $"Fehler: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
_isBusy = false;
|
|
_input = string.Empty;
|
|
_needsScroll = true;
|
|
}
|
|
}
|
|
|
|
private async Task ClearHistory()
|
|
{
|
|
await ChatService.ClearHistoryAsync(_userId);
|
|
_messages.Clear();
|
|
}
|
|
|
|
private async Task ScrollToBottom()
|
|
{
|
|
await JS.InvokeVoidAsync("eval", "document.getElementById('chat-messages').scrollTop = document.getElementById('chat-messages').scrollHeight");
|
|
}
|
|
|
|
private MarkupString MarkdownToHtml(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) return new MarkupString(string.Empty);
|
|
var html = System.Web.HttpUtility.HtmlEncode(text);
|
|
html = System.Text.RegularExpressions.Regex.Replace(html, @"\*\*(.+?)\*\*", "<strong>$1</strong>");
|
|
html = System.Text.RegularExpressions.Regex.Replace(html, @"\*(.+?)\*", "<em>$1</em>");
|
|
html = html.Replace("\n", "<br />");
|
|
return new MarkupString(html);
|
|
}
|
|
}
|