diff --git a/services/ontology/schemas/storyListen.json b/services/ontology/schemas/storyListen.json new file mode 100644 index 000000000..6e4a44f2d --- /dev/null +++ b/services/ontology/schemas/storyListen.json @@ -0,0 +1,96 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "schemaId": "06025631-8e24-4d74-94d5-c7e99bc9f263", + "title": "StoryListen", + "domain": "travel", + "type": "object", + "description": "One playback of a story by a person. Lives on the listener's vault. Carries no copies of story fields (title, language live in the story itself) and no inline context (the context lives in the referenced StoryRequest). Exactly one of storyRef / tmaticStoryId identifies the story: storyRef for stories that exist as TravelStory envelopes, tmaticStoryId for stories from the tmatic.travel library, which is not yet on W3DS", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique identifier of this listen event" + }, + "listenerEName": { + "type": "string", + "description": "@-prefixed eName of the listener" + }, + "storyRef": { + "type": "string", + "description": "w3ds://envelope?id=@/ reference to the TravelStory version played, for FWM-created stories" + }, + "tmaticStoryId": { + "type": "string", + "format": "uuid", + "description": "tmatic.travel story UUID, for stories played from the tmatic library" + }, + "tmaticLanguage": { + "type": "string", + "description": "BCP-47 tag of the tmatic language version played; only with tmaticStoryId (a tmatic story UUID covers all its language versions, so the pair identifies what was actually heard)" + }, + "requestRef": { + "type": "string", + "description": "w3ds://envelope reference to the StoryRequest this playback answered" + }, + "startedAt": { + "type": "string", + "format": "date-time", + "description": "Playback start, ISO 8601" + }, + "completionRate": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Fraction of the audio actually listened to" + }, + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "description": "Listener's rating of this story, if given" + }, + "feedbackText": { + "type": "string", + "description": "Free-form listener feedback, if given" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "Record creation timestamp, ISO 8601" + } + }, + "required": [ + "id", + "listenerEName", + "startedAt", + "createdAt" + ], + "additionalProperties": false, + "oneOf": [ + { + "required": [ + "storyRef" + ], + "not": { + "required": [ + "tmaticStoryId" + ] + } + }, + { + "required": [ + "tmaticStoryId" + ], + "not": { + "required": [ + "storyRef" + ] + } + } + ], + "dependencies": { + "tmaticLanguage": [ + "tmaticStoryId" + ] + } +} \ No newline at end of file diff --git a/services/ontology/schemas/storyProvenance.json b/services/ontology/schemas/storyProvenance.json new file mode 100644 index 000000000..e6c311e50 --- /dev/null +++ b/services/ontology/schemas/storyProvenance.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "schemaId": "52662185-59c1-44e5-8519-685796c70a96", + "title": "StoryProvenance", + "domain": "travel", + "type": "object", + "description": "How one TravelStory version came to be: who it was made for, where it was requested, which models produced it, and what it was translated or derived from. Lives next to the story on the content owner's vault. Language versions of one story are reconstructed through provenance: each translation's provenance points at the original version via originalStoryRef. Fields here record the immutable creation event; mutable facts stay in their own ontologies and are referenced", + "properties": { + "id": { "type": "string", "format": "uuid", "description": "Unique identifier of this provenance record" }, + "storyRef": { "type": "string", "description": "w3ds://envelope?id=@/ reference to the TravelStory version this record describes" }, + "requestedForEName": { "type": "string", "description": "@-prefixed eName of the person the story was originally created for" }, + "requestRef": { "type": "string", "description": "w3ds://envelope reference to the StoryRequest that triggered creation (lives on the requester's vault)" }, + "requestLocation": { + "type": "object", + "description": "Where the requester was at creation time, WGS84. An immutable event fact recorded at creation", + "properties": { + "lat": { "type": "number", "description": "Latitude" }, + "lon": { "type": "number", "description": "Longitude" } + }, + "required": ["lat", "lon"], + "additionalProperties": false + }, + "textModel": { "type": "string", "description": "Model that wrote the text, e.g. 'claude-fable-5'" }, + "voiceModel": { "type": "string", "description": "TTS model and voice, e.g. 'eleven_v3/'" }, + "promptVersion": { "type": "string", "description": "Version tag of the prompt template used" }, + "sourceTmaticStoryId": { "type": "string", "format": "uuid", "description": "tmatic.travel story UUID this version was translated or derived from, when the source is the tmatic library" }, + "originalStoryRef": { "type": "string", "description": "w3ds://envelope reference to the TravelStory version this one was translated from, when the source is another FWM story version" }, + "extractionRefs": { "type": "array", "items": { "type": "string" }, "description": "w3ds://envelope references to FileExtraction records used to identify the subject (e.g. vision reading of the user's photo)" }, + "createdAt": { "type": "string", "format": "date-time", "description": "Creation timestamp, ISO 8601" } + }, + "required": ["id", "storyRef", "createdAt"], + "additionalProperties": false +} diff --git a/services/ontology/schemas/storyRequest.json b/services/ontology/schemas/storyRequest.json new file mode 100644 index 000000000..9e678375a --- /dev/null +++ b/services/ontology/schemas/storyRequest.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "schemaId": "d40d3da4-0999-444a-b23a-9f964a96c964", + "title": "StoryRequest", + "domain": "travel", + "type": "object", + "description": "The context of one story request: a person pressed geo/camera/microphone (or accepted a suggestion) at a place and moment. Lives on the requester's vault. Referenced by StoryListen (what context a story was played in) and by StoryProvenance (what context a story was created in) — the context is written once here, never copied. Machine readings of the request's inputs (photo vision, voice transcript) are FileExtraction records, referenced via extractionRefs", + "properties": { + "id": { "type": "string", "format": "uuid", "description": "Unique identifier of this request" }, + "requesterEName": { "type": "string", "description": "@-prefixed eName of the person who made the request" }, + "entryMode": { "type": "string", "enum": ["geo", "camera", "voice", "suggestion"], "description": "How the request was initiated: geo button, camera shot, voice query, or an accepted in-app suggestion" }, + "location": { + "type": "object", + "description": "Where the requester was, WGS84", + "properties": { + "lat": { "type": "number", "description": "Latitude" }, + "lon": { "type": "number", "description": "Longitude" } + }, + "required": ["lat", "lon"], + "additionalProperties": false + }, + "modality": { "type": "string", "enum": ["walking", "cycling", "driving", "boat"], "description": "Detected movement mode at request time" }, + "queryText": { "type": "string", "description": "The voice query as text, when entryMode=voice and no FileExtraction transcript record was kept; when one was, reference it via extractionRefs instead of copying" }, + "photoFileRef": { "type": "string", "description": "w3ds://file URI of the photo taken, when entryMode=camera" }, + "extractionRefs": { "type": "array", "items": { "type": "string" }, "description": "w3ds://envelope references to FileExtraction records made from this request's inputs (vision reading of the photo, transcript of the voice query)" }, + "requestedAt": { "type": "string", "format": "date-time", "description": "Request timestamp, ISO 8601" } + }, + "required": ["id", "requesterEName", "entryMode", "requestedAt"], + "additionalProperties": false +} diff --git a/services/ontology/schemas/travelStory.json b/services/ontology/schemas/travelStory.json new file mode 100644 index 000000000..beee73229 --- /dev/null +++ b/services/ontology/schemas/travelStory.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "schemaId": "ba56d2d3-e6bf-4bfd-ac75-6436c56be76f", + "title": "TravelStory", + "domain": "travel", + "type": "object", + "description": "One language version of a location-bound story for travellers. Each language version is its own envelope; versions of the same story are linked through their StoryProvenance records, never by copying fields. Everything about HOW the story came to be (who requested it, where, which models wrote and voiced it, what it was translated from) lives in the referenced StoryProvenance envelope, not here.", + "properties": { + "id": { "type": "string", "format": "uuid", "description": "Unique identifier of this story version" }, + "title": { "type": "string", "description": "Story title in the story language" }, + "text": { "type": "string", "description": "Full story text (plain text, no HTML)" }, + "language": { "type": "string", "description": "BCP-47 language tag of this version, e.g. 'ru', 'en', 'nl'" }, + "subjectName": { "type": "string", "description": "Name of the object/place the story is about, e.g. 'Sint-Janskerk, Maastricht'. Interim field: once landmark entities get their own w3ids, the story-to-object link moves to a dedicated M:N relation and this stays only as a display hint" }, + "location": { + "type": "object", + "description": "Anchor point of the story subject, WGS84", + "properties": { + "lat": { "type": "number", "minimum": -90, "maximum": 90, "description": "Latitude" }, + "lon": { "type": "number", "minimum": -180, "maximum": 180, "description": "Longitude" } + }, + "required": ["lat", "lon"], + "additionalProperties": false + }, + "triggerZones": { + "type": "array", + "description": "Zones where this story is relevant to play, following tmatic.travel zone semantics. The zone is designed at creation time by the generating AI, which must reason about THIS object's nature and shape: what area a listener would stand or move in for the story to make sense. Illustrative examples, not a closed list: a square → its perimeter buffered ~20 m; a building → a circle of ~30 m; a whole village → its circumscribed circle; a street → a corridor along its full length; a river or embankment → a corridor along the stretch the story concerns; a fountain or monument → a small circle within sight; a viewpoint story about a distant object → the viewpoint, not the object. Stories written for driving carry an additional zone: a corridor along the road segment where playback is intended (~50 m each side)", + "items": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["CIRCLE", "POLYGON"], "description": "Zone geometry kind" }, + "centerLat": { "type": "number", "description": "CIRCLE only: center latitude" }, + "centerLon": { "type": "number", "description": "CIRCLE only: center longitude" }, + "radius": { "type": "number", "description": "CIRCLE only: radius in meters" }, + "geometry": { "type": "string", "description": "POLYGON only: WKT polygon string, lon-lat order" } + }, + "required": ["type"], + "additionalProperties": false + } + }, + "modality": { "type": "string", "enum": ["walking", "cycling", "driving", "boat"], "description": "Movement mode the story was written for" }, + "audioUri": { "type": "string", "description": "w3ds://file URI of the narrated audio" }, + "audioDuration": { "type": "integer", "description": "Audio duration in seconds" }, + "imageUris": { "type": "array", "items": { "type": "string" }, "description": "w3ds://file URIs of related photos" }, + "sourceType": { "type": "string", "enum": ["generated", "translated", "derived"], "description": "generated = written from scratch by AI; translated = translation of an existing story; derived = based on existing content. Details of the source are in StoryProvenance" }, + "license": { "type": "string", "description": "License of this content, e.g. 'proprietary-minstrel'" }, + "provenanceRef": { "type": "string", "description": "w3ds://envelope?id=@/ reference to the StoryProvenance record of this version. @ is the owning entity's eName (stable), resolved to its current eVault via the registry" }, + "createdAt": { "type": "string", "format": "date-time", "description": "Creation timestamp, ISO 8601" }, + "updatedAt": { "type": "string", "format": "date-time", "description": "Last update timestamp, ISO 8601" }, + "isArchived": { "type": "boolean", "description": "Soft-delete flag" } + }, + "required": ["id", "title", "text", "language", "location", "sourceType", "createdAt"], + "additionalProperties": false +} diff --git a/services/ontology/schemas/travelStoryPreferences.json b/services/ontology/schemas/travelStoryPreferences.json new file mode 100644 index 000000000..39185ca75 --- /dev/null +++ b/services/ontology/schemas/travelStoryPreferences.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "schemaId": "5a27dd19-8c2a-4b55-9be5-c7fae8e74530", + "title": "TravelStoryPreferences", + "domain": "travel", + "type": "object", + "description": "A person's taste profile for travel STORIES specifically: content languages, topics, depth and narration style, learned affinity to content providers. One envelope per person, updated in place. Deliberately narrow: general travel preferences (hotels, transport) do not belong here, and a person's free-form notes about plans and wishes belong in PersonalNote records, never in this envelope. The profile language of the person lives in User.inLanguage; contentLanguages here is the (possibly different) priority list for story content", + "properties": { + "id": { "type": "string", "format": "uuid", "description": "Unique identifier; one envelope per person" }, + "ownerEName": { "type": "string", "description": "@-prefixed eName of the person these preferences belong to" }, + "contentLanguages": { "type": "array", "items": { "type": "string" }, "description": "Languages the person accepts story content in, priority order, BCP-47. Distinct from User.inLanguage (profile/interface language)" }, + "interests": { "type": "array", "items": { "type": "string" }, "description": "Story topics the person enjoys, e.g. 'architecture', 'food', 'street-art', 'history'" }, + "avoidTopics": { "type": "array", "items": { "type": "string" }, "description": "Story topics the person prefers to skip" }, + "storyDepth": { "type": "string", "enum": ["short", "medium", "deep"], "description": "Preferred story length/detail level" }, + "narrationStyle": { "type": "string", "description": "Free-form description of preferred narration style, e.g. 'ironic, no pathos'" }, + "providerAffinity": { + "type": "array", + "description": "Learned per-person reputation of tmatic content providers", + "items": { + "type": "object", + "properties": { + "spaceId": { "type": "string", "format": "uuid", "description": "tmatic space (content provider) UUID" }, + "score": { "type": "number", "minimum": -1, "maximum": 1, "description": "Affinity from -1 (avoid) to 1 (prefer)" } + }, + "required": ["spaceId", "score"], + "additionalProperties": false + } + }, + "createdAt": { "type": "string", "format": "date-time", "description": "Creation timestamp, ISO 8601" }, + "updatedAt": { "type": "string", "format": "date-time", "description": "Last update timestamp, ISO 8601" } + }, + "required": ["id", "ownerEName", "createdAt"], + "additionalProperties": false +}