Phase 16D.2 - Character Discovery Confidence and Selection Refinement

This commit is contained in:
Nick Beckley 2026-06-27 20:27:44 +01:00
parent 7bee51834b
commit 1ee77d953d
3 changed files with 32 additions and 10 deletions

View File

@ -50,7 +50,8 @@ public sealed class WordCompanionService(
"Let", "Maybe", "Please", "God", "Something", "After", "Did", "Even", "All", "Nothing", "Have",
"Don", "Won", "Could", "Would", "Should", "Do", "Does", "Had", "Has", "Was", "Were", "Is", "Am",
"Right", "Really", "Now", "Look", "Listen", "Sorry", "Thanks", "Fine", "Great", "Sure", "Course",
"Thing", "Things", "Everyone", "Everything", "Anything", "Someone", "Nobody", "People"
"Thing", "Things", "Everyone", "Everything", "Anything", "Someone", "Nobody", "People",
"Still", "Err", "Er", "Erm", "Alright", "Allright", "Alrite"
};
private static readonly HashSet<string> CharacterDiscoveryTemporalWords = new(StringComparer.OrdinalIgnoreCase)
{
@ -72,6 +73,11 @@ public sealed class WordCompanionService(
"said", "asked", "replied", "answered", "whispered", "shouted", "called", "muttered", "cried", "snapped",
"sighed", "laughed", "continued", "began", "told", "yelled"
};
private static readonly HashSet<string> CharacterDiscoveryLocationIndicators = new(StringComparer.OrdinalIgnoreCase)
{
"Road", "Lane", "Street", "Close", "Avenue", "Drive", "Way", "Hill", "Hospital", "Inn", "Church", "School",
"House", "Farm", "Hall", "Court", "Station", "Hotel", "Shop", "Cafe", "Café", "Pub", "Park", "Gardens"
};
public async Task<WordCompanionProjectsResponse> ListProjectsAsync()
{
var userId = RequireUserId();
@ -431,6 +437,7 @@ public sealed class WordCompanionService(
var hasMultiWordName = words.Length > 1;
var hasNameLikeWord = words.Any(word => CharacterDiscoveryNameLikeWords.Contains(TrimHonorificPunctuation(word)));
var hasTemporalWord = words.Any(word => CharacterDiscoveryTemporalWords.Contains(TrimHonorificPunctuation(word)));
var hasLocationIndicator = words.Any(word => CharacterDiscoveryLocationIndicators.Contains(TrimHonorificPunctuation(word)));
var nonSentenceStartMentions = evidence.MentionCount - evidence.SentenceStartMentions;
var mostlySentenceStart = evidence.MentionCount > 0 && evidence.SentenceStartMentions >= Math.Ceiling(evidence.MentionCount * 0.8m);
@ -448,15 +455,15 @@ public sealed class WordCompanionService(
}
if (evidence.DialogueEvidenceCount > 0)
{
score += 3;
score += 5;
}
if (hasMultiWordName)
{
score += 2;
score += 5;
}
if (hasHonorific)
{
score += 2;
score += 4;
}
if (hasNameLikeWord)
{
@ -466,6 +473,10 @@ public sealed class WordCompanionService(
{
score -= 1;
}
if (hasLocationIndicator && evidence.DialogueEvidenceCount == 0 && !hasHonorific)
{
score -= 2;
}
if (mostlySentenceStart)
{
score -= 2;
@ -475,18 +486,23 @@ public sealed class WordCompanionService(
score -= 1;
}
if (score < 0)
if (score < 0 && !hasLocationIndicator)
{
return null;
}
var confidence = score >= 5 ? "High" : score >= 2 ? "Medium" : "Low";
var confidence = score >= 4 ? "High" : score >= 2 ? "Medium" : "Low";
if (hasLocationIndicator && evidence.DialogueEvidenceCount == 0 && !hasHonorific)
{
confidence = score >= 4 ? "Medium" : "Low";
}
return new WordCompanionCharacterCandidateDto
{
Text = evidence.Text,
MentionCount = evidence.MentionCount,
Confidence = confidence,
Reason = BuildCandidateReason(evidence, hasHonorific, hasMultiWordName, hasTemporalWord, mostlySentenceStart)
Reason = BuildCandidateReason(evidence, hasHonorific, hasMultiWordName, hasTemporalWord, hasLocationIndicator, mostlySentenceStart)
};
}
@ -495,6 +511,7 @@ public sealed class WordCompanionService(
bool hasHonorific,
bool hasMultiWordName,
bool hasTemporalWord,
bool hasLocationIndicator,
bool mostlySentenceStart)
{
if (evidence.DialogueEvidenceCount > 0)
@ -507,11 +524,16 @@ public sealed class WordCompanionService(
}
if (hasMultiWordName)
{
if (hasLocationIndicator)
{
return "Possible location";
}
return "Proper-name phrase";
}
if (hasTemporalWord)
{
return "Possible name";
return "Possible name or temporal word";
}
if (mostlySentenceStart)
{

View File

@ -1102,7 +1102,7 @@
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.value = candidate.text || "";
checkbox.checked = String(candidate.confidence || "").toLowerCase() !== "very low";
checkbox.checked = String(candidate.confidence || "").toLowerCase() === "high";
checkbox.addEventListener("change", updateFirstRunButtons);
const name = document.createElement("strong");

File diff suppressed because one or more lines are too long