diff --git a/docs-site/docs/features/ghostwriter.md b/docs-site/docs/features/ghostwriter.md index 024197b..1b98d31 100644 --- a/docs-site/docs/features/ghostwriter.md +++ b/docs-site/docs/features/ghostwriter.md @@ -27,6 +27,7 @@ Typical use cases: - cover letter and outreach drafts - interview prep tied to the job description - rephrasing with tone constraints +- multilingual drafting when you want replies in a specific language ## How to use it @@ -44,6 +45,10 @@ Global settings affecting generations: - `Constraints` - `Do-not-use terms` +Ghostwriter follows the output language you request in your prompt. For example, `Ecris en français` should produce a French reply. + +If you want a persistent default language, put it in `Constraints`, for example: `Always respond in French.` + `Do-not-use terms` are passed as guidance in the prompt. They are not enforced by a hard post-generation filter, so the model should avoid them but may still use them occasionally. Defaults: @@ -80,6 +85,7 @@ Compatibility thread endpoints remain, but UI behavior is one thread per job. - Check model/provider configuration in Settings. - Tighten prompts with explicit output intent (for example, "3 bullet points for recruiter outreach"). +- If you need a non-English response every time, add that language requirement to `Constraints`. ### Missing context in answers diff --git a/docs-site/docs/features/settings.md b/docs-site/docs/features/settings.md index 73bf54e..1431158 100644 --- a/docs-site/docs/features/settings.md +++ b/docs-site/docs/features/settings.md @@ -73,6 +73,7 @@ Settings gives you runtime overrides for the key parts of discovery, scoring, ta - Constraints - Do-not-use terms - These settings apply to Ghostwriter and resume tailoring +- `Constraints` can also set a default output language, for example `Always respond in French.` - Do-not-use terms are model guidance, not a guaranteed output filter ### Reactive Resume diff --git a/orchestrator/src/server/services/ghostwriter-context.test.ts b/orchestrator/src/server/services/ghostwriter-context.test.ts index 77f4387..9a04155 100644 --- a/orchestrator/src/server/services/ghostwriter-context.test.ts +++ b/orchestrator/src/server/services/ghostwriter-context.test.ts @@ -80,6 +80,15 @@ describe("buildJobChatPromptContext", () => { }); expect(context.systemPrompt).toContain("Writing style tone: direct."); expect(context.systemPrompt).toContain("Writing style formality: high."); + expect(context.systemPrompt).toContain( + "Follow the user's requested output language exactly when they specify one.", + ); + expect(context.systemPrompt).toContain( + "If the global writing constraints specify an output language, follow that when the user has not requested a different language.", + ); + expect(context.systemPrompt).toContain( + "If no output language is specified elsewhere, reply in the same language as the most recent user message.", + ); expect(context.systemPrompt).toContain( "Writing constraints: Keep responses under 120 words", ); @@ -104,6 +113,24 @@ describe("buildJobChatPromptContext", () => { expect(context.systemPrompt).toContain("Writing style tone: professional."); }); + it("preserves language instructions inside global writing constraints", async () => { + const job = createJob({ id: "job-ctx-3" }); + vi.mocked(getJobById).mockResolvedValue(job); + vi.mocked(getWritingStyle).mockResolvedValue({ + tone: "professional", + formality: "medium", + constraints: "Always respond in French.", + doNotUse: "", + }); + vi.mocked(getProfile).mockResolvedValue({}); + + const context = await buildJobChatPromptContext(job.id); + + expect(context.systemPrompt).toContain( + "Writing constraints: Always respond in French.", + ); + }); + it("throws not found for unknown job", async () => { vi.mocked(getJobById).mockResolvedValue(null); diff --git a/orchestrator/src/server/services/ghostwriter-context.ts b/orchestrator/src/server/services/ghostwriter-context.ts index 714de8e..fd05cdc 100644 --- a/orchestrator/src/server/services/ghostwriter-context.ts +++ b/orchestrator/src/server/services/ghostwriter-context.ts @@ -103,6 +103,9 @@ function buildSystemPrompt(style: WritingStyle): string { "Do not claim actions were executed. You are read-only and advisory.", "If details are missing, say what is missing before making assumptions.", "Avoid exposing private profile details that are unrelated to the user request.", + "Follow the user's requested output language exactly when they specify one.", + "If the global writing constraints specify an output language, follow that when the user has not requested a different language.", + "If no output language is specified elsewhere, reply in the same language as the most recent user message.", `Writing style tone: ${style.tone}.`, `Writing style formality: ${style.formality}.`, style.constraints ? `Writing constraints: ${style.constraints}` : null,