Ensure Story Intelligence queue supports known characters

This commit is contained in:
Nick Beckley 2026-08-16 17:04:28 +00:00
parent 01555136e7
commit dbbc46f02b

View File

@ -41,6 +41,9 @@ public interface IStoryIntelligenceResultRepository
public sealed class StoryIntelligenceResultRepository(ISqlConnectionFactory connectionFactory) : IStoryIntelligenceResultRepository
{
private static readonly SemaphoreSlim QueueSupportLock = new(1, 1);
private static bool queueSupportEnsured;
public async Task<int> SaveAsync(StoryIntelligenceRunSaveRequest request)
{
using var connection = connectionFactory.CreateConnection();
@ -160,6 +163,7 @@ public sealed class StoryIntelligenceResultRepository(ISqlConnectionFactory conn
public async Task<int> QueueAdminTextAsync(StoryIntelligenceRunQueueRequest request)
{
using var connection = connectionFactory.CreateConnection();
await EnsureKnownCharacterQueueSupportAsync(connection);
return await connection.QuerySingleAsync<int>(
"dbo.StoryIntelligenceRun_QueueAdminText",
new
@ -185,6 +189,129 @@ public sealed class StoryIntelligenceResultRepository(ISqlConnectionFactory conn
commandType: CommandType.StoredProcedure);
}
private static async Task EnsureKnownCharacterQueueSupportAsync(IDbConnection connection)
{
if (queueSupportEnsured)
{
return;
}
await QueueSupportLock.WaitAsync();
try
{
if (queueSupportEnsured)
{
return;
}
await connection.ExecuteAsync(
"""
IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'KnownCharactersJson') IS NULL
BEGIN
ALTER TABLE dbo.StoryIntelligenceRuns ADD KnownCharactersJson nvarchar(max) NULL;
END;
""");
await connection.ExecuteAsync(
"""
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_QueueAdminText
@UserID int,
@ProjectID int = NULL,
@BookID int = NULL,
@ChapterID int = NULL,
@ChapterNumber decimal(9, 2) = NULL,
@SourceType nvarchar(50),
@SourceLabel nvarchar(300),
@SourceText nvarchar(max),
@SourceFileName nvarchar(260) = NULL,
@SourceFileSizeBytes bigint = NULL,
@SourceWordCount int = NULL,
@SourceCharacterCount int = NULL,
@SourceParagraphCount int = NULL,
@SourceChapterCount int = NULL,
@Model nvarchar(100),
@PromptVersionsSummary nvarchar(500),
@KnownCharactersJson nvarchar(max) = NULL
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.StoryIntelligenceRuns
(
UserID, ProjectID, BookID, ChapterID, ChapterNumber, Status, SourceType, SourceLabel, SourceText,
SourceFileName, SourceFileSizeBytes, SourceWordCount, SourceCharacterCount, SourceParagraphCount,
SourceChapterCount, Model, PromptVersion, PromptVersionsSummary, KnownCharactersJson, StartedUtc,
CurrentStage, CurrentMessage, CompletedScenes, FailedScenes
)
VALUES
(
@UserID, @ProjectID, @BookID, @ChapterID, @ChapterNumber, N'Pending', @SourceType, @SourceLabel, @SourceText,
@SourceFileName, @SourceFileSizeBytes, @SourceWordCount, @SourceCharacterCount, @SourceParagraphCount,
@SourceChapterCount, @Model, @PromptVersionsSummary, @PromptVersionsSummary, @KnownCharactersJson,
SYSUTCDATETIME(), N'Pending', N'Queued for Story Intelligence processing.', 0, 0
);
SELECT CAST(SCOPE_IDENTITY() AS int) AS StoryIntelligenceRunID;
END;
""");
await connection.ExecuteAsync(
"""
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_ClaimNextPending
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @StoryIntelligenceRunID int;
SELECT TOP (1) @StoryIntelligenceRunID = StoryIntelligenceRunID
FROM dbo.StoryIntelligenceRuns WITH (UPDLOCK, READPAST)
WHERE Status = N'Pending'
ORDER BY CreatedUtc;
IF @StoryIntelligenceRunID IS NULL
BEGIN
SELECT TOP (0)
StoryIntelligenceRunID, UserID, ProjectID, BookID, ChapterID, ChapterNumber, Status, SourceType, SourceLabel,
SourceText, SourceWordCount, SourceCharacterCount, SourceParagraphCount, SourceChapterCount, PromptVersion,
PromptVersionsSummary, KnownCharactersJson, Model, StartedUtc, CompletedUtc, FailureStage, TotalInputTokens,
TotalOutputTokens, TotalTokens, TotalDurationMs, EstimatedCostGBP, EstimatedCostUSD,
ErrorMessage, ErrorDetail, CurrentStage, CurrentMessage, TotalDetectedScenes,
CompletedScenes, FailedScenes, CancellationRequestedUtc, CancelledUtc, CreatedUtc, UpdatedUtc
FROM dbo.StoryIntelligenceRuns;
RETURN;
END;
UPDATE dbo.StoryIntelligenceRuns
SET Status = N'Running',
StartedUtc = SYSUTCDATETIME(),
CurrentStage = N'ChapterStructure',
CurrentMessage = N'Running Chapter Structure analysis.',
UpdatedUtc = SYSUTCDATETIME()
WHERE StoryIntelligenceRunID = @StoryIntelligenceRunID
AND Status = N'Pending';
SELECT
StoryIntelligenceRunID, UserID, ProjectID, BookID, ChapterID, ChapterNumber, Status, SourceType, SourceLabel,
SourceText, SourceWordCount, SourceCharacterCount, SourceParagraphCount, SourceChapterCount, PromptVersion,
PromptVersionsSummary, KnownCharactersJson, Model, StartedUtc, CompletedUtc, FailureStage, TotalInputTokens,
TotalOutputTokens, TotalTokens, TotalDurationMs, EstimatedCostGBP, EstimatedCostUSD,
ErrorMessage, ErrorDetail, CurrentStage, CurrentMessage, TotalDetectedScenes,
CompletedScenes, FailedScenes, CancellationRequestedUtc, CancelledUtc, CreatedUtc, UpdatedUtc
FROM dbo.StoryIntelligenceRuns
WHERE StoryIntelligenceRunID = @StoryIntelligenceRunID;
END;
""");
queueSupportEnsured = true;
}
finally
{
QueueSupportLock.Release();
}
}
public async Task<StoryIntelligenceQueuedRun?> ClaimNextPendingAsync()
{
using var connection = connectionFactory.CreateConnection();