diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-02 21:47:12 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-02 21:47:12 +0300 |
| commit | 993b2efe63e221cee550756770894c9c58474d25 (patch) | |
| tree | 126b0dad9e1225c575f2521f29c6ad68556f4056 /internal/image/openai.go | |
| parent | dac35c77721c97f093a44d98164b38534452de9f (diff) | |
task 00g/00k/00h/008: gofmt, remove ProviderWithFallback, stdlib helpers, shared prompt
- task 00g: fix gofmt violations (trailing whitespace, missing newlines,
indentation) in 8 files; all pass gofmt -l now
- task 00k: remove unused ProviderWithFallback and its tests (YAGNI — no
production caller existed; voice-level fallback via RunWithVoiceFallbacks
already covers the real use case)
- task 00h: replace private splitLines/trimSpace/isSpace helpers in
internal/batch/processor.go with strings.Split+ReplaceAll and
strings.TrimSpace from the stdlib; remove the now-redundant tests
- task 008: extract buildEducationalPrompt into internal/image/prompt.go so
the prompt-assembly policy (scene truncation cascade, char limit) lives in
one place; both OpenAIClient and NanoBananaClient delegate to it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/image/openai.go')
| -rw-r--r-- | internal/image/openai.go | 74 |
1 files changed, 8 insertions, 66 deletions
diff --git a/internal/image/openai.go b/internal/image/openai.go index 8edb182..fdd88e4 100644 --- a/internal/image/openai.go +++ b/internal/image/openai.go @@ -243,10 +243,13 @@ func (c *OpenAIClient) SetPromptCallback(callback func(prompt string)) { c.PromptCallback = callback } -// createEducationalPrompt generates a prompt optimized for language learning +// createEducationalPrompt generates a prompt optimized for language learning. +// Scene generation and style selection are handled here; the shared +// buildEducationalPrompt helper assembles the actual prompt text so that the +// same policy is used by both OpenAIClient and NanoBananaClient. func (c *OpenAIClient) createEducationalPrompt(ctx context.Context, bulgarianWord, englishTranslation string) string { subject := promptSubject(englishTranslation, bulgarianWord) - // Generate a scene description for the word + scene, err := c.generateSceneDescription(ctx, bulgarianWord, englishTranslation) if err != nil { fmt.Printf(" Failed to generate scene: %v, using basic prompt\n", err) @@ -260,76 +263,15 @@ func (c *OpenAIClient) createEducationalPrompt(ctx context.Context, bulgarianWor } } - // Select a random style from the shared pool. Fall back to a generic style if - // the pool has been emptied by tests or future callers. + // Select a random style from the shared pool. Fall back to a generic style + // if the pool has been exhausted by tests or other callers. selectedStyle := chooseArtisticStyle() if selectedStyle == defaultArtisticStyle { fmt.Printf(" No artistic styles available, using generic prompt\n") } fmt.Printf(" Using image style: %s\n", selectedStyle) - // Define prompt components in order of importance - var prompt string - - if scene != "" { - // Full prompt with scene - fullPrompt := fmt.Sprintf( - "Generate a %s educational flashcard image illustrating \"%s\". Scene: %s "+ - "The image should be educational and suitable for language learning flashcards. "+ - "Requirements: The main subject or concept must be clearly visible, easily recognizable, and prominent in the image. It should occupy the central area with sharp focus and proper lighting. Ensure the scene makes \"%s\" immediately identifiable. "+ - "IMPORTANT: No text whatsoever. Do not include any words, letters, typography, labels, captions, or writing of any kind. Image only, without any text elements.", - selectedStyle, subject, withTerminalPunctuation(scene), subject, - ) - - // Check if full prompt exceeds 1000 characters - if len(fullPrompt) > maxImagePromptChars { - // Try without the IMPORTANT notice - prompt = fmt.Sprintf( - "Generate a %s flashcard image illustrating \"%s\". Scene: %s "+ - "The image should be educational and suitable for language learning flashcards. "+ - "Requirements: The main subject or concept must be clearly visible, centered, well lit, and easy to identify.", - selectedStyle, subject, withTerminalPunctuation(scene), - ) - - // If still too long, truncate the scene - if len(prompt) > maxImagePromptChars { - // Truncate scene to fit within limit - maxSceneLen := maxImagePromptChars - len(fmt.Sprintf( - "Generate a %s flashcard image illustrating \"%s\". Scene: "+ - "The image should be educational and suitable for language learning flashcards. "+ - "Requirements: The main subject or concept must be clearly visible, centered, well lit, and easy to identify.", - selectedStyle, subject, - )) - if maxSceneLen > 3 && len(scene) > maxSceneLen { - scene = scene[:maxSceneLen] + "..." - } - prompt = fmt.Sprintf( - "Generate a %s flashcard image illustrating \"%s\". Scene: %s "+ - "The image should be educational and suitable for language learning flashcards. "+ - "Requirements: The main subject or concept must be clearly visible, centered, well lit, and easy to identify.", - selectedStyle, subject, withTerminalPunctuation(scene), - ) - } - } else { - prompt = fullPrompt - } - } else { - // Basic prompt without scene - prompt = fmt.Sprintf( - "Generate a %s educational flashcard image illustrating \"%s\". %s "+ - "The image should be educational and suitable for language learning flashcards. "+ - "Requirements: The main subject or concept must be clearly visible, easily recognizable, and prominent in the image. Show it prominently centered with excellent lighting and sharp focus. "+ - "IMPORTANT: No text whatsoever. Do not include any words, letters, typography, labels, captions, or writing of any kind. Image only, without any text elements.", - selectedStyle, subject, fallbackVisualDirection(subject), - ) - } - - // Final check to ensure prompt is within 1000 characters - if len(prompt) > maxImagePromptChars { - prompt = prompt[:997] + "..." - } - - return prompt + return buildEducationalPrompt(selectedStyle, scene, subject) } // translateBulgarianToEnglish translates a Bulgarian word to English using OpenAI |
