implemented final sdk migrations

This commit is contained in:
munimunigamer 2026-02-01 17:57:21 -06:00
parent 9723dff31b
commit ca504fad8e
5 changed files with 206 additions and 66 deletions

View file

@ -0,0 +1,44 @@
/**
* Card Import Schemas
*
* Zod schemas for SillyTavern character card import operations.
*/
import { z } from 'zod';
/**
* NPC extracted from character card.
* Maps to the character-card-import template output.
*/
export const cardImportNpcSchema = z.object({
name: z.string().describe("Character's actual name"),
role: z.string().describe("Character's role (ally, mentor, antagonist, love interest, guide, friend)"),
description: z.string().describe("1-2 sentences: who they are and key appearance details"),
personality: z.string().describe("Key personality traits as comma-separated list"),
relationship: z.string().describe("Their relationship to the protagonist"),
});
/**
* Result from character-card-import template.
* Cleans SillyTavern cards and converts to Aventura scenario settings.
*/
export const cardImportResultSchema = z.object({
primaryCharacterName: z.string().describe("The ACTUAL name of the main character that {{char}} refers to"),
settingSeed: z.string().describe("The FULL cleaned text with {{char}} replaced, {{user}} kept as-is"),
npcs: z.array(cardImportNpcSchema).describe("All significant characters from the card"),
});
/**
* Result from vault-character-import template.
* Extracts clean character data for the vault.
*/
export const vaultCharacterImportSchema = z.object({
name: z.string().describe("The character's actual name"),
description: z.string().describe("1-2 paragraphs describing who this character is"),
traits: z.array(z.string()).describe("3-8 personality traits"),
visualDescriptors: z.array(z.string()).describe("Physical appearance details for image generation"),
});
export type CardImportNpc = z.infer<typeof cardImportNpcSchema>;
export type CardImportResult = z.infer<typeof cardImportResultSchema>;
export type VaultCharacterImport = z.infer<typeof vaultCharacterImportSchema>;

View file

@ -120,3 +120,20 @@ export const finishLoreManagementSchema = z.object({
});
export type FinishLoreManagementSchema = z.infer<typeof finishLoreManagementSchema>;
/**
* Classification result for a single entry.
*/
export const entryClassificationSchema = z.object({
index: z.number().describe('Index of the entry being classified'),
type: entryTypeSchema.describe('The classified type for this entry'),
});
/**
* Result from lorebook-classifier template.
* Array of entry classifications.
*/
export const lorebookClassificationResultSchema = z.array(entryClassificationSchema);
export type EntryClassification = z.infer<typeof entryClassificationSchema>;
export type LorebookClassificationResult = z.infer<typeof lorebookClassificationResultSchema>;

View file

