224 lines
9.6 KiB
C#
224 lines
9.6 KiB
C#
namespace PlotLine.Models;
|
|
|
|
public static class StoryIntelligenceJobStatuses
|
|
{
|
|
public const string Pending = "Pending";
|
|
public const string Preparing = "Preparing";
|
|
public const string ReadingScenes = "ReadingScenes";
|
|
public const string AnalysingCharacters = "AnalysingCharacters";
|
|
public const string AnalysingRelationships = "AnalysingRelationships";
|
|
public const string AnalysingLocations = "AnalysingLocations";
|
|
public const string AnalysingAssets = "AnalysingAssets";
|
|
public const string AnalysingTimeline = "AnalysingTimeline";
|
|
public const string Finalising = "Finalising";
|
|
public const string Completed = "Completed";
|
|
public const string Cancelled = "Cancelled";
|
|
public const string Failed = "Failed";
|
|
}
|
|
|
|
public sealed class StoryIntelligenceJob
|
|
{
|
|
public Guid JobID { get; init; }
|
|
public int ConsentID { get; init; }
|
|
public int UserID { get; init; }
|
|
public int ProjectID { get; init; }
|
|
public int BookID { get; init; }
|
|
public string Status { get; init; } = StoryIntelligenceJobStatuses.Pending;
|
|
public DateTime CreatedUtc { get; init; }
|
|
public DateTime? StartedUtc { get; init; }
|
|
public DateTime? CompletedUtc { get; init; }
|
|
public DateTime? CancelledUtc { get; init; }
|
|
public int ProgressPercent { get; init; }
|
|
public string CurrentStage { get; init; } = StoryIntelligenceJobStatuses.Pending;
|
|
public string CurrentMessage { get; init; } = "Story Intelligence is queued.";
|
|
public string? EstimatedRemaining { get; init; }
|
|
public string? ErrorMessage { get; init; }
|
|
public DateTime UpdatedUtc { get; init; }
|
|
public bool IsActive => Status is StoryIntelligenceJobStatuses.Pending
|
|
or StoryIntelligenceJobStatuses.Preparing
|
|
or StoryIntelligenceJobStatuses.ReadingScenes
|
|
or StoryIntelligenceJobStatuses.AnalysingCharacters
|
|
or StoryIntelligenceJobStatuses.AnalysingRelationships
|
|
or StoryIntelligenceJobStatuses.AnalysingLocations
|
|
or StoryIntelligenceJobStatuses.AnalysingAssets
|
|
or StoryIntelligenceJobStatuses.AnalysingTimeline
|
|
or StoryIntelligenceJobStatuses.Finalising;
|
|
public bool IsCompleted => string.Equals(Status, StoryIntelligenceJobStatuses.Completed, StringComparison.Ordinal);
|
|
public bool IsCancelled => string.Equals(Status, StoryIntelligenceJobStatuses.Cancelled, StringComparison.Ordinal);
|
|
}
|
|
|
|
public sealed class StoryIntelligenceJobProgress
|
|
{
|
|
public Guid JobID { get; init; }
|
|
public int UserID { get; init; }
|
|
public int ProjectID { get; init; }
|
|
public int BookID { get; init; }
|
|
public string Status { get; init; } = StoryIntelligenceJobStatuses.Pending;
|
|
public string CurrentStage { get; init; } = StoryIntelligenceJobStatuses.Pending;
|
|
public string CurrentMessage { get; init; } = "Story Intelligence is queued.";
|
|
public int ProgressPercent { get; init; }
|
|
public string? EstimatedRemaining { get; init; }
|
|
public string ElapsedTime { get; init; } = "0 min";
|
|
public string? ErrorMessage { get; init; }
|
|
public DateTime UpdatedUtc { get; init; }
|
|
public bool IsActive { get; init; }
|
|
public bool IsCompleted { get; init; }
|
|
}
|
|
|
|
public sealed class StoryIntelligenceRunProgressEvent
|
|
{
|
|
public int RunID { get; init; }
|
|
public int UserID { get; init; }
|
|
public int? ProjectID { get; init; }
|
|
public int? BookID { get; init; }
|
|
public int? ChapterID { get; init; }
|
|
public decimal? ChapterNumber { get; init; }
|
|
public string Status { get; init; } = StoryIntelligenceRunStatuses.Pending;
|
|
public string CurrentStage { get; init; } = string.Empty;
|
|
public string CurrentMessage { get; init; } = string.Empty;
|
|
public string EventType { get; init; } = string.Empty;
|
|
public int? TotalDetectedScenes { get; init; }
|
|
public int? CompletedScenes { get; init; }
|
|
public int? FailedScenes { get; init; }
|
|
public int? TotalTokens { get; init; }
|
|
public long? TotalDurationMs { get; init; }
|
|
public int? CurrentSceneNumber { get; init; }
|
|
public string ElapsedTime { get; init; } = "0 sec";
|
|
public string? EstimatedRemaining { get; init; }
|
|
public string? LatestSceneSummary { get; init; }
|
|
public IReadOnlyList<string> RecentDiscoveries { get; init; } = [];
|
|
public string? ErrorMessage { get; init; }
|
|
public DateTime UpdatedUtc { get; init; } = DateTime.UtcNow;
|
|
public bool IsActive { get; init; }
|
|
public bool IsCompleted { get; init; }
|
|
}
|
|
|
|
public sealed class StoryIntelligenceOptions
|
|
{
|
|
public string ApiKey { get; init; } = string.Empty;
|
|
public string Model { get; init; } = string.Empty;
|
|
public string ChapterStructureModel { get; init; } = string.Empty;
|
|
public string SceneIntelligenceModel { get; init; } = string.Empty;
|
|
public string ImageGenerationModel { get; init; } = string.Empty;
|
|
public string ImageGenerationSize { get; init; } = "1024x1024";
|
|
public int MaxOutputTokens { get; init; } = 4000;
|
|
public int? SceneIntelligenceMaxOutputTokens { get; init; }
|
|
public int TimeoutSeconds { get; init; } = 120;
|
|
|
|
public string EffectiveModel => FirstConfigured(Model);
|
|
public string EffectiveChapterStructureModel => FirstConfigured(ChapterStructureModel, Model);
|
|
public string EffectiveSceneIntelligenceModel => FirstConfigured(SceneIntelligenceModel, Model);
|
|
|
|
public static string BuildModelSummary(string? chapterStructureModel, string? sceneIntelligenceModel)
|
|
=> $"Chapter={FirstConfigured(chapterStructureModel)}; Scene={FirstConfigured(sceneIntelligenceModel)}";
|
|
|
|
public static StoryIntelligenceStageModels ParseModelSummary(
|
|
string? modelSummary,
|
|
string defaultChapterStructureModel,
|
|
string defaultSceneIntelligenceModel)
|
|
{
|
|
var chapterModel = defaultChapterStructureModel;
|
|
var sceneModel = defaultSceneIntelligenceModel;
|
|
|
|
if (!string.IsNullOrWhiteSpace(modelSummary))
|
|
{
|
|
var segments = modelSummary.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
var matchedStage = false;
|
|
foreach (var segment in segments)
|
|
{
|
|
var parts = segment.Split('=', 2, StringSplitOptions.TrimEntries);
|
|
if (parts.Length != 2 || string.IsNullOrWhiteSpace(parts[1]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (string.Equals(parts[0], "Chapter", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
chapterModel = parts[1];
|
|
matchedStage = true;
|
|
}
|
|
else if (string.Equals(parts[0], "Scene", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
sceneModel = parts[1];
|
|
matchedStage = true;
|
|
}
|
|
}
|
|
|
|
if (!matchedStage && !string.IsNullOrWhiteSpace(modelSummary))
|
|
{
|
|
chapterModel = modelSummary.Trim();
|
|
sceneModel = modelSummary.Trim();
|
|
}
|
|
}
|
|
|
|
return new StoryIntelligenceStageModels(
|
|
FirstConfigured(chapterModel, defaultChapterStructureModel),
|
|
FirstConfigured(sceneModel, defaultSceneIntelligenceModel));
|
|
}
|
|
|
|
private static string FirstConfigured(params string?[] values)
|
|
=> values.FirstOrDefault(value => !string.IsNullOrWhiteSpace(value))?.Trim() ?? string.Empty;
|
|
}
|
|
|
|
public sealed record StoryIntelligenceStageModels(string ChapterStructureModel, string SceneIntelligenceModel);
|
|
|
|
public sealed class StoryIntelligencePricingOptions
|
|
{
|
|
public decimal? InputPerMillionUsd { get; init; }
|
|
public decimal? OutputPerMillionUsd { get; init; }
|
|
public decimal? UsdToGbpRate { get; init; }
|
|
public Dictionary<string, StoryIntelligenceModelPricingOptions> Models { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public StoryIntelligenceModelPricingOptions? GetPricingForModel(string? model)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(model)
|
|
&& Models.TryGetValue(model.Trim(), out var modelPricing)
|
|
&& (modelPricing.InputPerMillionUsd.HasValue || modelPricing.OutputPerMillionUsd.HasValue))
|
|
{
|
|
return modelPricing;
|
|
}
|
|
|
|
return InputPerMillionUsd.HasValue || OutputPerMillionUsd.HasValue
|
|
? new StoryIntelligenceModelPricingOptions
|
|
{
|
|
InputPerMillionUsd = InputPerMillionUsd,
|
|
OutputPerMillionUsd = OutputPerMillionUsd
|
|
}
|
|
: null;
|
|
}
|
|
}
|
|
|
|
public sealed class StoryIntelligenceModelPricingOptions
|
|
{
|
|
public decimal? InputPerMillionUsd { get; init; }
|
|
public decimal? OutputPerMillionUsd { get; init; }
|
|
}
|
|
|
|
public sealed class StoryIntelligenceRun
|
|
{
|
|
public Guid RunId { get; init; } = Guid.NewGuid();
|
|
public int ProjectId { get; init; }
|
|
public int BookId { get; init; }
|
|
public DateTime StartedUtc { get; init; } = DateTime.UtcNow;
|
|
public DateTime? CompletedUtc { get; init; }
|
|
public string Status { get; init; } = "Pending";
|
|
public string PromptVersion { get; init; } = string.Empty;
|
|
public string Model { get; init; } = string.Empty;
|
|
public int? InputTokens { get; init; }
|
|
public int? OutputTokens { get; init; }
|
|
public decimal? EstimatedCost { get; init; }
|
|
public bool Cancelled { get; init; }
|
|
public string? ErrorMessage { get; init; }
|
|
}
|
|
|
|
public sealed class StoryIntelligenceClientResult
|
|
{
|
|
public string RawResponseText { get; init; } = string.Empty;
|
|
public string Model { get; init; } = string.Empty;
|
|
public TimeSpan Duration { get; init; }
|
|
public int RetryCount { get; init; }
|
|
public int? InputTokens { get; init; }
|
|
public int? OutputTokens { get; init; }
|
|
public decimal? EstimatedCost { get; init; }
|
|
}
|