Compare commits

...

1 Commits

Author SHA1 Message Date
Admin
87c541b178 fix: text-gen chapter-names truncation and bad title format
All checks were successful
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 43s
Release / Docker / caddy (push) Successful in 51s
Release / Docker / backend (push) Successful in 2m45s
Release / Docker / runner (push) Successful in 2m50s
Release / Docker / ui (push) Successful in 2m14s
Release / Gitea Release (push) Successful in 44s
- Default max_tokens to 4096 for chapter-names so large chapter lists
  are not cut off mid-JSON by the model's token limit
- Rewrite system prompt to clarify placeholder semantics ({n} = number,
  {scene} = scene hint) and explicitly forbid echoing the number inside
  the title field — prevents "Chapter 1 - 1: ..." style duplications
- UI: surface raw_response in the error area when chapters:[] is returned
  so the admin can see what the model actually produced
2026-04-04 13:43:29 +05:00
2 changed files with 27 additions and 8 deletions

View File

@@ -99,12 +99,17 @@ func (s *Server) handleAdminTextGenChapterNames(w http.ResponseWriter, r *http.R
}
systemPrompt := `You are a chapter title editor for a web novel platform. ` +
`The user will provide a list of chapter numbers with their current titles, ` +
`and a naming pattern. Your task is to produce a renamed version of every chapter ` +
`following the pattern exactly. ` +
`Respond ONLY with a JSON array — no prose, no markdown fences, no explanation. ` +
`Each element must be an object: {"number": <int>, "title": <string>}. ` +
`Output every chapter in the input list. Do not skip any.`
`The user provides a list of chapter numbers with their current titles, ` +
`and a naming pattern template. ` +
`Your job: produce one new title for every chapter, following the pattern exactly. ` +
`Pattern placeholders: {n} = the chapter number (integer), {scene} = a very short (25 word) scene hint derived from the existing title. ` +
`RULES: ` +
`1. Do NOT include the chapter number inside the title text — the {n} placeholder is already in the pattern. ` +
`2. Do NOT include any prefix like "Chapter X -" or "Chapter X:" inside the title field itself. ` +
`3. The "title" field in your JSON must be the fully-rendered string (e.g. if pattern is "Chapter {n}: {scene}", output "Chapter 3: The Bet"). ` +
`4. Respond ONLY with a raw JSON array — no prose, no markdown fences, no explanation. ` +
`5. Each element: {"number": <int>, "title": <string>}. ` +
`6. Output every chapter in the input list, in order. Do not skip any.`
userPrompt := fmt.Sprintf(
"Naming pattern: %s\n\nChapters:\n%s",
@@ -117,8 +122,14 @@ func (s *Server) handleAdminTextGenChapterNames(w http.ResponseWriter, r *http.R
model = cfai.DefaultTextModel
}
// Default to 4096 tokens so large chapter lists are not truncated.
maxTokens := req.MaxTokens
if maxTokens <= 0 {
maxTokens = 4096
}
s.deps.Log.Info("admin: text-gen chapter-names requested",
"slug", req.Slug, "chapters", len(chapters), "model", model)
"slug", req.Slug, "chapters", len(chapters), "model", model, "max_tokens", maxTokens)
raw, genErr := s.deps.TextGen.Generate(r.Context(), cfai.TextRequest{
Model: model,
@@ -126,7 +137,7 @@ func (s *Server) handleAdminTextGenChapterNames(w http.ResponseWriter, r *http.R
{Role: "system", Content: systemPrompt},
{Role: "user", Content: userPrompt},
},
MaxTokens: req.MaxTokens,
MaxTokens: maxTokens,
})
if genErr != nil {
s.deps.Log.Error("admin: text-gen chapter-names failed", "err", genErr)

View File

@@ -71,6 +71,11 @@
);
chRawResponse = body.raw_response ?? '';
chUsedModel = body.model ?? '';
// If backend returned chapters:[] but we have a raw response, the model
// output was unparseable (likely truncated). Treat it as an error.
if (chProposals.length === 0 && chRawResponse.trim().length > 0) {
chError = 'Model response could not be parsed (output may be truncated). Raw response shown below.';
}
} catch {
chError = 'Network error.';
} finally {
@@ -287,6 +292,9 @@
{#if chError}
<p class="text-sm text-(--color-danger) bg-(--color-danger)/10 rounded-lg px-3 py-2">{chError}</p>
{#if chRawResponse}
<pre class="text-xs bg-(--color-surface-2) border border-(--color-border) rounded-lg p-3 overflow-auto max-h-48 text-(--color-muted) whitespace-pre-wrap break-words">{chRawResponse}</pre>
{/if}
{/if}
</div>