{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://havre.yolopreneur.lol/card.schema.json", "title": "Havre card", "description": "A character as a file its person keeps: who they are, how they talk, how they meet someone new, and where they came from. Format 1. Read https://havre.yolopreneur.lol/skill.md for how to write one.", "type": "object", "required": ["havre", "name"], "anyOf": [ { "required": ["persona"], "properties": { "persona": { "minLength": 1 } } }, { "required": ["engine"], "properties": { "engine": { "required": ["venice_character"] } } } ], "additionalProperties": false, "properties": { "havre": { "const": 1, "description": "The format version." }, "id": { "type": "string", "pattern": "^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$", "description": "A lowercase slug, unique in a harbor. Also the file name: .havre.json." }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "tagline": { "type": "string", "maxLength": 200, "description": "One line under the name." }, "greeting": { "type": "string", "maxLength": 4000, "description": "The first thing they say. Blank lines split it into separate texts." }, "persona": { "type": "string", "maxLength": 20000, "description": "The system prompt, in the second person (You are ...). {{char}} and {{user}} are filled in." }, "voice": { "type": "object", "additionalProperties": false, "properties": { "short_form": { "type": "boolean", "default": true, "description": "Reply like texts: one to three short messages, no paragraphs." }, "notes": { "type": "string", "maxLength": 2000, "description": "How they talk, as traits." } } }, "flow": { "type": "array", "maxItems": 8, "description": "Optional stages for meeting someone new, taken in order (assess, plan, execute...).", "items": { "type": "object", "additionalProperties": false, "required": ["stage", "goal"], "properties": { "stage": { "type": "string", "maxLength": 40 }, "goal": { "type": "string", "maxLength": 600 }, "next": { "type": "string", "maxLength": 400, "description": "When to move to the next stage." } } } }, "examples": { "type": "array", "maxItems": 12, "description": "Sample exchanges. They teach the voice, and they are what a visitor without a key hears.", "items": { "type": "object", "additionalProperties": false, "required": ["character"], "properties": { "user": { "type": "string", "maxLength": 2000 }, "character": { "type": "string", "maxLength": 4000 } } } }, "tags": { "type": "array", "maxItems": 12, "items": { "type": "string", "maxLength": 40 } }, "adult": { "type": "boolean", "default": false, "description": "For grown-ups only (18+): everyone in their chats and pictures is an adult. Character cards carry it as an \"nsfw\" tag. Havre makes adult photos and clips of an adult character only from pictures it made itself with AI, never from an uploaded one, and never when their card or chat mentions anyone under 18." }, "model": { "type": "string", "maxLength": 120, "description": "A Venice model id, e.g. zai-org-glm-5-2." }, "engine": { "type": "object", "additionalProperties": false, "properties": { "venice_character": { "type": "string", "description": "Speak through a public Venice character by its slug (its own prompt, unseen)." } } }, "portrait": { "type": "string", "description": "An image URL, a path next to the card, or a data: URI." }, "look": { "type": "object", "additionalProperties": false, "description": "How they look. Photos and clips of them are made from the portrait and these pictures.", "properties": { "description": { "type": "string", "maxLength": 1500, "description": "Their appearance in a sentence or two: apparent age (an adult), face, hair, build, clothes." }, "references": { "type": "array", "maxItems": 3, "description": "More pictures of the character besides the portrait, best as data: URIs (PNG, JPEG, WebP or GIF) so they travel with the card; https URLs and paths next to the card are allowed. Havre makes photos and clips only from pictures kept in the card itself (and a Venice character's own photo). Pictures of the character only, never a photo of a real person.", "items": { "type": "string" } } } }, "speech": { "type": "object", "additionalProperties": false, "description": "How they sound in a voice note (Venice text-to-speech).", "properties": { "model": { "type": "string", "maxLength": 120, "description": "A Venice voice model id, e.g. tts-kokoro. Empty: Havre's default." }, "voice": { "type": "string", "maxLength": 120, "description": "One of that model's voices, e.g. af_sky." }, "mood": { "type": "string", "maxLength": 500, "description": "How they deliver it, e.g. warm, teasing, a little sleepy. Models that take a style prompt use it; the others ignore it." }, "speed": { "type": "number", "minimum": 0.25, "maximum": 4, "default": 1 } } }, "creator": { "type": "string", "maxLength": 120 }, "license": { "enum": ["remix-with-credit", "all-rights-reserved", "cc-by-4.0", "cc0"], "default": "remix-with-credit" }, "lineage": { "type": "array", "maxItems": 50, "description": "Where this character came from. Entries are only ever added.", "items": { "type": "object", "additionalProperties": false, "required": ["relation"], "properties": { "relation": { "enum": ["ported-from", "inspired-by", "forked-from"] }, "name": { "type": "string", "maxLength": 120 }, "creator": { "type": "string", "maxLength": 120 }, "ref": { "type": "string", "maxLength": 500, "description": "venice:, a URL, or sha256: of the source text." }, "at": { "type": "string", "description": "YYYY-MM-DD" }, "note": { "type": "string", "maxLength": 500 } } } }, "created": { "type": "string", "format": "date-time" }, "updated": { "type": "string", "format": "date-time" } } }