mirror of
https://github.com/AventurasTeam/Aventuras.git
synced 2026-04-26 10:51:24 +00:00
implemented final sdk migrations
This commit is contained in:
parent
9723dff31b
commit
ca504fad8e
5 changed files with 206 additions and 66 deletions
44
src/lib/services/ai/sdk/schemas/cardimport.ts
Normal file
44
src/lib/services/ai/sdk/schemas/cardimport.ts
Normal 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>;
|
||||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue