2026-05-20 06:12:54 +02:00
|
|
|
@page "/assistant"
|
2026-05-21 11:04:42 +02:00
|
|
|
@attribute [Authorize]
|
2026-05-20 06:12:54 +02:00
|
|
|
@inject IOllamaService OllamaService
|
|
|
|
|
@inject ISettingsService SettingsService
|
|
|
|
|
@inject NavigationManager Navigation
|
2026-05-25 06:38:16 +02:00
|
|
|
@inject ILocalizationService Loc
|
2026-05-20 06:12:54 +02:00
|
|
|
|
|
|
|
|
@rendermode InteractiveServer
|
|
|
|
|
|
2026-05-25 06:38:16 +02:00
|
|
|
<PageTitle>@Loc["page.title.assistant"]</PageTitle>
|
2026-05-20 06:12:54 +02:00
|
|
|
|
|
|
|
|
<div class="assistant-page">
|
|
|
|
|
<div class="assistant-toolbar">
|
|
|
|
|
<select @bind="selectedMode">
|
2026-05-25 06:38:16 +02:00
|
|
|
<option value="lektorat">@Loc["assistant.mode.lektorat"]</option>
|
|
|
|
|
<option value="advocatus">@Loc["assistant.mode.advocatus"]</option>
|
|
|
|
|
<option value="hookfinder">@Loc["assistant.mode.hookfinder"]</option>
|
|
|
|
|
<option value="eli5">@Loc["assistant.mode.eli5"]</option>
|
2026-05-20 06:12:54 +02:00
|
|
|
</select>
|
2026-05-25 06:38:16 +02:00
|
|
|
<button @onclick="RunAssistant" disabled="@(() => isRunning || string.IsNullOrWhiteSpace(inputText))">@Loc["assistant.button.execute"]</button>
|
2026-05-20 07:27:41 +02:00
|
|
|
@if (!string.IsNullOrEmpty(outputText))
|
|
|
|
|
{
|
2026-05-25 06:38:16 +02:00
|
|
|
<button @onclick="SendToEditor" class="primary">@Loc["assistant.button.send_to_editor"]</button>
|
2026-05-20 07:27:41 +02:00
|
|
|
}
|
2026-05-20 06:12:54 +02:00
|
|
|
<span style="flex:1;font-size:12px;color:var(--border-color);text-align:right;">@statusText</span>
|
|
|
|
|
@if (isRunning)
|
|
|
|
|
{
|
|
|
|
|
<progress value="@progress" max="100"></progress>
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="assistant-panels">
|
|
|
|
|
<div class="assistant-input-panel">
|
2026-05-25 06:38:16 +02:00
|
|
|
<h4>@Loc["assistant.label.input_text"]</h4>
|
|
|
|
|
<textarea class="assistant-textarea" @bind="inputText" placeholder="@Loc["assistant.placeholder.input"]"></textarea>
|
2026-05-20 06:12:54 +02:00
|
|
|
</div>
|
|
|
|
|
<div class="assistant-output-panel">
|
2026-05-25 06:38:16 +02:00
|
|
|
<h4>@Loc["assistant.label.result"]</h4>
|
2026-05-20 06:12:54 +02:00
|
|
|
<div class="assistant-output">
|
|
|
|
|
@if (!string.IsNullOrEmpty(outputText))
|
|
|
|
|
{
|
|
|
|
|
<pre>@outputText</pre>
|
|
|
|
|
}
|
|
|
|
|
else if (!string.IsNullOrEmpty(errorMessage))
|
|
|
|
|
{
|
|
|
|
|
<div class="assistant-error">@errorMessage</div>
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-05-25 06:38:16 +02:00
|
|
|
<span class="assistant-hint">@Loc["assistant.hint.empty_result"]</span>
|
2026-05-20 06:12:54 +02:00
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@code {
|
2026-05-20 07:27:41 +02:00
|
|
|
private string selectedMode = "lektorat";
|
2026-05-20 06:12:54 +02:00
|
|
|
private string inputText = string.Empty;
|
|
|
|
|
private string outputText = string.Empty;
|
|
|
|
|
private string errorMessage = string.Empty;
|
2026-05-20 07:27:41 +02:00
|
|
|
private string statusText = string.Empty;
|
2026-05-20 06:12:54 +02:00
|
|
|
private bool isRunning;
|
|
|
|
|
private double progress;
|
|
|
|
|
|
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
|
|
|
{
|
2026-05-25 06:38:16 +02:00
|
|
|
statusText = Loc["common.status.ready"];
|
2026-05-20 07:27:41 +02:00
|
|
|
|
|
|
|
|
var pending = SettingsService.PendingAssistantInput;
|
|
|
|
|
if (!string.IsNullOrEmpty(pending))
|
|
|
|
|
{
|
|
|
|
|
inputText = pending;
|
|
|
|
|
SettingsService.PendingAssistantInput = null;
|
|
|
|
|
}
|
2026-05-20 06:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task RunAssistant()
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(inputText)) return;
|
|
|
|
|
|
|
|
|
|
isRunning = true;
|
|
|
|
|
progress = 50;
|
|
|
|
|
outputText = string.Empty;
|
|
|
|
|
errorMessage = string.Empty;
|
2026-05-25 06:38:16 +02:00
|
|
|
statusText = Loc["assistant.status.processing"];
|
2026-05-20 06:12:54 +02:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var systemPrompt = selectedMode switch
|
|
|
|
|
{
|
2026-05-20 07:27:41 +02:00
|
|
|
"lektorat" => await SettingsService.GetLektorSystemPromptAsync() ?? string.Empty,
|
2026-05-20 06:12:54 +02:00
|
|
|
"advocatus" => await SettingsService.GetAdvocatusDiaboliPromptAsync() ?? string.Empty,
|
|
|
|
|
"hookfinder" => await SettingsService.GetHookFinderPromptAsync() ?? string.Empty,
|
|
|
|
|
"eli5" => await SettingsService.GetEli5PromptAsync() ?? string.Empty,
|
|
|
|
|
_ => string.Empty
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(systemPrompt))
|
|
|
|
|
{
|
2026-05-25 06:38:16 +02:00
|
|
|
errorMessage = Loc["assistant.error.no_prompt"];
|
2026-05-20 06:12:54 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var model = await SettingsService.GetStyleModelAsync() ?? "llama3.2";
|
|
|
|
|
outputText = await OllamaService.Chat(model, inputText, systemPrompt);
|
2026-05-25 06:38:16 +02:00
|
|
|
statusText = Loc["assistant.status.done"];
|
2026-05-20 06:12:54 +02:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2026-05-25 06:38:16 +02:00
|
|
|
errorMessage = $"{Loc["common.error"]} {ex.Message}";
|
|
|
|
|
statusText = Loc["common.status.error"];
|
2026-05-20 06:12:54 +02:00
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
isRunning = false;
|
|
|
|
|
progress = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-20 07:27:41 +02:00
|
|
|
|
|
|
|
|
private void SendToEditor()
|
|
|
|
|
{
|
|
|
|
|
SettingsService.PendingAssistantOutput = outputText;
|
|
|
|
|
Navigation.NavigateTo("/editor");
|
|
|
|
|
}
|
2026-05-20 06:12:54 +02:00
|
|
|
}
|