Replace RSC streaming with /api/ai Route Handler for Docker reliability.

Server Actions + createStreamableValue kept failing in production; fetch-based text stream avoids RSC serialization issues and shows readable error messages.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 22:39:09 +08:00
parent 0f3bc2c50a
commit dba0245cb1
13 changed files with 342 additions and 384 deletions
+18 -24
View File
@@ -1,7 +1,6 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { readStreamableValue } from "ai/rsc";
import { BrainCircuit, ListRestart } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
@@ -15,9 +14,8 @@ import HexagramInput from "@/components/shared/hexagram-input";
import RegionSelect, {
useRegionLocation,
} from "@/components/shared/region-select";
import { getLiuyaoAnswer } from "@/app/actions/liuyao";
import { streamAiCompletion } from "@/lib/ai/client-stream";
import type { GuaResult } from "@/lib/calc/hexagram";
import { ERROR_PREFIX } from "@/lib/constant";
import todayJson from "@/lib/data/today.json";
const todayData: string[] = todayJson;
@@ -72,27 +70,23 @@ export default function LiuyaoForm() {
setShowAi(true);
try {
const stream = await getLiuyaoAnswer({
question,
calcDate,
calcTime,
locationName: location!.name,
longitude: location!.longitude,
guaMark: guaData!.result.guaMark,
guaTitle: guaData!.result.guaTitle,
guaResult: guaData!.result.guaResult,
guaChange: guaData!.result.guaChange,
});
let ret = "";
for await (const delta of readStreamableValue(stream)) {
if (typeof delta === "string" && delta.startsWith(ERROR_PREFIX)) {
setError(delta.slice(ERROR_PREFIX.length));
return;
}
ret += delta ?? "";
setCompletion(ret);
}
await streamAiCompletion(
{
mode: "liuyao",
payload: {
question,
calcDate,
calcTime,
locationName: location!.name,
longitude: location!.longitude,
guaMark: guaData!.result.guaMark,
guaTitle: guaData!.result.guaTitle,
guaResult: guaData!.result.guaResult,
guaChange: guaData!.result.guaChange,
},
},
setCompletion,
);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
} finally {