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

Admin utility

Chapter Story Intelligence Test

This in-memory dry run detects suggested scene boundaries and then runs Scene Intelligence sequentially for each suggested scene. It does not save data.

Manual combined pipeline test

Use fictional sample text only. This page runs Chapter Structure first, splits the submitted chapter into in-memory scene blocks, then runs Scene Intelligence for each block without creating PlotDirector scenes.

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

Combined pipeline result

@if (!string.IsNullOrWhiteSpace(result.ErrorMessage)) {
@result.ErrorMessage
} @if (result.PipelineStopped) {
@result.StopReason
}

Model

@Display(result.Model)

Paragraphs

@result.ParagraphCount.ToString("N0")

Detected scenes

@result.SceneBlocks.Count.ToString("N0")

Total duration

@Display(result.TotalDuration)

Chapter prompt
@result.ChapterPromptVersion
Scene prompt
@result.ScenePromptVersion
Total input tokens
@Display(result.TotalInputTokens)
Total output tokens
@Display(result.TotalOutputTokens)

Chapter Structure

Duration

@Display(result.ChapterDuration)

Input tokens

@Display(result.ChapterInputTokens)

Output tokens

@Display(result.ChapterOutputTokens)

Validation

@if (result.ChapterValidation is null) {
Chapter Structure validation did not run.
} else {

@ValidationSummary(result.ChapterValidation)

@RenderIssues("Errors", result.ChapterValidation.Errors) @RenderIssues("Warnings", result.ChapterValidation.Warnings) @RenderIssues("Information", result.ChapterValidation.Information) }

Parsed Model

@if (result.ParsedChapter is null) {

No parsed Chapter Structure model is available.

} else {

@Display(result.ParsedChapter.ChapterSummary)

@if (result.ParsedChapter.SceneBoundaries is not null) {
    @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. }
} }

Raw JSON

Detected Scene List

@if (result.SceneBlocks.Count == 0) {

No in-memory scene blocks were produced.

} else {
    @foreach (var block in result.SceneBlocks) {
  1. Scene @Display(block.TemporarySceneNumber), paragraphs @Display(block.StartParagraph)-@Display(block.EndParagraph): @Display(block.SceneTextPreview)
  2. }
}
@foreach (var block in result.SceneBlocks) {

Suggested Scene @Display(block.TemporarySceneNumber)

Paragraph range
@Display(block.StartParagraph)-@Display(block.EndParagraph)
Boundary confidence
@Display(block.BoundaryConfidence)
Boundary reason
@Display(block.BoundaryReason)
Scene Intelligence duration
@Display(block.Duration)
Input tokens
@Display(block.InputTokens)
Output tokens
@Display(block.OutputTokens)
@if (!block.SplitValid) {
@block.SplitErrorMessage
} @if (!string.IsNullOrWhiteSpace(block.ErrorMessage)) {
@block.ErrorMessage
}

Scene Text Preview

@Display(block.SceneTextPreview)

Validation

@if (block.Validation is null) {

Scene validation did not run.

} else {

@ValidationSummary(block.Validation)

@RenderIssues("Errors", block.Validation.Errors) @RenderIssues("Warnings", block.Validation.Warnings) }

Parsed Model

@if (block.ParsedScene is null) {

No parsed Scene Intelligence model is available.

} else {
Summary
@Display(block.ParsedScene.Summary?.Short)
Setting
@Display(block.ParsedScene.Setting?.LocationName) (@Display(block.ParsedScene.Setting?.LocationType))
Characters
@Display(block.ParsedScene.Characters?.Count)
Locations
@Display(block.ParsedScene.Locations?.Count)
Assets
@Display(block.ParsedScene.Assets?.Count)
Observations
@Display(block.ParsedScene.Observations?.Count)
}

Raw Scene Intelligence Response

} } @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", int number => number.ToString("N0"), _ => 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); }