Skip to content

feat: auto-infer course language from user input#384

Draft
cosarah wants to merge 9 commits intomainfrom
feat/language-inference
Draft

feat: auto-infer course language from user input#384
cosarah wants to merge 9 commits intomainfrom
feat/language-inference

Conversation

@cosarah
Copy link
Copy Markdown
Collaborator

@cosarah cosarah commented Apr 8, 2026

Summary

  • Replace the manual language selector (zh-CN / en-US toggle) with LLM-based automatic language inference
  • A standalone /api/generate/language-directive endpoint infers a natural-language directive from requirement text, PDF content, user profile, and app locale
  • The directive is consumed by agent profile generation, outline generation, and downstream content steps
  • Add LLM-as-judge eval test suite (17 test cases, local-only)

Changes

  • New: lib/generation/language-inference.ts — standalone inference function with dedicated prompt
  • New: lib/types/language-directive.ts — type definition
  • New: app/api/generate/language-directive/route.ts — API endpoint
  • New: tests/generation/language-inference.eval.test.ts — eval suite with LLM-as-judge
  • Modified: Remove manual language picker from generation toolbar
  • Modified: Inject {{languageDirective}} into outline prompt (replaces {{language}})
  • Modified: Propagate directive to agent profiles, interactive HTML, and Stage persistence
  • Modified: Use app i18n locale instead of navigator.language for fallback signals
  • Cleanup: Remove dead generationLanguage localStorage reads, orphaned i18n keys

Architecture

/api/generate/language-directive  (~1s, standalone LLM call)
  ↓ languageDirective (natural language string)
  ├→ /api/generate/agent-profiles  (language = directive)
  ├→ /api/generate/scene-outlines-stream  ({{languageDirective}} in prompt)
  └→ scene-content / scene-actions  (inherits via outline)

Test plan

  • pnpm vitest run tests/generation/language-inference.eval.test.ts — 17/17 passing with LLM-as-judge
  • Manual: generate course with Chinese requirement → verify Chinese output
  • Manual: generate course with English requirement → verify English output
  • Manual: generate course with Japanese requirement → verify Japanese output
  • Manual: generate course with short ambiguous input + non-default app locale → verify locale fallback works

Closes #381

🤖 Generated with Claude Code

cosarah and others added 3 commits April 8, 2026 21:59
Replace the manual language selector with LLM-based language inference.
A standalone `/api/generate/language-directive` endpoint infers a
natural-language directive from requirement text, PDF content, user
profile, and app locale. The directive is then consumed by agent profile
generation, outline generation, and downstream content steps.

- Add `LanguageDirective` type (natural-language string)
- Add standalone `inferLanguageDirective()` with dedicated prompt
- Add `/api/generate/language-directive` API endpoint
- Inject directive into outline prompt via `{{languageDirective}}`
- Remove manual language picker from generation toolbar
- Pass app locale (i18n) instead of navigator.language
- Add LLM-as-judge eval test suite (local only, 17 test cases)
- Store `languageDirective` in Stage for persistence

Closes #381

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Propagate languageDirective to SceneOutline and downstream scene
  generation (interactive HTML now receives the directive)
- Replace dead localStorage 'generationLanguage' reads in agent-bar
  with app locale from useI18n()
- Remove orphaned toolbar.languageHint i18n key from all 4 locales
- Update UserRequirements.language comment to reflect legacy status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add locale to useCallback deps in AgentVoicePill and TeacherVoicePill,
remove unused locale destructuring from AgentBar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cosarah cosarah marked this pull request as draft April 8, 2026 14:33
cosarah and others added 6 commits April 8, 2026 22:37
Eval tests (*.eval.test.ts) require real LLM API keys and are meant
for local-only execution. Exclude them from the default vitest glob
so CI doesn't fail on missing API credentials.

Run locally with: pnpm vitest run tests/generation/language-inference.eval.test.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The core issue: languageDirective was generated but not consumed by
most downstream steps, causing mixed-language output when UI locale
differed from requirement language.

Changes:
- Remove language parameter from formatImageDescription/Placeholder
  (use English uniformly for LLM-facing metadata)
- Inject {{languageDirective}} into all prompt templates: slide-content,
  slide-actions, quiz-content, quiz-actions, interactive-actions,
  interactive-html
- Pass outline.languageDirective through every buildPrompt call
- Update scene-content API to propagate languageDirective from stageInfo
- Update prompt-builder.ts to prefer languageDirective over language code
- Persist languageDirective in IndexedDB via StageRecord
- Remove hardcoded zh-CN/en-US branches from quiz-grade API
- Remove all requirements.language consumption in outline generation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The agent profile prompt received the full directive string in a
"Names and personas must be in language:" slot, which confused the
LLM. Change to inject it as a language directive that the LLM
interprets holistically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The streaming outline route and client were not propagating
languageDirective onto individual outline objects. This caused
downstream scene-content/actions/quiz generation to have no
language instruction, resulting in wrong-language output.

- Streaming route: attach languageDirective to each enriched outline
- Client: ensure languageDirective on every outline in done event

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Language is a course-level attribute, not per-outline. Remove
languageDirective from SceneOutline, store it only on Stage.
Pass it as a parameter through generateSceneContent and
generateSceneActions instead of reading from outline.

- Remove SceneOutline.languageDirective field
- Add languageDirective parameter to generateSceneContent/Actions
- scene-content API reads from stageInfo.languageDirective
- scene-actions API accepts languageDirective in request body
- Client passes stage.languageDirective to all API calls
- Server pipeline passes languageDirective through all steps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
apiSuccess spreads data into the top-level response object, so
the directive is at ldData.directive, not ldData.data.directive.
This caused languageDirective to always be undefined on the client,
making all downstream steps fall back to locale instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Infer course language from user input instead of manual selection

1 participant