Phase 20O.1 Extend the Story Intelligence persistence/audit design so every import attempt is fully traceable.
This commit is contained in:
parent
35e76d8ac4
commit
650febab70
@ -32,15 +32,29 @@ public sealed class StoryIntelligenceResultRepository(ISqlConnectionFactory conn
|
||||
request.BookID,
|
||||
request.Status,
|
||||
request.SourceType,
|
||||
request.SourceFileName,
|
||||
request.SourceFileSizeBytes,
|
||||
request.SourceWordCount,
|
||||
request.SourceCharacterCount,
|
||||
request.SourceChapterCount,
|
||||
request.SourceDetectedImagesCount,
|
||||
request.SourceDetectedTablesCount,
|
||||
request.SourceDetectedFootnotesCount,
|
||||
request.SourceDetectedCommentsCount,
|
||||
request.PromptVersion,
|
||||
request.Model,
|
||||
request.StartedUtc,
|
||||
request.CompletedUtc,
|
||||
request.FailureStage,
|
||||
request.TotalInputTokens,
|
||||
request.TotalOutputTokens,
|
||||
request.TotalTokens,
|
||||
request.TotalDurationMs,
|
||||
request.ErrorMessage
|
||||
request.EstimatedCostGBP,
|
||||
request.EstimatedCostUSD,
|
||||
request.PromptVersionsSummary,
|
||||
request.ErrorMessage,
|
||||
request.ErrorDetail
|
||||
},
|
||||
transaction,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
@ -2,9 +2,23 @@ namespace PlotLine.Models;
|
||||
|
||||
public static class StoryIntelligenceRunStatuses
|
||||
{
|
||||
public const string Pending = "Pending";
|
||||
public const string Running = "Running";
|
||||
public const string Completed = "Completed";
|
||||
public const string CompletedWithIssues = "CompletedWithIssues";
|
||||
public const string CompletedWithWarnings = "CompletedWithWarnings";
|
||||
public const string Failed = "Failed";
|
||||
public const string Cancelled = "Cancelled";
|
||||
}
|
||||
|
||||
public static class StoryIntelligenceFailureStages
|
||||
{
|
||||
public const string DocumentRead = "DocumentRead";
|
||||
public const string ChapterStructure = "ChapterStructure";
|
||||
public const string SceneSplit = "SceneSplit";
|
||||
public const string SceneIntelligence = "SceneIntelligence";
|
||||
public const string Validation = "Validation";
|
||||
public const string Persistence = "Persistence";
|
||||
public const string DomainImport = "DomainImport";
|
||||
}
|
||||
|
||||
public sealed class StoryIntelligenceRunSaveRequest
|
||||
@ -14,15 +28,29 @@ public sealed class StoryIntelligenceRunSaveRequest
|
||||
public int? BookID { get; init; }
|
||||
public string Status { get; init; } = StoryIntelligenceRunStatuses.Completed;
|
||||
public string SourceType { get; init; } = "AdminDryRun";
|
||||
public string? SourceFileName { get; init; }
|
||||
public long? SourceFileSizeBytes { get; init; }
|
||||
public int? SourceWordCount { get; init; }
|
||||
public int? SourceCharacterCount { get; init; }
|
||||
public int? SourceChapterCount { get; init; }
|
||||
public int? SourceDetectedImagesCount { get; init; }
|
||||
public int? SourceDetectedTablesCount { get; init; }
|
||||
public int? SourceDetectedFootnotesCount { get; init; }
|
||||
public int? SourceDetectedCommentsCount { get; init; }
|
||||
public string PromptVersion { get; init; } = string.Empty;
|
||||
public string Model { get; init; } = string.Empty;
|
||||
public DateTime StartedUtc { get; init; }
|
||||
public DateTime? CompletedUtc { get; init; }
|
||||
public string? FailureStage { get; init; }
|
||||
public int? TotalInputTokens { get; init; }
|
||||
public int? TotalOutputTokens { get; init; }
|
||||
public int? TotalTokens { get; init; }
|
||||
public long? TotalDurationMs { get; init; }
|
||||
public decimal? EstimatedCostGBP { get; init; }
|
||||
public decimal? EstimatedCostUSD { get; init; }
|
||||
public string? PromptVersionsSummary { get; init; }
|
||||
public string? ErrorMessage { get; init; }
|
||||
public string? ErrorDetail { get; init; }
|
||||
public StoryIntelligenceChapterResultSaveRequest? ChapterResult { get; init; }
|
||||
public IReadOnlyList<StoryIntelligenceSceneResultSaveRequest> SceneResults { get; init; } = [];
|
||||
}
|
||||
@ -80,9 +108,13 @@ public sealed class StoryIntelligenceSavedRunListItem
|
||||
public int? BookID { get; init; }
|
||||
public string? BookTitle { get; init; }
|
||||
public string Status { get; init; } = string.Empty;
|
||||
public string SourceType { get; init; } = string.Empty;
|
||||
public string? FailureStage { get; init; }
|
||||
public int SceneCount { get; init; }
|
||||
public int? TotalTokens { get; init; }
|
||||
public long? TotalDurationMs { get; init; }
|
||||
public decimal? EstimatedCostGBP { get; init; }
|
||||
public decimal? EstimatedCostUSD { get; init; }
|
||||
public int ValidationErrorsCount { get; init; }
|
||||
public int ValidationWarningsCount { get; init; }
|
||||
}
|
||||
@ -97,15 +129,29 @@ public sealed class StoryIntelligenceSavedRun
|
||||
public string? BookTitle { get; init; }
|
||||
public string Status { get; init; } = string.Empty;
|
||||
public string SourceType { get; init; } = string.Empty;
|
||||
public string? SourceFileName { get; init; }
|
||||
public long? SourceFileSizeBytes { get; init; }
|
||||
public int? SourceWordCount { get; init; }
|
||||
public int? SourceCharacterCount { get; init; }
|
||||
public int? SourceChapterCount { get; init; }
|
||||
public int? SourceDetectedImagesCount { get; init; }
|
||||
public int? SourceDetectedTablesCount { get; init; }
|
||||
public int? SourceDetectedFootnotesCount { get; init; }
|
||||
public int? SourceDetectedCommentsCount { get; init; }
|
||||
public string PromptVersion { get; init; } = string.Empty;
|
||||
public string? PromptVersionsSummary { get; init; }
|
||||
public string Model { get; init; } = string.Empty;
|
||||
public DateTime StartedUtc { get; init; }
|
||||
public DateTime? CompletedUtc { get; init; }
|
||||
public string? FailureStage { get; init; }
|
||||
public int? TotalInputTokens { get; init; }
|
||||
public int? TotalOutputTokens { get; init; }
|
||||
public int? TotalTokens { get; init; }
|
||||
public long? TotalDurationMs { get; init; }
|
||||
public decimal? EstimatedCostGBP { get; init; }
|
||||
public decimal? EstimatedCostUSD { get; init; }
|
||||
public string? ErrorMessage { get; init; }
|
||||
public string? ErrorDetail { get; init; }
|
||||
public DateTime CreatedUtc { get; init; }
|
||||
public DateTime UpdatedUtc { get; init; }
|
||||
}
|
||||
|
||||
@ -86,11 +86,13 @@ public sealed class StoryIntelligenceResultPersistenceService(
|
||||
: completedUtc;
|
||||
var errorCount = CountErrors(result);
|
||||
var warningCount = CountWarnings(result);
|
||||
var status = !string.IsNullOrWhiteSpace(result.ErrorMessage) || result.PipelineStopped
|
||||
var sceneFailed = result.SceneBlocks.Any(block => !block.Success && block.SplitValid);
|
||||
var status = !string.IsNullOrWhiteSpace(result.ErrorMessage) || result.PipelineStopped || errorCount > 0 || sceneFailed
|
||||
? StoryIntelligenceRunStatuses.Failed
|
||||
: errorCount > 0 || warningCount > 0 || result.SceneBlocks.Any(block => !block.Success && block.SplitValid)
|
||||
? StoryIntelligenceRunStatuses.CompletedWithIssues
|
||||
: warningCount > 0
|
||||
? StoryIntelligenceRunStatuses.CompletedWithWarnings
|
||||
: StoryIntelligenceRunStatuses.Completed;
|
||||
var promptVersionsSummary = $"Chapter={result.ChapterPromptVersion}; Scene={result.ScenePromptVersion}";
|
||||
|
||||
return new StoryIntelligenceRunSaveRequest
|
||||
{
|
||||
@ -99,15 +101,21 @@ public sealed class StoryIntelligenceResultPersistenceService(
|
||||
BookID = form.BookID,
|
||||
Status = status,
|
||||
SourceType = "AdminDryRun",
|
||||
PromptVersion = $"Chapter={result.ChapterPromptVersion}; Scene={result.ScenePromptVersion}",
|
||||
SourceWordCount = CountWords(form.ChapterText),
|
||||
SourceCharacterCount = string.IsNullOrEmpty(form.ChapterText) ? null : form.ChapterText.Length,
|
||||
SourceChapterCount = 1,
|
||||
PromptVersion = promptVersionsSummary,
|
||||
PromptVersionsSummary = promptVersionsSummary,
|
||||
Model = result.Model,
|
||||
StartedUtc = startedUtc,
|
||||
CompletedUtc = completedUtc,
|
||||
FailureStage = DetermineFailureStage(result),
|
||||
TotalInputTokens = result.TotalInputTokens,
|
||||
TotalOutputTokens = result.TotalOutputTokens,
|
||||
TotalTokens = SumTokens(result.TotalInputTokens, result.TotalOutputTokens),
|
||||
TotalDurationMs = result.TotalDurationMs,
|
||||
ErrorMessage = FirstError(result),
|
||||
ErrorDetail = BuildErrorDetail(result),
|
||||
ChapterResult = BuildChapterResult(form, result),
|
||||
SceneResults = result.SceneBlocks.Select(block => BuildSceneResult(form, result, block)).ToList()
|
||||
};
|
||||
@ -172,6 +180,11 @@ public sealed class StoryIntelligenceResultPersistenceService(
|
||||
private static int? SumTokens(int? inputTokens, int? outputTokens)
|
||||
=> inputTokens.HasValue || outputTokens.HasValue ? (inputTokens ?? 0) + (outputTokens ?? 0) : null;
|
||||
|
||||
private static int? CountWords(string? value)
|
||||
=> string.IsNullOrWhiteSpace(value)
|
||||
? null
|
||||
: value.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries).Length;
|
||||
|
||||
private static int CountErrors(ChapterStoryIntelligenceDryRunResultViewModel result)
|
||||
=> (result.ChapterValidation?.Errors.Count ?? 0)
|
||||
+ result.SceneBlocks.Sum(block => block.Validation?.Errors.Count ?? 0);
|
||||
@ -184,4 +197,57 @@ public sealed class StoryIntelligenceResultPersistenceService(
|
||||
=> !string.IsNullOrWhiteSpace(result.ErrorMessage)
|
||||
? result.ErrorMessage
|
||||
: result.SceneBlocks.FirstOrDefault(block => !string.IsNullOrWhiteSpace(block.ErrorMessage))?.ErrorMessage;
|
||||
|
||||
private static string? DetermineFailureStage(ChapterStoryIntelligenceDryRunResultViewModel result)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(result.ErrorMessage)
|
||||
&& !result.PipelineStopped
|
||||
&& result.ChapterValidation?.Errors.Count is not > 0
|
||||
&& result.SceneBlocks.All(block => block.Success || !block.SplitValid)
|
||||
&& result.SceneBlocks.All(block => block.Validation?.Errors.Count is not > 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result.ChapterValidation?.Errors.Count is > 0 || result.StopReason.Contains("validation", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return StoryIntelligenceFailureStages.Validation;
|
||||
}
|
||||
|
||||
if (result.SceneBlocks.Any(block => !block.SplitValid))
|
||||
{
|
||||
return StoryIntelligenceFailureStages.SceneSplit;
|
||||
}
|
||||
|
||||
if (result.SceneBlocks.Any(block => !block.Success || !string.IsNullOrWhiteSpace(block.ErrorMessage)))
|
||||
{
|
||||
return StoryIntelligenceFailureStages.SceneIntelligence;
|
||||
}
|
||||
|
||||
return StoryIntelligenceFailureStages.ChapterStructure;
|
||||
}
|
||||
|
||||
private static string? BuildErrorDetail(ChapterStoryIntelligenceDryRunResultViewModel result)
|
||||
{
|
||||
var details = new List<string>();
|
||||
if (!string.IsNullOrWhiteSpace(result.StopReason))
|
||||
{
|
||||
details.Add(result.StopReason);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
|
||||
{
|
||||
details.Add(result.ErrorMessage);
|
||||
}
|
||||
|
||||
details.AddRange(result.ChapterValidation?.Errors.Select(issue => $"{issue.Path}: {issue.Message}") ?? []);
|
||||
details.AddRange(result.SceneBlocks
|
||||
.Where(block => !string.IsNullOrWhiteSpace(block.ErrorMessage))
|
||||
.Select(block => $"Scene {block.TemporarySceneNumber}: {block.ErrorMessage}"));
|
||||
details.AddRange(result.SceneBlocks
|
||||
.Where(block => block.Validation is not null)
|
||||
.SelectMany(block => block.Validation!.Errors.Select(issue => $"Scene {block.TemporarySceneNumber} {issue.Path}: {issue.Message}")));
|
||||
|
||||
return details.Count == 0 ? null : string.Join(Environment.NewLine, details);
|
||||
}
|
||||
}
|
||||
|
||||
187
PlotLine/Sql/116_Phase20J_StoryIntelligenceRunAuditFields.sql
Normal file
187
PlotLine/Sql/116_Phase20J_StoryIntelligenceRunAuditFields.sql
Normal file
@ -0,0 +1,187 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceFileName') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceFileName nvarchar(260) NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceFileSizeBytes') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceFileSizeBytes bigint NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceWordCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceWordCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceCharacterCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceCharacterCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceChapterCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceChapterCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceDetectedImagesCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceDetectedImagesCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceDetectedTablesCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceDetectedTablesCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceDetectedFootnotesCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceDetectedFootnotesCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'SourceDetectedCommentsCount') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD SourceDetectedCommentsCount int NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'FailureStage') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD FailureStage nvarchar(80) NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'ErrorDetail') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD ErrorDetail nvarchar(max) NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'EstimatedCostGBP') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD EstimatedCostGBP decimal(19,6) NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'EstimatedCostUSD') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD EstimatedCostUSD decimal(19,6) NULL;
|
||||
GO
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'PromptVersionsSummary') IS NULL
|
||||
ALTER TABLE dbo.StoryIntelligenceRuns ADD PromptVersionsSummary nvarchar(500) NULL;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_Save
|
||||
@UserID int,
|
||||
@ProjectID int = NULL,
|
||||
@BookID int = NULL,
|
||||
@Status nvarchar(50),
|
||||
@SourceType nvarchar(50),
|
||||
@SourceFileName nvarchar(260) = NULL,
|
||||
@SourceFileSizeBytes bigint = NULL,
|
||||
@SourceWordCount int = NULL,
|
||||
@SourceCharacterCount int = NULL,
|
||||
@SourceChapterCount int = NULL,
|
||||
@SourceDetectedImagesCount int = NULL,
|
||||
@SourceDetectedTablesCount int = NULL,
|
||||
@SourceDetectedFootnotesCount int = NULL,
|
||||
@SourceDetectedCommentsCount int = NULL,
|
||||
@PromptVersion nvarchar(200),
|
||||
@Model nvarchar(100),
|
||||
@StartedUtc datetime2,
|
||||
@CompletedUtc datetime2 = NULL,
|
||||
@FailureStage nvarchar(80) = NULL,
|
||||
@TotalInputTokens int = NULL,
|
||||
@TotalOutputTokens int = NULL,
|
||||
@TotalTokens int = NULL,
|
||||
@TotalDurationMs bigint = NULL,
|
||||
@EstimatedCostGBP decimal(19,6) = NULL,
|
||||
@EstimatedCostUSD decimal(19,6) = NULL,
|
||||
@PromptVersionsSummary nvarchar(500) = NULL,
|
||||
@ErrorMessage nvarchar(max) = NULL,
|
||||
@ErrorDetail nvarchar(max) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
INSERT dbo.StoryIntelligenceRuns
|
||||
(
|
||||
UserID, ProjectID, BookID, Status, SourceType, SourceFileName, SourceFileSizeBytes,
|
||||
SourceWordCount, SourceCharacterCount, SourceChapterCount, SourceDetectedImagesCount,
|
||||
SourceDetectedTablesCount, SourceDetectedFootnotesCount, SourceDetectedCommentsCount,
|
||||
PromptVersion, Model, StartedUtc, CompletedUtc, FailureStage, TotalInputTokens,
|
||||
TotalOutputTokens, TotalTokens, TotalDurationMs, EstimatedCostGBP, EstimatedCostUSD,
|
||||
PromptVersionsSummary, ErrorMessage, ErrorDetail
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@UserID, @ProjectID, @BookID, @Status, @SourceType, @SourceFileName, @SourceFileSizeBytes,
|
||||
@SourceWordCount, @SourceCharacterCount, @SourceChapterCount, @SourceDetectedImagesCount,
|
||||
@SourceDetectedTablesCount, @SourceDetectedFootnotesCount, @SourceDetectedCommentsCount,
|
||||
@PromptVersion, @Model, @StartedUtc, @CompletedUtc, @FailureStage, @TotalInputTokens,
|
||||
@TotalOutputTokens, @TotalTokens, @TotalDurationMs, @EstimatedCostGBP, @EstimatedCostUSD,
|
||||
@PromptVersionsSummary, @ErrorMessage, @ErrorDetail
|
||||
);
|
||||
|
||||
SELECT CAST(SCOPE_IDENTITY() AS int) AS StoryIntelligenceRunID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_ListAdmin
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
r.StoryIntelligenceRunID,
|
||||
r.CreatedUtc,
|
||||
r.UserID,
|
||||
r.ProjectID,
|
||||
p.ProjectName AS ProjectTitle,
|
||||
r.BookID,
|
||||
b.BookTitle,
|
||||
r.Status,
|
||||
r.SourceType,
|
||||
r.FailureStage,
|
||||
COUNT(sr.SceneResultID) AS SceneCount,
|
||||
r.TotalTokens,
|
||||
r.TotalDurationMs,
|
||||
r.EstimatedCostGBP,
|
||||
r.EstimatedCostUSD,
|
||||
COALESCE(cr.ValidationErrorsCount, 0) + COALESCE(SUM(sr.ValidationErrorsCount), 0) AS ValidationErrorsCount,
|
||||
COALESCE(cr.ValidationWarningsCount, 0) + COALESCE(SUM(sr.ValidationWarningsCount), 0) AS ValidationWarningsCount
|
||||
FROM dbo.StoryIntelligenceRuns r
|
||||
LEFT JOIN dbo.Projects p ON p.ProjectID = r.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = r.BookID
|
||||
LEFT JOIN dbo.StoryIntelligenceChapterResults cr ON cr.StoryIntelligenceRunID = r.StoryIntelligenceRunID
|
||||
LEFT JOIN dbo.StoryIntelligenceSceneResults sr ON sr.StoryIntelligenceRunID = r.StoryIntelligenceRunID
|
||||
GROUP BY
|
||||
r.StoryIntelligenceRunID, r.CreatedUtc, r.UserID, r.ProjectID, p.ProjectName,
|
||||
r.BookID, b.BookTitle, r.Status, r.SourceType, r.FailureStage, r.TotalTokens,
|
||||
r.TotalDurationMs, r.EstimatedCostGBP, r.EstimatedCostUSD,
|
||||
cr.ValidationErrorsCount, cr.ValidationWarningsCount
|
||||
ORDER BY r.CreatedUtc DESC;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_GetAdmin
|
||||
@StoryIntelligenceRunID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
r.StoryIntelligenceRunID,
|
||||
r.UserID,
|
||||
r.ProjectID,
|
||||
p.ProjectName AS ProjectTitle,
|
||||
r.BookID,
|
||||
b.BookTitle,
|
||||
r.Status,
|
||||
r.SourceType,
|
||||
r.SourceFileName,
|
||||
r.SourceFileSizeBytes,
|
||||
r.SourceWordCount,
|
||||
r.SourceCharacterCount,
|
||||
r.SourceChapterCount,
|
||||
r.SourceDetectedImagesCount,
|
||||
r.SourceDetectedTablesCount,
|
||||
r.SourceDetectedFootnotesCount,
|
||||
r.SourceDetectedCommentsCount,
|
||||
r.PromptVersion,
|
||||
r.PromptVersionsSummary,
|
||||
r.Model,
|
||||
r.StartedUtc,
|
||||
r.CompletedUtc,
|
||||
r.FailureStage,
|
||||
r.TotalInputTokens,
|
||||
r.TotalOutputTokens,
|
||||
r.TotalTokens,
|
||||
r.TotalDurationMs,
|
||||
r.EstimatedCostGBP,
|
||||
r.EstimatedCostUSD,
|
||||
r.ErrorMessage,
|
||||
r.ErrorDetail,
|
||||
r.CreatedUtc,
|
||||
r.UpdatedUtc
|
||||
FROM dbo.StoryIntelligenceRuns r
|
||||
LEFT JOIN dbo.Projects p ON p.ProjectID = r.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = r.BookID
|
||||
WHERE r.StoryIntelligenceRunID = @StoryIntelligenceRunID;
|
||||
END;
|
||||
GO
|
||||
@ -66,24 +66,49 @@ else
|
||||
<dl class="mt-3 mb-0">
|
||||
<dt>Source type</dt>
|
||||
<dd>@run.SourceType</dd>
|
||||
<dt>Source file</dt>
|
||||
<dd>@Display(run.SourceFileName)</dd>
|
||||
<dt>Source file size</dt>
|
||||
<dd>@DisplayBytes(run.SourceFileSizeBytes)</dd>
|
||||
<dt>Source word count</dt>
|
||||
<dd>@Display(run.SourceWordCount)</dd>
|
||||
<dt>Source character count</dt>
|
||||
<dd>@Display(run.SourceCharacterCount)</dd>
|
||||
<dt>Source chapter count</dt>
|
||||
<dd>@Display(run.SourceChapterCount)</dd>
|
||||
<dt>Detected document features</dt>
|
||||
<dd>
|
||||
Images: @Display(run.SourceDetectedImagesCount),
|
||||
tables: @Display(run.SourceDetectedTablesCount),
|
||||
footnotes: @Display(run.SourceDetectedFootnotesCount),
|
||||
comments: @Display(run.SourceDetectedCommentsCount)
|
||||
</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>Prompt versions summary</dt>
|
||||
<dd>@Display(run.PromptVersionsSummary)</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>Failure stage</dt>
|
||||
<dd>@Display(run.FailureStage)</dd>
|
||||
<dt>Input tokens</dt>
|
||||
<dd>@Display(run.TotalInputTokens)</dd>
|
||||
<dt>Output tokens</dt>
|
||||
<dd>@Display(run.TotalOutputTokens)</dd>
|
||||
<dt>Estimated cost</dt>
|
||||
<dd>@DisplayCost(run.EstimatedCostGBP, "GBP") / @DisplayCost(run.EstimatedCostUSD, "USD")</dd>
|
||||
<dt>Error message</dt>
|
||||
<dd>@Display(run.ErrorMessage)</dd>
|
||||
<dt>Error detail</dt>
|
||||
<dd><pre class="mb-0">@Display(run.ErrorDetail)</pre></dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@ -174,6 +199,12 @@ else
|
||||
: $"{TimeSpan.FromMilliseconds(milliseconds.Value).TotalSeconds:0.00} sec"
|
||||
: "None";
|
||||
|
||||
private static string DisplayBytes(long? bytes)
|
||||
=> bytes.HasValue ? $"{bytes.Value:N0} bytes" : "None";
|
||||
|
||||
private static string DisplayCost(decimal? value, string currency)
|
||||
=> value.HasValue ? $"{value.Value:0.000000} {currency}" : "None";
|
||||
|
||||
private static string DisplayName(string? title, int? id, string label)
|
||||
=> !string.IsNullOrWhiteSpace(title)
|
||||
? title
|
||||
|
||||
@ -43,8 +43,11 @@
|
||||
<th>Created</th>
|
||||
<th>Project / Book</th>
|
||||
<th>Status</th>
|
||||
<th>Source</th>
|
||||
<th>Failure stage</th>
|
||||
<th>Scenes</th>
|
||||
<th>Tokens</th>
|
||||
<th>Cost</th>
|
||||
<th>Duration</th>
|
||||
<th>Validation</th>
|
||||
<th></th>
|
||||
@ -61,8 +64,11 @@
|
||||
<div class="muted">@DisplayName(run.BookTitle, run.BookID, "Book")</div>
|
||||
</td>
|
||||
<td>@run.Status</td>
|
||||
<td>@Display(run.SourceType)</td>
|
||||
<td>@Display(run.FailureStage)</td>
|
||||
<td>@run.SceneCount.ToString("N0")</td>
|
||||
<td>@Display(run.TotalTokens)</td>
|
||||
<td>@DisplayCost(run.EstimatedCostGBP, "GBP") / @DisplayCost(run.EstimatedCostUSD, "USD")</td>
|
||||
<td>@DisplayDuration(run.TotalDurationMs)</td>
|
||||
<td>@run.ValidationErrorsCount.ToString("N0") error(s), @run.ValidationWarningsCount.ToString("N0") warning(s)</td>
|
||||
<td><a class="btn btn-outline-primary btn-sm" asp-action="StoryIntelligenceRunDetails" asp-route-id="@run.StoryIntelligenceRunID">Open</a></td>
|
||||
@ -90,6 +96,9 @@
|
||||
: $"{TimeSpan.FromMilliseconds(milliseconds.Value).TotalSeconds:0.00} sec"
|
||||
: "None";
|
||||
|
||||
private static string DisplayCost(decimal? value, string currency)
|
||||
=> value.HasValue ? $"{value.Value:0.000000} {currency}" : "None";
|
||||
|
||||
private static string DisplayName(string? title, int? id, string label)
|
||||
=> !string.IsNullOrWhiteSpace(title)
|
||||
? title
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user