Give reasoning models room; skip the advisor's self-contradictory ask

DeepSeek is a reasoning model — its chain-of-thought counts against the
completion budget. The 1024 max_tokens default let it burn the whole budget
"thinking" and get cut off (finish_reason: length) with an empty content, which
read as a parse failure. Raise DEFAULT_MAX_TOKENS to 4096 so it can finish
reasoning AND emit the JSON. Also return a clear error on empty responses.

The spiral was triggered by a contradictory prompt: the fight was already
"deadly" yet the advisor still asked the model to "add creatures to reach
Deadly." Skip the LLM when the target tier isn't actually harder than the
current one and let the deterministic path ease the over-tuned fight instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 00:14:49 +02:00
parent 53854deb81
commit 1b275ed5e6
2 changed files with 13 additions and 2 deletions
+7 -1
View File
@@ -1,7 +1,10 @@
import type { CompleteOptions, LlmConfig, LlmErrorKind, LlmResult } from './types';
const DEFAULT_TIMEOUT = 30_000;
const DEFAULT_MAX_TOKENS = 1024;
// Generous so reasoning models (e.g. DeepSeek, whose chain-of-thought counts
// against the completion budget) have room to finish thinking AND emit the JSON.
// A small cap truncates them mid-reasoning, leaving an empty `content`.
const DEFAULT_MAX_TOKENS = 4096;
function err<T>(error: LlmErrorKind, message: string): LlmResult<T> {
return { ok: false, error, message };
@@ -176,6 +179,9 @@ export async function complete<T>(cfg: LlmConfig, opts: CompleteOptions<T>): Pro
const text = extractText(cfg.provider, json);
if (!wantJson) return { ok: true, text };
if (text.trim() === '') {
return err('parse', 'The model returned an empty response — it likely ran out of output tokens before answering (raise max tokens, or the model spent its whole budget reasoning).');
}
const parsed = tryParseJson(text);
if (parsed === undefined) return err('parse', 'The model did not return valid JSON.');
const result = opts.schema!.safeParse(parsed);