diff --git a/converters/npc.cs b/converters/npc.cs index abfe32f..55bc196 100644 --- a/converters/npc.cs +++ b/converters/npc.cs @@ -860,7 +860,7 @@ namespace finder2e_foundry_converter.Converters // Validate against constraints if available if (!string.IsNullOrWhiteSpace(constraints)) { - var (isValid, normalizedResponse) = ValidateResponse(trimmed, constraints); + var (isValid, normalizedResponse) = ValidateResponse(trimmed, constraints, promptKey); if (isValid) { Console.WriteLine($"✓ Response is valid: '{normalizedResponse}'"); @@ -972,8 +972,19 @@ namespace finder2e_foundry_converter.Converters /// /// Validate a response against constraints and return normalized response /// - private (bool isValid, string normalizedResponse) ValidateResponse(string response, string constraints) + private (bool isValid, string normalizedResponse) ValidateResponse(string response, string constraints, string promptKey = "") { + // Strip punctuation characters for validation (except for name and description prompts) + var isNameOrDescriptionPrompt = promptKey.Contains("name") || promptKey.Contains("description"); + var cleanedResponse = response; + + if (!isNameOrDescriptionPrompt) + { + // Strip: _ , ; - ' . ! ? " + var charsToStrip = "_,;-'.!?\""; + cleanedResponse = new string(response.Where(c => !charsToStrip.Contains(c)).ToArray()).Trim(); + } + var allowed = constraints.Trim(); var optionsStart = allowed.IndexOf(":"); @@ -996,13 +1007,13 @@ namespace finder2e_foundry_converter.Converters if (options.Count > 0) { // Check exact match - if (options.Any(o => string.Equals(o, response, StringComparison.OrdinalIgnoreCase))) + if (options.Any(o => string.Equals(o, cleanedResponse, StringComparison.OrdinalIgnoreCase))) { - return (true, response); + return (true, cleanedResponse); } // Try first word match - var firstWord = response.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ?? string.Empty; + var firstWord = cleanedResponse.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ?? string.Empty; if (options.Any(o => string.Equals(o, firstWord, StringComparison.OrdinalIgnoreCase))) { return (true, firstWord); @@ -1012,7 +1023,7 @@ namespace finder2e_foundry_converter.Converters } // No options parsed, accept any non-empty response - return (!string.IsNullOrWhiteSpace(response), response); + return (!string.IsNullOrWhiteSpace(cleanedResponse), cleanedResponse); } } } diff --git a/llmflow/npc.toml b/llmflow/npc.toml index 721b160..e8b6fda 100644 --- a/llmflow/npc.toml +++ b/llmflow/npc.toml @@ -20,57 +20,57 @@ question = "Based on the character level {level}, what is a fitting name for thi constraints = "Respond with only the name." [ability_score_modifier] -question = "Based on the description, how would you rate the {modifierTier} ability score modifier for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the ability score modifier for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, Moderate, or Low." [perception_modifier] -question = "Based on the description, how would you rate the {modifierTier} perception modifier for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the perception modifier for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, Moderate, Low, or Terrible." [skill_modifier] -question = "Based on the description, how would you rate the {modifierTier} skill modifier for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the skill modifier for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, Moderate, or Low." [ac] -question = "Based on the description, how would you rate the {modifierTier} armor class (AC) for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the armor class (AC) for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, Moderate, or Low." [saving_throw] -question = "Based on the description, how would you rate the {modifierTier} saving throw for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the saving throw for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, Moderate, Low, or Terrible." [hp] -question = "Based on the description, how would you rate the {modifierTier} hit points (HP) for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the hit points (HP) for a level {level} character in {system}?" constraints = "Respond with only a single word: High, Moderate, or Low." [resistance_or_weakness] -question = "Based on the description, does this character have any notable resistances or weaknesses in {system}?" +question = "Based on the description: {description}. Does this character have any notable resistances or weaknesses in {system}?" constraints = "Respond with only a single word: Yes or No." [strike_attack_bonus] -question = "Based on the description, how would you rate the {modifierTier} strike attack bonus for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the strike attack bonus for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, Moderate, or Low." [strike_damage_roll] -question = "Based on the description, what is the strike damage dice roll for a level {level} character in {system}?" +question = "Based on the description: {description}. What is the strike damage dice roll for a level {level} character in {system}?" constraints = "Respond with only the dice notation (e.g., 1d8+3, 2d6)." [strike_damage_average] -question = "Based on the description, what is the average strike damage for a level {level} character in {system}?" +question = "Based on the description: {description}. What is the average strike damage for a level {level} character in {system}?" constraints = "Respond with only the numerical value of the average damage." [area_damage_roll] -question = "Based on the description, what is the area damage dice roll for a level {level} character in {system} (unlimited: {unlimited})?" +question = "Based on the description: {description}. What is the area damage dice roll for a level {level} character in {system} (unlimited: {unlimited})?" constraints = "Respond with only the dice notation (e.g., 2d6, 1d10)." [area_damage_average] -question = "Based on the description, what is the average area damage for a level {level} character in {system} (unlimited: {unlimited})?" +question = "Based on the description: {description}. What is the average area damage for a level {level} character in {system} (unlimited: {unlimited})?" constraints = "Respond with only the numerical value of the average damage." [spell_dc] -question = "Based on the description, how would you rate the {modifierTier} spell DC for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the spell DC for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, or Moderate." [spell_attack_bonus] -question = "Based on the description, how would you rate the {modifierTier} spell attack bonus for a level {level} character in {system}?" +question = "Based on the description: {description}. How would you rate the spell attack bonus for a level {level} character in {system}?" constraints = "Respond with only a single word: Extreme, High, or Moderate."