feat(engine): auto-select art style via parallel LLM call

When user picks "自动", the client sends styleGuide="auto" to the
server. The orchestrator then runs a lightweight style-selector LLM
call in parallel with the Architect — both only depend on worldSetting,
so there is zero added latency. The selector picks the best-matching
preset from STYLE_MAP based on genre, mood, and setting.

Also moves STYLE_MAP from page.tsx to lib/options.ts so it can be
shared between client and server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yuanzonghao
2026-06-06 22:08:08 +08:00
parent 5fab22b9d5
commit 585f302908
4 changed files with 83 additions and 33 deletions
+16 -6
View File
@@ -14,6 +14,7 @@ import type {
} from "@infiplot/types";
import { coerceOrientation } from "@infiplot/types";
import { runArchitect } from "./agents/architect";
import { selectStyle } from "./agents/styleSelector";
import { directInsertBeat, directScene } from "./director";
import { synthesizeBeat } from "./voice";
import { interpret } from "./vision";
@@ -52,16 +53,25 @@ export async function startSession(
orientation: coerceOrientation(req.orientation),
};
// Stage 0 — Architect: expand the terse world/style prompt into a story
// bible BEFORE the first scene. Serial by necessity (the opening Writer
// reads session.storyState), but it gives the whole story a spine from beat
// one — the latency is offset by the director's portrait/voice overlap win.
// Stage 0 — Architect (+ optional auto style selection, in parallel).
// Both only depend on worldSetting, so they run concurrently.
console.log(
`[start] worldSetting (${session.worldSetting.length} chars):\n${session.worldSetting}`,
);
const isAutoStyle = session.styleGuide === "auto";
const tArchitect = Date.now();
session.storyState = await runArchitect(config.text, session);
tlog("[start] Architect", tArchitect);
const [architectResult, autoStyleGuide] = await Promise.all([
runArchitect(config.text, session),
isAutoStyle
? selectStyle(config.text, session.worldSetting)
: Promise.resolve(null),
]);
session.storyState = architectResult;
if (isAutoStyle && autoStyleGuide) {
session.styleGuide = autoStyleGuide;
console.log(`[start] auto-selected style: ${autoStyleGuide.slice(0, 60)}`);
}
tlog("[start] Architect" + (isAutoStyle ? " + StyleSelector" : ""), tArchitect);
console.log(
`[start] storyBible: logline="${session.storyState.logline}" | genreTags="${session.storyState.genreTags}" | synopsis="${session.storyState.synopsis}"`,
);