using PlotLine.Models; namespace PlotLine.Services; public static class StoryIntelligenceSceneStructuredOutputSchema { public const string ContractName = "scene_intelligence_v1"; public static StoryIntelligenceResponseContract Contract { get; } = new() { Name = ContractName, Strict = true, Schema = BuildSchema() }; private static Dictionary BuildSchema() { var sceneReference = Obj(Props( ("projectId", NullableInteger()), ("bookId", NullableInteger()), ("chapterId", NullableInteger()), ("sceneId", NullableInteger()), ("chapterNumber", NullableNumber()), ("sceneNumber", NullableNumber()), ("sourceLabel", Str()))); var purpose = Obj(Props(("observedFunction", Str()), ("confidence", Confidence()))); var pointOfView = Obj(Props( ("characterName", NullableString()), ("narrativeMode", NullableString()), ("confidence", Confidence()), ("evidence", NullableString()))); var setting = Obj(Props( ("timeOfDay", NullableString()), ("dateOrTimeReference", NullableString()), ("locationName", NullableString()), ("locationType", Enum("named", "generic", "unclear")), ("genericRoomType", NullableString()), ("parentLocationHint", NullableString()), ("confidence", Confidence()))); var characters = Arr(Obj(Props( ("name", Str()), ("roleInScene", Enum("present", "pointOfView", "speaker", "mentioned", "unclear")), ("mentionedOnly", Bool()), ("aliases", Arr(Str())), ("actions", Arr(Str())), ("confidence", Confidence()), ("notes", NullableString())))); var characterAppearance = Arr(Obj(Props( ("canonicalName", Str()), ("aliases", Arr(Str())), ("title", NullableString()), ("presentation", NullableString()), ("explicitAge", NullableString()), ("inferredAgeBand", NullableString()), ("hairColour", NullableString()), ("hairLength", NullableString()), ("hairStyle", NullableString()), ("eyeColour", NullableString()), ("facialHair", NullableString()), ("glasses", NullableString()), ("height", NullableString()), ("build", NullableString()), ("distinctiveFeatures", Arr(Str())), ("clothing", NullableString()), ("roleOrOccupation", NullableString()), ("relationshipAgeEvidence", NullableString()), ("pronounEvidence", NullableString()), ("evidence", Str()), ("confidence", Confidence()), ("evidenceKind", Enum("explicit", "inferred"))))); var locations = Arr(Obj(Props( ("name", Str()), ("locationType", Enum("named", "generic", "unclear")), ("genericRoomType", NullableString()), ("parentLocationHint", NullableString()), ("presentInScene", Bool()), ("mentionedOnly", Bool()), ("confidence", Confidence()), ("notes", NullableString())))); var assets = Arr(Obj(Props( ("name", Str()), ("assetType", NullableString()), ("status", NullableString()), ("ownerOrHolder", NullableString()), ("mentionedOnly", Bool()), ("confidence", Confidence()), ("notes", NullableString())))); var relationships = Arr(Obj(Props( ("characterA", Str()), ("characterB", Str()), ("relationshipSignal", Enum("trust", "mistrust", "affection", "fear", "authority", "secrecy", "conflict", "dependence", "protection", "rivalry", "obligation")), ("evidence", Str()), ("confidence", Confidence())))); var knowledgeChanges = Arr(Obj(Props( ("recipientCharacter", Str()), ("knowledgeItem", Str()), ("changeType", Enum("Learns", "Realises", "IsTold", "Discovers", "Confirms", "Suspects", "Misunderstands")), ("sourceCharacter", NullableString()), ("sourceType", NullableString()), ("evidence", Str()), ("confidence", Confidence())))); var timelineClues = Arr(Obj(Props( ("clue", Str()), ("relativeOrder", NullableString()), ("absoluteDate", NullableString()), ("evidence", Str()), ("confidence", Confidence())))); var narrativeArcs = Arr(Obj(Props( ("title", Str()), ("description", Str()), ("arcType", Str()), ("scale", Enum("major", "secondary", "thread")), ("changeType", Enum("Introduced", "Developed", "Complicated", "Revealed", "Escalated", "Resolved", "Reopened", "Mentioned")), ("significance", Enum("major", "moderate", "minor")), ("continuityKey", Str()), ("parentArcTitle", NullableString()), ("matchingExistingPlotLineId", NullableInteger()), ("matchingExistingThreadId", NullableInteger()), ("evidence", Str()), ("confidence", Confidence())))); var questionsRaised = Arr(Obj(Props(("question", Str()), ("scope", Str()), ("evidence", Str()), ("confidence", Confidence())))); var questionsAnswered = Arr(Obj(Props(("question", Str()), ("answer", Str()), ("evidence", Str()), ("confidence", Confidence())))); var observations = Arr(Obj(Props( ("observationType", Enum("CharacterAction", "CharacterKnowledge", "CharacterLocation", "RelationshipSignal", "AssetInteraction", "LocationSignal", "TimelineSignal", "QuestionSignal")), ("subjectEntityType", Enum("Character", "Location", "Asset", "Knowledge", "Relationship", "Timeline", "Scene", "Unknown")), ("subjectName", Str()), ("objectEntityType", NullableEnum("Character", "Location", "Asset", "Knowledge", "Relationship", "Timeline", "Scene", "Unknown")), ("objectName", NullableString()), ("predicate", Str()), ("description", Str()), ("evidence", Str()), ("confidence", Confidence())))); var metricValues = Arr(Obj(Props( ("key", Str("Configured metric key from sceneContext.configuredMetrics.")), ("score", Integer()), ("confidence", Confidence())))); var sourceLimits = Obj(Props(("containsAmbiguity", Bool()), ("ambiguityNotes", Arr(Str())))); return Obj(Props( ("schemaVersion", Str("Scene Intelligence schema version. Use 1.0.")), ("sceneReference", sceneReference), ("scenePurpose", purpose), ("pointOfView", pointOfView), ("setting", setting), ("characters", characters), ("characterAppearance", characterAppearance), ("locations", locations), ("assets", assets), ("relationships", relationships), ("knowledgeChanges", knowledgeChanges), ("timelineClues", timelineClues), ("narrativeArcs", narrativeArcs), ("questionsRaised", questionsRaised), ("questionsAnswered", questionsAnswered), ("observations", observations), ("metricValues", metricValues), ("sourceLimits", sourceLimits))); } private static Dictionary Obj(Dictionary properties) => new() { ["type"] = "object", ["properties"] = properties, ["required"] = properties.Keys.ToArray(), ["additionalProperties"] = false }; private static Dictionary Arr(object items) => new() { ["type"] = "array", ["items"] = items }; private static Dictionary Str(string? description = null) { var value = new Dictionary { ["type"] = "string" }; if (!string.IsNullOrWhiteSpace(description)) { value["description"] = description; } return value; } private static Dictionary NullableString() => new() { ["type"] = new[] { "string", "null" } }; private static Dictionary Integer() => new() { ["type"] = "integer" }; private static Dictionary NullableInteger() => new() { ["type"] = new[] { "integer", "null" } }; private static Dictionary NullableNumber() => new() { ["type"] = new[] { "number", "null" } }; private static Dictionary Bool() => new() { ["type"] = "boolean" }; private static Dictionary Confidence() => new() { ["type"] = "number", ["minimum"] = 0, ["maximum"] = 1 }; private static Dictionary Enum(params string[] values) => new() { ["type"] = "string", ["enum"] = values }; private static Dictionary NullableEnum(params string[] values) => new() { ["type"] = new[] { "string", "null" }, ["enum"] = values.Cast().Append(null).ToArray() }; private static Dictionary Props(params (string Name, object Schema)[] properties) => properties.ToDictionary(property => property.Name, property => property.Schema, StringComparer.Ordinal); }