package glossary import ( "encoding/json" "fmt" "gitea.maximumdirect.net/eric/audita/internal/core/schema" "gitea.maximumdirect.net/eric/audita/internal/framework/contracts" "gitea.maximumdirect.net/eric/audita/internal/framework/promptcontext" ) type promptSegment struct { ID int `json:"id"` Speaker string `json:"speaker"` Start float64 `json:"start"` End float64 `json:"end"` Text string `json:"text"` Categories []string `json:"categories,omitempty"` } type promptTranscriptSection struct { SectionIndex int `json:"section_index"` Segments []promptSegment `json:"segments"` } func BuildProposalMessages(transcript *schema.Transcript, glossary *schema.Glossary, sectionIndex int, transcriptDescription string) ([]contracts.LLMMessage, error) { glossaryJSON, err := json.MarshalIndent(glossary, "", " ") if err != nil { return nil, fmt.Errorf("marshal glossary prompt context: %w", err) } sectionPayload := promptTranscriptSection{ SectionIndex: sectionIndex, Segments: make([]promptSegment, 0), } if transcript != nil { for _, s := range transcript.Segments { sectionPayload.Segments = append(sectionPayload.Segments, promptSegment{ ID: s.ID, Speaker: s.Speaker, Start: s.Start, End: s.End, Text: s.Text, Categories: append([]string(nil), s.Categories...), }) } } sectionJSON, err := json.MarshalIndent(sectionPayload, "", " ") if err != nil { return nil, fmt.Errorf("marshal transcript prompt context: %w", err) } system := "You are Audita, a careful transcript correction assistant. Identify only transcription errors that are strongly supported by the glossary. A valid correction must be acoustically plausible: the original transcript text should sound similar to the proposed correction when spoken aloud. Do not make generic grammar, spelling, capitalization, style, or filler-word edits. Do not substitute an unrelated glossary term just because it could fit the topic. Preserve speaker names, timestamps, and meaning." user := "Review this transcript section and return only glossary-supported corrections that should be applied.\n\n" + "Rules:\n" + "- Correct domain-specific names, aliases, jargon, deities, locations, NPCs, players, factions, and similar terms only when both the glossary and surrounding transcript context support the correction.\n" + "- The correction must plausibly fix a transcription error: the original words should be phonetically or acoustically similar to the corrected words in spoken English.\n" + "- Appropriate example: correcting \"gestures\" to \"Jesters\" can be valid if \"Jesters\" appears in the glossary and nearby context supports that inference.\n" + "- Inappropriate example: correcting \"Lyra\" to \"Jesters\" should be omitted because those words are not similar in spoken English, even if \"Jesters\" appears in the glossary.\n" + "- Do not replace one clear glossary term, character name, location, or ordinary word with a different glossary term unless it is a plausible mishearing.\n" + "- Treat glossary names and aliases already present in the transcript as protected spellings.\n" + "- Do not replace, Anglicize, normalize, lowercase, or otherwise alter protected glossary names or aliases away from their glossary spelling.\n" + "- Preserve canonical glossary capitalization for protected names and aliases, even if they look unusual.\n" + "- If a segment includes categories, treat them as additional transcript context.\n" + "- Plural forms of glossary names and aliases are allowed targets when spoken similarity and context support them, even if the plural is not explicitly listed in the glossary.\n" + "- Use the exact id from the input segment.\n" + "- For returned corrections, original_text must be only the exact text span that needs replacement, not the full segment text unless the whole segment is the replacement span.\n" + "- corrected_text must be only the replacement text for that span, not the full corrected segment text unless the whole segment is the replacement span.\n" + "- Each returned correction must contain only id, original_text, corrected_text, and confidence.\n" + "- Do not return corrections where original_text and corrected_text are identical.\n" + "- Do not return speaker, start, or end fields.\n" + "- Return only changed segments; do not return entries for unchanged segments.\n" + "- confidence must be between 0.0 and 1.0.\n" + "- If no corrections are needed, return an empty corrections list.\n\n" + promptcontext.TranscriptDescriptionBlock(transcriptDescription) + fmt.Sprintf("Glossary:\n%s\n\nTranscript section:\n%s", string(glossaryJSON), string(sectionJSON)) return []contracts.LLMMessage{ {Role: "system", Content: system}, {Role: "user", Content: user}, }, nil }