PlotDirector/PlotLine/Views/Admin/StoryIntelligenceRunDetails.cshtml
2026-07-04 21:04:40 +01:00

184 lines
7.6 KiB
Plaintext

@model StoryIntelligenceSavedRunDetailViewModel
@{
ViewData["Title"] = "Story Intelligence Run";
var run = Model.Run;
}
<div class="page-heading compact">
<div>
<p class="eyebrow">Admin utility</p>
<h1>Story Intelligence Run @(run is null ? string.Empty : run.StoryIntelligenceRunID.ToString("N0"))</h1>
<p class="lead-text">Saved audit output from the admin chapter-to-scene dry-run pipeline.</p>
</div>
</div>
<nav class="mb-3" aria-label="Breadcrumb">
<a asp-controller="Projects" asp-action="Index">Projects</a>
<span class="muted"> / </span>
<a asp-action="Index">Admin</a>
<span class="muted"> / </span>
<a asp-action="StoryIntelligenceRuns">Saved Story Intelligence Runs</a>
<span class="muted"> / Run @(run is null ? string.Empty : run.StoryIntelligenceRunID.ToString("N0"))</span>
</nav>
@if (TempData["AdminMessage"] is string message)
{
<div class="alert alert-success">@message</div>
}
@if (run is null)
{
<section class="edit-panel">
<p class="mb-0">This Story Intelligence run could not be found.</p>
</section>
}
else
{
<section class="edit-panel">
<h2>Run metadata</h2>
<div class="row g-3">
<div class="col-md-3">
<div class="border rounded p-3 h-100">
<p class="eyebrow">Status</p>
<h3 class="mb-0">@run.Status</h3>
</div>
</div>
<div class="col-md-3">
<div class="border rounded p-3 h-100">
<p class="eyebrow">Scenes</p>
<h3 class="mb-0">@Model.SceneResults.Count.ToString("N0")</h3>
</div>
</div>
<div class="col-md-3">
<div class="border rounded p-3 h-100">
<p class="eyebrow">Tokens</p>
<h3 class="mb-0">@Display(run.TotalTokens)</h3>
</div>
</div>
<div class="col-md-3">
<div class="border rounded p-3 h-100">
<p class="eyebrow">Duration</p>
<h3 class="mb-0">@DisplayDuration(run.TotalDurationMs)</h3>
</div>
</div>
</div>
<dl class="mt-3 mb-0">
<dt>Source type</dt>
<dd>@run.SourceType</dd>
<dt>Project</dt>
<dd>@DisplayName(run.ProjectTitle, run.ProjectID, "Project")</dd>
<dt>Book</dt>
<dd>@DisplayName(run.BookTitle, run.BookID, "Book")</dd>
<dt>Prompt version</dt>
<dd>@run.PromptVersion</dd>
<dt>Model</dt>
<dd>@run.Model</dd>
<dt>Started</dt>
<dd>@run.StartedUtc.ToString("yyyy-MM-dd HH:mm:ss") UTC</dd>
<dt>Completed</dt>
<dd>@(run.CompletedUtc?.ToString("yyyy-MM-dd HH:mm:ss") ?? "None") UTC</dd>
<dt>Input tokens</dt>
<dd>@Display(run.TotalInputTokens)</dd>
<dt>Output tokens</dt>
<dd>@Display(run.TotalOutputTokens)</dd>
<dt>Error message</dt>
<dd>@Display(run.ErrorMessage)</dd>
</dl>
</section>
<section class="edit-panel">
<h2>Chapter result</h2>
@if (Model.ChapterResult is null)
{
<p class="mb-0">No chapter result was saved for this run.</p>
}
else
{
var chapter = Model.ChapterResult;
<dl>
<dt>Source label</dt>
<dd>@chapter.SourceLabel</dd>
<dt>Chapter</dt>
<dd>@Display(chapter.ChapterNumber)</dd>
<dt>Prompt version</dt>
<dd>@chapter.PromptVersion</dd>
<dt>Validation</dt>
<dd>@chapter.ValidationErrorsCount.ToString("N0") error(s), @chapter.ValidationWarningsCount.ToString("N0") warning(s)</dd>
<dt>Duration</dt>
<dd>@DisplayDuration(chapter.DurationMs)</dd>
<dt>Tokens</dt>
<dd>@Display(chapter.TotalTokens) total (@Display(chapter.InputTokens) input, @Display(chapter.OutputTokens) output)</dd>
</dl>
<label class="form-label" for="chapterParsedJson">Parsed JSON</label>
<textarea class="form-control font-monospace" id="chapterParsedJson" rows="10" readonly>@chapter.ParsedJson</textarea>
<label class="form-label mt-3" for="chapterOutputJson">Extracted output JSON</label>
<textarea class="form-control font-monospace" id="chapterOutputJson" rows="10" readonly>@chapter.OutputTextJson</textarea>
<label class="form-label mt-3" for="chapterRawJson">Raw OpenAI response JSON</label>
<textarea class="form-control font-monospace" id="chapterRawJson" rows="10" readonly>@chapter.RawResponseJson</textarea>
}
</section>
<section class="edit-panel">
<h2>Scene results</h2>
@if (Model.SceneResults.Count == 0)
{
<p class="mb-0">No scene results were saved for this run.</p>
}
</section>
@foreach (var scene in Model.SceneResults)
{
<section class="edit-panel">
<h2>Suggested Scene @scene.TemporarySceneNumber.ToString("N0")</h2>
<dl>
<dt>Paragraph range</dt>
<dd>@Display(scene.StartParagraph)-@Display(scene.EndParagraph)</dd>
<dt>Source label</dt>
<dd>@scene.SourceLabel</dd>
<dt>Prompt version</dt>
<dd>@scene.PromptVersion</dd>
<dt>Validation</dt>
<dd>@scene.ValidationErrorsCount.ToString("N0") error(s), @scene.ValidationWarningsCount.ToString("N0") warning(s)</dd>
<dt>Duration</dt>
<dd>@DisplayDuration(scene.DurationMs)</dd>
<dt>Tokens</dt>
<dd>@Display(scene.TotalTokens) total (@Display(scene.InputTokens) input, @Display(scene.OutputTokens) output)</dd>
</dl>
<label class="form-label" for="sceneParsedJson-@scene.SceneResultID">Parsed JSON</label>
<textarea class="form-control font-monospace" id="sceneParsedJson-@scene.SceneResultID" rows="10" readonly>@scene.ParsedJson</textarea>
<label class="form-label mt-3" for="sceneOutputJson-@scene.SceneResultID">Extracted output JSON</label>
<textarea class="form-control font-monospace" id="sceneOutputJson-@scene.SceneResultID" rows="10" readonly>@scene.OutputTextJson</textarea>
<label class="form-label mt-3" for="sceneRawJson-@scene.SceneResultID">Raw OpenAI response JSON</label>
<textarea class="form-control font-monospace" id="sceneRawJson-@scene.SceneResultID" rows="10" readonly>@scene.RawResponseJson</textarea>
</section>
}
}
@functions {
private static string Display(object? value) => value switch
{
null => "None",
string text when string.IsNullOrWhiteSpace(text) => "None",
decimal number => number.ToString("0.##"),
int number => number.ToString("N0"),
_ => value.ToString() ?? "None"
};
private static string DisplayDuration(long? milliseconds)
=> milliseconds.HasValue
? TimeSpan.FromMilliseconds(milliseconds.Value).TotalSeconds < 1
? $"{milliseconds.Value:N0} ms"
: $"{TimeSpan.FromMilliseconds(milliseconds.Value).TotalSeconds:0.00} sec"
: "None";
private static string DisplayName(string? title, int? id, string label)
=> !string.IsNullOrWhiteSpace(title)
? title
: id.HasValue
? $"{label} {id.Value:N0}"
: "None";
}