@ -3,15 +3,16 @@
*
* Imports SillyTavern character cards (V1/V2 JSON format) into Aventura's wizard.
* Supports both JSON files and PNG files with embedded character data.
*
* STATUS: PARTIALLY STUBBED - Awaiting SDK migration
* - PNG/JSON parsing: WORKING
* - LLM conversion: STUBBED
*/
import type { StoryMode } from '$lib/types';
import type { StoryMode, VisualDescriptors } from '$lib/types';
import type { Genre, GeneratedCharacter } from '$lib/services/ai/wizard/ScenarioService';
import { settings } from '$lib/stores/settings.svelte';
import { promptService, type PromptContext } from '$lib/services/prompts';
import { generateStructured } from './ai/sdk/generate';
import {
cardImportResultSchema,
vaultCharacterImportSchema,
} from './ai/sdk/schemas/cardimport';
import { createLogger } from './ai/core/config';
const log = createLogger('CharacterCardImporter');
@ -262,7 +263,6 @@ function buildCardContext(card: ParsedCard): string {
/**
* Convert a parsed character card into a scenario setting using LLM.
* @throws Error - LLM conversion not implemented during SDK migration
*/
export async function convertCardToScenario(
jsonString: string,
@ -290,20 +290,51 @@ export async function convertCardToScenario(
const preprocessedFirstMessage = normalizeUserMacro(card.firstMessage);
const preprocessedAlternateGreetings = card.alternateGreetings.map(g => normalizeUserMacro(g));
// Return a basic fallback without LLM conversion
const fallbackDescription = normalizeUserMacro(
[card.scenario, card.description].filter(s => s.trim()).join('\n\n')
);
// Build card content for LLM
const cardContent = buildCardContext(card);
// Minimal context for prompt rendering
const promptContext: PromptContext = {
mode,
pov: 'second',
tense: 'present',
protagonistName: '',
};
const system = promptService.renderPrompt('character-card-import', promptContext);
const prompt = promptService.renderUserPrompt('character-card-import', promptContext, {
genre,
title: cardTitle,
cardContent,
});
const result = await generateStructured({
presetId: 'classification',
schema: cardImportResultSchema,
system,
prompt,
});
// Convert LLM result to CardImportResult format
const npcs: GeneratedCharacter[] = result.npcs.map(npc => ({
name: npc.name,
role: npc.role,
description: npc.description,
relationship: npc.relationship,
traits: npc.personality.split(',').map(t => t.trim()).filter(Boolean),
}));
log('Card import successful', { primaryCharacter: result.primaryCharacterName, npcCount: npcs.length });
return {
success: false,
settingSeed: fallbackDescription.slice(0, 2000),
npcs: [],
primaryCharacterName: cardTitle,
storyTitle: cardTitle,
success: true,
settingSeed: result.settingSeed,
npcs,
primaryCharacterName: result.primaryCharacterName,
storyTitle: result.primaryCharacterName || cardTitle,
firstMessage: preprocessedFirstMessage,
alternateGreetings: preprocessedAlternateGreetings,
errors: ['LLM conversion not implemented - awaiting SDK migration. Basic extraction was used as fallback.'],
errors: [],
};
}
@ -311,12 +342,11 @@ export interface SanitizedCharacter {
name: string;
description: string;
traits: string[];
visualDescriptors: import('$lib/types').VisualDescriptors;
visualDescriptors: VisualDescriptors;
}
/**
* Sanitize a character card using LLM to extract clean character data.
* @throws Error - LLM sanitization not implemented during SDK migration
*/
export async function sanitizeCharacterCard(
jsonString: string,
@ -328,13 +358,41 @@ export async function sanitizeCharacterCard(
return null;
}
log('Sanitization not implemented - returning basic extraction');
// Build card content for LLM
const cardContent = buildCardContext(card);
// Minimal context for prompt rendering
const promptContext: PromptContext = {
mode: 'adventure',
pov: 'second',
tense: 'present',
protagonistName: '',
};
const system = promptService.renderPrompt('vault-character-import', promptContext);
const prompt = promptService.renderUserPrompt('vault-character-import', promptContext, {
cardContent,
});
const result = await generateStructured({
presetId: 'classification',
schema: vaultCharacterImportSchema,
system,
prompt,
});
log('Character sanitization successful', { name: result.name });
// Convert array of visual descriptors to VisualDescriptors object
const visualDescriptors: VisualDescriptors = {};
result.visualDescriptors.forEach((desc, i) => {
visualDescriptors[`descriptor_${i}`] = desc;
});
// Return basic extraction without LLM
return {
name: card.name,
description: card.description || card.personality || '',
traits: [],
visualDescriptors: {},
name: result.name,
description: result.description,
traits: result.traits,
visualDescriptors,
};
}

View file

@ -2,15 +2,13 @@
* Lorebook Importer Service
*
* Imports lorebooks from various formats (primarily SillyTavern) into Aventura's Entry system.
*
* STATUS: PARTIALLY STUBBED - Awaiting SDK migration
* - Parsing and basic type inference: WORKING
* - LLM classification: STUBBED
*/
import type { Entry, EntryType, EntryInjectionMode, EntryCreator } from '$lib/types';
import { settings } from '$lib/stores/settings.svelte';
import type { StoryMode } from '$lib/services/prompts';
import { promptService, type PromptContext } from '$lib/services/prompts';
import { generateStructured } from './ai/sdk/generate';
import { lorebookClassificationResultSchema } from './ai/sdk/schemas/lorebook';
import { createLogger } from './ai/core/config';
const log = createLogger('LorebookImporter');
@ -171,7 +169,7 @@ function inferEntryType(name: string, content: string): EntryType {
/**
* LLM-based entry type classification.
* @throws Error - LLM classification not implemented during SDK migration
* Classifies entries in batches to avoid token limits.
*/
export async function classifyEntriesWithLLM(
entries: ImportedEntry[],
@ -180,14 +178,67 @@ export async function classifyEntriesWithLLM(
): Promise<ImportedEntry[]> {
if (entries.length === 0) return entries;
log('LLM classification not implemented - using keyword-based inference');
const BATCH_SIZE = 20; // Process in batches to avoid token limits
const result = [...entries];
let classified = 0;
// Return entries as-is with keyword-based types (already set during parsing)
if (onProgress) {
onProgress(entries.length, entries.length);
// Minimal context for prompt rendering
const promptContext: PromptContext = {
mode,
pov: 'second',
tense: 'present',
protagonistName: '',
};
const system = promptService.renderPrompt('lorebook-classifier', promptContext);
// Process in batches
for (let i = 0; i < entries.length; i += BATCH_SIZE) {
const batch = entries.slice(i, i + BATCH_SIZE);
// Build entries JSON for the prompt
const entriesJson = JSON.stringify(
batch.map((entry, batchIndex) => ({
index: batchIndex,
name: entry.name,
content: entry.description.slice(0, 500), // Limit content length
keywords: entry.keywords.slice(0, 10),
})),
null,
2
);
const prompt = promptService.renderUserPrompt('lorebook-classifier', promptContext, {
entriesJson,
});
const classifications = await generateStructured({
presetId: 'classification',
schema: lorebookClassificationResultSchema,
system,
prompt,
});
// Apply classifications to batch
for (const classification of classifications) {
const globalIndex = i + classification.index;
if (globalIndex < result.length) {
result[globalIndex] = {
...result[globalIndex],
type: classification.type as EntryType,
};
}
}
classified += batch.length;
if (onProgress) {
onProgress(classified, entries.length);
}
log('Classified batch', { batch: i / BATCH_SIZE + 1, classified, total: entries.length });
}
return entries;
return result;
}
function determineInjectionMode(entry: SillyTavernEntry): EntryInjectionMode {

View file

@ -2746,34 +2746,6 @@ Respond with ONLY the translated text, no explanations.`,
userContent: `{{content}}`,
};
// ============================================================================
// AGENTIC RETRY PROMPTS
// ============================================================================
/**
* Prompt sent when the agentic retrieval agent doesn't make tool calls.
* Used to encourage the model to continue using tools or finish.
*/
const agenticRetrievalRetryTemplate: PromptTemplate = {
id: 'agentic-retrieval-retry',
name: 'Agentic Retrieval Retry',
category: 'service',
description: 'Prompt sent when agentic retrieval agent stops calling tools',
content: 'Please use the available tools to gather relevant context, or call finish_retrieval when you are done.',
};
/**
* Prompt sent when the lore management agent doesn't make tool calls.
* Used to encourage the model to continue using tools or finish.
*/
const loreManagementRetryTemplate: PromptTemplate = {
id: 'lore-management-retry',
name: 'Lore Management Retry',
category: 'service',
description: 'Prompt sent when lore management agent stops calling tools',
content: 'Please use the available tools to make any necessary changes, or call finish_lore_management if you are done reviewing the lorebook.',
};
// ============================================================================
// COMBINED PROMPT TEMPLATES
// ============================================================================
@ -2796,8 +2768,6 @@ export const PROMPT_TEMPLATES: PromptTemplate[] = [
loreManagementPromptTemplate,
interactiveLorebookPromptTemplate,
agenticRetrievalPromptTemplate,
agenticRetrievalRetryTemplate,
loreManagementRetryTemplate,
characterCardImportPromptTemplate,
vaultCharacterImportPromptTemplate,
imagePromptAnalysisTemplate,