@model ChapterStructureDryRunViewModel @{ ViewData["Title"] = "Chapter Intelligence Test"; var result = Model.Result; }

Admin utility

Chapter Intelligence Test

This is a structural dry run. It makes one live OpenAI request only when submitted, and it does not create scenes or save data.

Manual chapter structure test

Use fictional sample text only. This page loads Chapter-Structure-Prompt-V1.md, replaces runtime placeholders, asks for likely scene boundaries, and displays the raw response without saving it.

Back to diagnostics
@if (result is not null) {

Chapter structure result

@if (!result.Success) {
@result.ErrorMessage
}

Prompt version

@result.PromptVersion

Model

@result.Model

Duration

@(string.IsNullOrWhiteSpace(result.Duration) ? "Not run" : result.Duration)

Paragraphs

@result.ParagraphCount.ToString("N0")

Input tokens
@(result.InputTokens.HasValue ? result.InputTokens.Value.ToString("N0") : "Not reported")
Output tokens
@(result.OutputTokens.HasValue ? result.OutputTokens.Value.ToString("N0") : "Not reported")
Retries
@result.RetryCount.ToString("N0")
@if (result.Validation is null) {
Validation did not run.
} else {

@ValidationSummary(result.Validation)

@RenderIssues("Errors", result.Validation.Errors) @RenderIssues("Warnings", result.Validation.Warnings) @RenderIssues("Information", result.Validation.Information) }
@if (result.ParsedChapter is null) {
No parsed Chapter Structure model is available.
} else {

Chapter

Schema
@Display(result.ParsedChapter.SchemaVersion)
Summary
@Display(result.ParsedChapter.ChapterSummary)

Scene Boundaries

@if (result.ParsedChapter.SceneBoundaries is null || result.ParsedChapter.SceneBoundaries.Count == 0) {

No scene boundaries reported.

} else {
    @foreach (var boundary in result.ParsedChapter.SceneBoundaries) {
  1. Scene @Display(boundary.SceneNumber): paragraphs @Display(boundary.StartParagraph)-@Display(boundary.EndParagraph), confidence @Display(boundary.Confidence). @Display(boundary.Reason)
  2. }
}
}
} @functions { private static string Display(object? value) => value switch { null => "None", string text when string.IsNullOrWhiteSpace(text) => "None", decimal number => number.ToString("0.##"), bool flag => flag ? "Yes" : "No", _ => value.ToString() ?? "None" }; private static string ValidationSummary(PlotLine.Models.StoryIntelligence.ValidationResult validation) => validation.IsValid ? $"✓ Passed with {validation.Warnings.Count:N0} warning(s)" : $"✖ Failed with {validation.Errors.Count:N0} error(s) and {validation.Warnings.Count:N0} warning(s)"; private static string SeveritySymbol(string severity) => severity switch { "Error" => "✖", "Warning" => "⚠", _ => "✓" }; private static Microsoft.AspNetCore.Html.IHtmlContent RenderIssues(string heading, IReadOnlyList issues) { var builder = new System.Text.StringBuilder(); builder.Append("
"); builder.Append("

").Append(Encode(heading)).Append(" (").Append(issues.Count.ToString("N0")).Append(")

"); if (issues.Count == 0) { builder.Append("

None.

"); } else { builder.Append("
"); foreach (var issue in issues) { builder.Append("
"); builder.Append("

") .Append(Encode(SeveritySymbol(issue.Severity))) .Append(' ') .Append(Encode(issue.Severity)) .Append(" ") .Append(Encode(issue.Path)) .Append("

"); builder.Append("

").Append(Encode(issue.Message)).Append("

"); builder.Append("

").Append(Encode(issue.SuggestedFix)).Append("

"); builder.Append("
"); } builder.Append("
"); } builder.Append("
"); return new Microsoft.AspNetCore.Html.HtmlString(builder.ToString()); } private static string Encode(string? value) => System.Net.WebUtility.HtmlEncode(value ?? string.Empty); }