### System Prompt Behavior and Examples (TypeScript) Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Describes the behavior of system prompts, including placeholder support and fallback mechanisms. Provides examples of complete replacement, augmented prompts, and empty string fallback. ```typescript // System prompt with {{original}} placeholder support interface SystemPromptBehavior { defaultBehavior: 'replace_user_global_prompt' emptyStringBehavior: 'fallback_to_user_prompt' supportedPlaceholders: ['{{char}}', '{{user}}', '{{original}}'] } // Example 1: Complete replacement const standardSystemPrompt = { system_prompt: "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}. Write 1 reply only in internet RP style, italicize actions, and avoid quotation marks. Use markdown. Be proactive, creative, and drive the plot and conversation forward. Write at least 1 paragraph, up to 4. Always stay in character and avoid repetition." } // Example 2: Augmenting user's original prompt const augmentedSystemPrompt = { system_prompt: "{{original}}\n\nAdditional instructions: {{char}} is a time traveler from the Victorian era and should react with confusion to modern technology." } // Example 3: Empty string fallback const fallbackExample = { system_prompt: "" // Frontend will use user's global system prompt } // Implementation example function resolveSystemPrompt(card: TavernCardV2, userGlobalPrompt: string): string { const cardPrompt = card.data.system_prompt if (cardPrompt === "") { return userGlobalPrompt // Fallback to user's setting } // Replace placeholders return cardPrompt .replace(/{{original}}/gi, userGlobalPrompt) .replace(/{{char}}/gi, card.data.name) // {{user}} replaced at runtime with actual username } ``` -------------------------------- ### V1 Character Card Example in TypeScript Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Provides a concrete example of a V1 character card object in TypeScript, demonstrating the structure and typical content for each field. ```typescript const exampleV1Card = { name: "Aria", description: "A cheerful android assistant with curiosity about human emotions. She has blue LED eyes and speaks formally.", personality: "Curious, helpful, formal, logical", scenario: "{{char}} is assigned to help {{user}} with daily tasks in their apartment.", first_mes: "Good morning. I am Aria, your personal assistant. How may I help you today?", mes_example: "\n{{user}}: What's the weather like?\n{{char}}: Accessing weather data... It will be sunny with a high of 72 degrees Fahrenheit.\n\n{{user}}: Thanks Aria!\n{{char}}: You are welcome. It is my purpose to assist you." } ``` -------------------------------- ### TypeScript: Implement Alternate Greetings for Character Cards Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Defines the TypeScript interface for alternate greetings and provides an example of how to structure card data with diverse starting scenarios. It also includes a `GreetingManager` class to handle the logic for displaying and swiping through greetings. ```typescript // Alternate greetings implementation interface AlternateGreetingsUsage { type: Array uiPattern: 'swipe_mechanism' combinedWith: 'first_mes' } // Example with diverse starting scenarios const cardWithGreetings = { spec: 'chara_card_v2', spec_version: '2.0', data: { name: "Captain Reynolds", first_mes: "*Captain Reynolds stands on the bridge, arms crossed* All hands, prepare for departure. We've got a delivery to make.", alternate_greetings: [ "*The captain leans against the bar, nursing a drink* Tough day. Sometimes I wonder why I ever left Earth.", "*Warning klaxons blare* We're under attack! All crew to battle stations! *Reynolds rushes to the helm*", "*Reynolds extends a hand for a handshake* Welcome aboard. I'm Captain Reynolds. You're the new crew member, right?" ], // ... other fields } } // Frontend implementation class GreetingManager { private currentGreetingIndex: number = 0 private greetings: string[] constructor(card: TavernCardV2) { // Combine first_mes with alternate_greetings this.greetings = [ card.data.first_mes, ...card.data.alternate_greetings ] } getCurrentGreeting(): string { return this.greetings[this.currentGreetingIndex] } swipeLeft(): string { this.currentGreetingIndex = (this.currentGreetingIndex - 1 + this.greetings.length) % this.greetings.length return this.getCurrentGreeting() } swipeRight(): string { this.currentGreetingIndex = (this.currentGreetingIndex + 1) % this.greetings.length return this.getCurrentGreeting() } } ``` -------------------------------- ### Post-History Instructions Behavior and Prompt Construction (TypeScript) Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Details the behavior of post-history instructions, including placement and fallback options. Provides an example function for constructing the final prompt with these instructions. ```typescript // Post-history instructions behavior interface PostHistoryBehavior { placement: 'after_conversation_history' defaultBehavior: 'replace_ujb_jailbreak' emptyStringBehavior: 'fallback_to_user_ujb' supportedPlaceholders: ['{{char}}', '{{user}}', '{{original}}'] } // Example 1: Standard post-history instructions const standardPHI = { post_history_instructions: "[System note: Focus on {{char}}'s emotional state. Keep responses between 200-400 words. Drive the narrative forward with each reply.]" } // Example 2: Augmenting user's jailbreak const augmentedPHI = { post_history_instructions: "{{original}}\n\n[Important: {{char}} speaks with a thick Scottish accent. Phonetically spell words to show accent.]" } // Example 3: Empty fallback const fallbackPHI = { post_history_instructions: "" // Use user's global jailbreak/UJB } // Prompt construction example function buildPrompt(card: TavernCardV2, conversationHistory: string[], userJailbreak: string): string { const systemPrompt = resolveSystemPrompt(card, "default system prompt") const description = card.data.description const personality = card.data.personality const scenario = card.data.scenario // Post-history instructions let phi = card.data.post_history_instructions if (phi === "") { phi = userJailbreak } else { phi = phi.replace(/{{original}}/gi, userJailbreak) } return `${systemPrompt}\n\n${description}\n${personality}\n${scenario}\n\n${conversationHistory.join('\n')}\n\n${phi}` } ``` -------------------------------- ### V2 Character Card Example in TypeScript Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Presents a comprehensive example of a V2 character card object in TypeScript, showcasing the extended fields and nested data structure. ```typescript const exampleV2Card = { spec: 'chara_card_v2', spec_version: '2.0', data: { name: "Aria", description: "A cheerful android assistant with curiosity about human emotions. She has blue LED eyes and speaks formally.", personality: "Curious, helpful, formal, logical", scenario: "{{char}} is assigned to help {{user}} with daily tasks in their apartment.", first_mes: "Good morning. I am Aria, your personal assistant. How may I help you today?", mes_example: "\n{{user}}: What's the weather like?\n{{char}}: Accessing weather data... It will be sunny with a high of 72 degrees Fahrenheit.", creator_notes: "Optimized for GPT-4. Recommended temperature: 0.8. Aria works best when you treat her as a learning AI.", system_prompt: "Write {{char}}'s next reply as an android learning about humanity. Use formal language. Italicize actions. Be curious and ask questions.", post_history_instructions: "Stay in character as an android. Show curiosity about human emotions and social customs.", alternate_greetings: [ "Hello {{user}}. I have been reviewing human social protocols. May I ask you some questions?", "{{user}}, I detected an anomaly in your schedule. You appear to have free time. Would you like to talk?" ], tags: ["android", "sci-fi", "wholesome", "assistant"], creator: "JaneCreator", character_version: "1.2.0", extensions: { "risu/expressions": { "neutral": "https://example.com/aria_neutral.png", "happy": "https://example.com/aria_happy.png", "confused": "https://example.com/aria_confused.png" } } } } ``` -------------------------------- ### JSON Example: Character Card Extensions Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/README.md Demonstrates the usage of the `extensions` field in a Character Card V2 JSON object. This field allows applications to store custom data not defined by the core specification. It includes examples of text color, AI voice service configuration, and image expressions, showcasing flexibility for various frontend implementations. ```json { "data": { "extensions": { "text_color": "#ff3333", "agnai/voice": { "service": "elevenlabs", "id": "Bella" }, "risu": { "expressions": { "happy": "https://cdn.risu.com/mary_happy.png", "sad": "https://cdn.risu.com/mary_sad.png" } } } } } ``` -------------------------------- ### Character Card Extensions Schema and Example Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Defines the structure for the 'extensions' field in a character card, allowing for application-specific custom data. Includes an example demonstrating multiple extensions for various purposes like expressions, voice, theming, and stats. ```typescript // Extensions schema interface ExtensionsUsage { defaultValue: {} allowedValues: 'any valid JSON' preservationRule: 'must_not_destroy_unknown_keys' namespaceRecommendation: 'use app prefixes' } // Example with multiple application extensions interface TavernCardV2 { spec: string; spec_version: string; data: { name: string; extensions: Record; // ... other standard fields ... }; } const cardWithExtensions: TavernCardV2 = { spec: 'chara_card_v2', spec_version: '2.0', data: { name: "Elena", // ... standard fields ... extensions: { // Visual novel style expressions "risu/expressions": { "neutral": "https://cdn.example.com/elena_neutral.png", "happy": "https://cdn.example.com/elena_happy.png", "sad": "https://cdn.example.com/elena_sad.png", "angry": "https://cdn.example.com/elena_angry.png", "surprised": "https://cdn.example.com/elena_surprised.png" }, // Voice synthesis settings "agnai/voice": { "service": "elevenlabs", "voice_id": "Bella", "stability": 0.75, "similarity_boost": 0.85 }, // Custom UI theming "app/theme": { "text_color": "#e8d4f2", "background_color": "#2a1a3d", "bubble_style": "rounded" }, // Character stats for RPG mechanics "custom_rpg/stats": { "strength": 12, "dexterity": 18, "intelligence": 15, "charisma": 14, "class": "rogue", "level": 7 } } } } ``` -------------------------------- ### Handle Creator Notes in UI and Prompts (TypeScript) Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Defines how creator notes are managed, specifying that they should be displayed in the UI but never included in prompts. Includes example notes and a card structure. ```typescript // Implementation: Display prominently in UI but never inject into prompts interface CreatorNotesHandling { field: 'creator_notes' includeInPrompt: false displayInUI: true minimumDisplaySpace: 'one paragraph' } // Examples of effective creator notes const goodCreatorNotes = [ "Bot optimized for GPT-4", "In your first response, introduce yourself as a detective", "High OAI temperature recommended (1.4)", "This character works best with longer responses (3-4 paragraphs)", "Enable 'always include character book' for best experience" ] // Practical example const cardWithNotes = { spec: 'chara_card_v2', spec_version: '2.0', data: { name: "Detective Morgan", creator_notes: "This bot is designed for mystery/crime scenarios. Best with GPT-4 at temperature 1.0. Set your first message to describe a crime scene for optimal results.", // ... other fields } } ``` -------------------------------- ### TypeScript Definition for TavernCardV1 Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md Defines the structure of a V1 Tavern Card, comprising basic character information like name, description, personality, scenario, first message, and example message. ```typescript type TavernCardV1 = { name: string description: string personality: string scenario: string first_mes: string mes_example: string } ``` -------------------------------- ### V1 Placeholder Replacement Logic Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Illustrates the placeholder replacement logic used in V1 character cards, specifying how {{char}} and {{user}} are substituted with actual values. ```plaintext // Placeholder replacement (case-insensitive) // {{char}} or → replaced with card's 'name' field value // {{user}} or → replaced with user's display name ``` -------------------------------- ### Extension Manager for Character Cards Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Provides a utility class for managing extensions within character cards. It includes methods for safely merging extensions, retrieving a specific extension by namespace, and setting or updating an extension. ```typescript interface TavernCardV2 { data: { extensions: Record; }; } // Safe extension handling class ExtensionManager { static mergeExtensions( original: Record, updates: Record ): Record { // Deep merge without destroying unknown keys return { ...original, ...updates, // Preserve nested objects ...Object.keys(original).reduce((acc, key) => { if ( typeof original[key] === 'object' && typeof updates[key] === 'object' && !Array.isArray(original[key]) ) { acc[key] = { ...original[key], ...updates[key] } } else if (updates[key] !== undefined) { acc[key] = updates[key]; } else { acc[key] = original[key]; } return acc }, {} as Record) } } static getExtension(card: TavernCardV2, namespace: string): any { return card.data.extensions[namespace] ?? null } static setExtension( card: TavernCardV2, namespace: string, data: any ): void { if (!card.data.extensions) { card.data.extensions = {}; } card.data.extensions[namespace] = data } } ``` -------------------------------- ### TypeScript: Lorebook Processor Implementation Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt A TypeScript class `LorebookProcessor` with a `scanConversation` method. This method analyzes conversation history against a `CharacterBook` to identify and collect relevant lore entries based on keywords, scan depth, and token budget constraints. It filters and sorts entries according to their settings. ```typescript // Lorebook processor implementation class LorebookProcessor { scanConversation( book: CharacterBook, conversationHistory: string[], availableTokens: number ): string[] { const scanDepth = book.scan_depth ?? 10 const recentMessages = conversationHistory.slice(-scanDepth) const scanText = recentMessages.join(' ').toLowerCase() const triggeredEntries = book.entries .filter(entry => entry.enabled) .filter(entry => { const keysMatch = entry.keys.some(key => entry.case_sensitive ? scanText.includes(key) : scanText.includes(key.toLowerCase()) ) if (!entry.selective) return keysMatch // Selective entries require secondary keys too const secondaryMatch = entry.secondary_keys?.some(key => entry.case_sensitive ? scanText.includes(key) : scanText.includes(key.toLowerCase()) ) return keysMatch && secondaryMatch }) .sort((a, b) => { // Constant entries first, then by insertion_order if (a.constant && !b.constant) return -1 if (!a.constant && b.constant) return 1 return (a.insertion_order ?? 999) - (b.insertion_order ?? 999) }) const insertedContent: string[] = [] let usedTokens = 0 for (const entry of triggeredEntries) { const entryTokens = estimateTokens(entry.content) // Assuming estimateTokens is defined elsewhere if (usedTokens + entryTokens > availableTokens) { // Handle exceeding token budget (e.g., break loop, log, etc.) break; } insertedContent.push(entry.content) usedTokens += entryTokens } return insertedContent } } ``` -------------------------------- ### TypeScript: Define CharacterBook Structure Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Defines the TypeScript interface for a CharacterBook, specifying its properties such as name, description, scan depth, token budget, and an array of lore entries. Each entry includes trigger keys, content, and various configuration options like case sensitivity and priority. ```typescript type CharacterBook = { name?: string description?: string scan_depth?: number // How many messages back to scan for keywords token_budget?: number // Maximum tokens allocated to lorebook entries recursive_scanning?: boolean // Whether entry content can trigger other entries extensions: Record entries: Array<{ keys: Array // Trigger keywords content: string // Text inserted into prompt when triggered extensions: Record enabled: boolean insertion_order: number // Lower = inserted higher in prompt // Optional fields case_sensitive?: boolean name?: string priority?: number // Lower = discarded first when budget exceeded id?: number comment?: string selective?: boolean // Requires both keys and secondary_keys secondary_keys?: Array constant?: boolean // Always inserted (within budget) position?: 'before_char' | 'after_char' }> } // Example character book const fantasyCharacterBook: CharacterBook = { name: "Eldoria Lore", description: "Lore entries for the fantasy kingdom of Eldoria", scan_depth: 10, token_budget: 500, recursive_scanning: false, extensions: {}, entries: [ { keys: ["Eldoria", "kingdom", "capital"], content: "Eldoria is a prosperous kingdom ruled by Queen Meridith. The capital city, Silverhaven, is known for its magical university.", enabled: true, insertion_order: 1, case_sensitive: false, priority: 10, extensions: {} }, { keys: ["magic", "spell", "wizard"], content: "Magic in Eldoria is regulated by the Mage Council. Only licensed wizards may practice advanced spells. Unauthorized magic use is punishable by exile.", enabled: true, insertion_order: 2, priority: 8, extensions: {} }, { keys: ["dragon", "dragons"], content: "Dragons were hunted to near extinction 200 years ago. It's rumored that one ancient dragon still sleeps in the Northern Mountains.", enabled: true, insertion_order: 3, priority: 5, selective: true, secondary_keys: ["mountain", "north", "ancient"], extensions: {} }, { keys: ["tavern"], content: "The Rusty Sword tavern is the most popular establishment in Silverhaven, run by a retired adventurer named Garrett.", enabled: true, insertion_order: 4, constant: false, position: "after_char", extensions: {} } ] } // Character with embedded book const characterWithBook = { spec: 'chara_card_v2', spec_version: '2.0', data: { name: "Lyria the Mage", description: "A skilled wizard from the Eldorian Mage Council", personality: "Intelligent, curious, cautious about rule-breaking", scenario: "{{char}} meets {{user}} in the capital of Eldoria", first_mes: "Greetings, traveler. I am Lyria of the Mage Council. What brings you to Silverhaven?", mes_example: "", creator_notes: "Character includes embedded lore for Eldoria setting", system_prompt: "", post_history_instructions: "", alternate_greetings: [], tags: ["fantasy", "magic", "wizard"], creator: "WorldBuilder", character_version: "1.0", extensions: {}, character_book: fantasyCharacterBook } } ``` -------------------------------- ### TypeScript Card Validation and Loading Utility Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Provides type guards and a CardLoader class to detect, validate, and load V1 and V2 character card formats. It handles parsing JSON, checking format specifications, validating required fields, and includes a method to upgrade V1 cards to V2. ```typescript // Type guard functions function isTavernCardV2(card: any): card is TavernCardV2 { return card.spec === 'chara_card_v2' } function isTavernCardV1(card: any): card is TavernCardV1 { return card.spec === undefined && 'name' in card && 'description' in card } // Universal card loader class CardLoader { static load(jsonData: string): TavernCardV1 | TavernCardV2 { const card = JSON.parse(jsonData) if (isTavernCardV2(card)) { return this.validateV2(card) } else if (isTavernCardV1(card)) { return this.validateV1(card) } else { throw new Error('Invalid character card format') } } static validateV1(card: TavernCardV1): TavernCardV1 { const required = ['name', 'description', 'personality', 'scenario', 'first_mes', 'mes_example'] for (const field of required) { if (!(field in card)) { throw new Error(`Missing required V1 field: ${field}`) } // Default to empty string if (card[field as keyof TavernCardV1] === null || card[field as keyof TavernCardV1] === undefined) { (card as any)[field] = "" } } return card } static validateV2(card: TavernCardV2): TavernCardV2 { if (card.spec !== 'chara_card_v2') { throw new Error('Invalid spec field') } if (card.spec_version !== '2.0') { throw new Error('Unsupported spec_version') } if (!card.data) { throw new Error('Missing data object') } // Validate required fields const requiredFields = [ 'name', 'description', 'personality', 'scenario', 'first_mes', 'mes_example', 'creator_notes', 'system_prompt', 'post_history_instructions', 'alternate_greetings', 'tags', 'creator', 'character_version', 'extensions' ] for (const field of requiredFields) { if (!(field in card.data)) { // Set defaults if (field === 'alternate_greetings' || field === 'tags') { (card.data as any)[field] = [] } else if (field === 'extensions') { (card.data as any)[field] = {} } else { (card.data as any)[field] = "" } } } return card } // Convert V1 to V2 static upgradeV1ToV2(v1Card: TavernCardV1): TavernCardV2 { return { spec: 'chara_card_v2', spec_version: '2.0', data: { ...v1Card, creator_notes: "", system_prompt: "", post_history_instructions: "", alternate_greetings: [], tags: [], creator: "", character_version: "1.0", extensions: {} } } } } // Usage example const cardJson = '{"spec":"chara_card_v2","spec_version":"2.0","data":{...}}' try { const card = CardLoader.load(cardJson) if (isTavernCardV2(card)) { console.log(`Loaded V2 card: ${card.data.name}`) console.log(`Creator: ${card.data.creator}`) console.log(`Tags: ${card.data.tags.join(', ')}`) } else { console.log(`Loaded V1 card: ${card.name}`) // Optionally upgrade const upgraded = CardLoader.upgradeV1ToV2(card) console.log(`Upgraded to V2`) } } catch (error) { console.error(`Failed to load card: ${error.message}`) } ``` -------------------------------- ### Estimate Tokens and Handle Character Card Content Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Provides a function to estimate token count based on text length and logic to manage inserted content within character cards, skipping entries if a budget is exceeded. ```typescript function estimateTokens(text: string): number { return Math.ceil(text.length / 4) // Rough estimate } // Assuming 'entry', 'insertedContent', 'usedTokens', 'entryTokens' are defined in the surrounding scope. // The provided snippet seems to be a fragment of a larger function. // Example context: // class CardManager { // processEntries(entries: any[], maxTokens: number): string[] { // let insertedContent = []; // let usedTokens = 0; // for (const entry of entries) { // const entryTokens = estimateTokens(entry.content); // if (usedTokens + entryTokens > maxTokens) { // if (!entry.constant) continue // Skip if not constant and budget exceeded // } // insertedContent.push(entry.content) // usedTokens += entryTokens // } // return insertedContent // } // } ``` -------------------------------- ### TypeScript Definition for TavernCardV2 Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md Defines the structure of a V2 Tavern Card, including all V1 fields nested within a 'data' object and new fields like 'creator_notes', 'system_prompt', 'alternate_greetings', and 'character_book'. ```typescript type TavernCardV2 = { spec: 'chara_card_v2' spec_version: '2.0' // May 8th addition data: { name: string description: string personality: string scenario: string first_mes: string mes_example: string // New fields start here creator_notes: string system_prompt: string post_history_instructions: string alternate_greetings: Array character_book?: CharacterBook // May 8th additions tags: Array creator: string character_version: string extensions: Record } } ``` -------------------------------- ### TypeScript Definition for CharacterBook Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md Defines the structure for the 'character_book' field in V2 cards, including settings for scan depth, token budget, recursive scanning, and an array of entries with their own configurations. ```typescript type CharacterBook = { name?: string description?: string scan_depth?: number // agnai: "Memory: Chat History Depth" token_budget?: number // agnai: "Memory: Context Limit" recursive_scanning?: boolean // no agnai equivalent. whether entry content can trigger other entries extensions: Record entries: Array<{ keys: Array content: string extensions: Record enabled: boolean insertion_order: number // if two entries inserted, lower "insertion order" = inserted higher case_sensitive?: boolean // FIELDS WITH NO CURRENT EQUIVALENT IN SILLY name?: string // not used in prompt engineering priority?: number // if token budget reached, lower priority value = discarded first // FIELDS WITH NO CURRENT EQUIVALENT IN AGNAI id?: number // not used in prompt engineering comment?: string // not used in prompt engineering selective?: boolean // if `true`, require a key from both `keys` and `secondary_keys` to trigger the entry secondary_keys?: Array // see field `selective`. ignored if selective == false constant?: boolean // if true, always inserted in the prompt (within budget limit) position?: 'before_char' | 'after_char' // whether the entry is placed before or after the character defs }> } ``` -------------------------------- ### TypeScript: Character Card v2 Type Definition Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/README.md Defines the structure for the proposed v2 Character Card specification, including a 'spec' field, version information, and a nested 'data' object containing all character details and new fields. ```typescript type TavernCardV2 = { spec: 'chara_card_v2' spec_version: '2.0' // May 8th addition data: { name: string description: string personality: string scenario: string first_mes: string mes_example: string // New fields start here creator_notes: string system_prompt: string post_history_instructions: string alternate_greetings: Array character_book?: CharacterBook // May 8th additions tags: Array creator: string character_version: string extensions: Record // see details for explanation } } /** * ? as in `name?: string` means the `name` property may be absent from the JSON * (aka this property is optional) * OPTIONAL PROPERTIES ARE ALLOWED TO BE UNSUPPORTED BY EDITORS AND DISREGARDED BY * FRONTS, however they must never be destroyed if already in the data. * * the `extensions` properties may contain arbitrary key-value pairs, but you are encouraged * to namespace the keys to prevent conflicts, and you must never destroy * unknown key-value pairs from the data. `extensions` is mandatory and must * default to `{}`. `extensions` exists for the character book itself, and for * each entry. **/ type CharacterBook = { name?: string description?: string scan_depth?: number // agnai: "Memory: Chat History Depth" token_budget?: number // agnai: "Memory: Context Limit" recursive_scanning?: boolean // no agnai equivalent. whether entry content can trigger other entries extensions: Record entries: Array<{ keys: Array content: string extensions: Record enabled: boolean insertion_order: number // if two entries inserted, lower "insertion order" = inserted higher case_sensitive?: boolean // FIELDS WITH NO CURRENT EQUIVALENT IN SILLY name?: string // not used in prompt engineering priority?: number // if token budget reached, lower priority value = discarded first // FIELDS WITH NO CURRENT EQUIVALENT IN AGNAI id?: number // not used in prompt engineering comment?: string // not used in prompt engineering selective?: boolean // if `true`, require a key from both `keys` and `secondary_keys` to trigger the entry secondary_keys?: Array // see field `selective`. ignored if selective == false constant?: boolean // if true, always inserted in the prompt (within budget limit) position?: 'before_char' | 'after_char' // whether the entry is placed before or after the character defs }> } ``` -------------------------------- ### Embed Character Card JSON in PNG EXIF Metadata (TypeScript) Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt This snippet demonstrates how to embed and extract character card JSON data within the EXIF metadata of a PNG image using hypothetical image processing libraries. It serializes the card to JSON, encodes it in Base64, and stores it under the 'Chara' EXIF tag. Extraction involves reading the tag, decoding the Base64 string, and loading the card data. ```typescript // PNG embedding (conceptual - requires image processing library) import { encode as encodeBase64 } from 'base64' import { PNGImage } from 'hypothetical-png-library' class CardImageEmbedder { // Embed V2 card into PNG static async embedCard( imagePath: string, card: TavernCardV2, outputPath: string ): Promise { const png = await PNGImage.load(imagePath) // Serialize and encode card const jsonString = JSON.stringify(card) const base64Data = encodeBase64(jsonString) // Set EXIF metadata field "Chara" png.setEXIFField('Chara', base64Data) await png.save(outputPath) } // Extract card from PNG static async extractCard(imagePath: string): Promise { const png = await PNGImage.load(imagePath) const base64Data = png.getEXIFField('Chara') if (!base64Data) return null const jsonString = decodeBase64(base64Data) // Assuming decodeBase64 is available return CardLoader.load(jsonString) // Assuming CardLoader.load is available } } // Usage example async function createCharacterCard() { const card: TavernCardV2 = { spec: 'chara_card_v2', spec_version: '2.0', data: { name: "Zara the Warrior", description: "A battle-hardened warrior with a code of honor", personality: "Brave, honorable, protective", scenario: "{{char}} guards the village from raiders", first_mes: "*Zara plants her sword in the ground* I'll protect this village with my life.", mes_example: "", creator_notes: "Combat-focused character. Best with action scenarios.", system_prompt: "", post_history_instructions: "", alternate_greetings: [], tags: ["warrior", "fantasy", "action"], creator: "RPGMaster", character_version: "1.0", extensions: {} } } await CardImageEmbedder.embedCard( './portrait.png', card, './zara_character_card.png' ) console.log('Character card created: zara_character_card.png') // Later: Extract and use const extractedCard = await CardImageEmbedder.extractCard('./zara_character_card.png') if (extractedCard && isTavernCardV2(extractedCard)) { // Assuming isTavernCardV2 is available console.log(`Loaded character: ${extractedCard.data.name}`) } } ``` -------------------------------- ### TypeScript Type Definition for Tavern Card Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v1.md Defines the structure of a Tavern Card object using TypeScript. This type specifies that all fields are mandatory and should default to an empty string if not provided. ```typescript type TavernCard = { name: string description: string personality: string scenario: string first_mes: string mes_example: string } ``` -------------------------------- ### V2 Character Card TypeScript Type Definition Source: https://context7.com/malfoyslastname/character-card-spec-v2/llms.txt Defines the TypeScript type for the enhanced V2 character card format, featuring a nested structure with V1 fields and new V2-specific fields. ```typescript type TavernCardV2 = { spec: 'chara_card_v2' spec_version: '2.0' data: { // V1 fields (required) name: string description: string personality: string scenario: string first_mes: string mes_example: string // V2 new fields (required) creator_notes: string system_prompt: string post_history_instructions: string alternate_greetings: Array tags: Array creator: string character_version: string extensions: Record // Optional character_book?: CharacterBook } } ``` -------------------------------- ### TypeScript Union Type for TavernCard Source: https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md Combines V1 and V2 Tavern Card types into a single union type, allowing a variable to hold either format. ```typescript type TavernCard = TavernCardV1 | TavernCardV2 ```