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:
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { readStreamableValue } from "ai/rsc";
|
||||
import { BrainCircuit } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
@@ -12,8 +11,7 @@ import RegionSelect, {
|
||||
useRegionLocation,
|
||||
} from "@/components/shared/region-select";
|
||||
import { calculateBazi, type BaziChart } from "@/lib/calc/bazi";
|
||||
import { getBaziAnswer } from "@/app/actions/bazi";
|
||||
import { ERROR_PREFIX } from "@/lib/constant";
|
||||
import { streamAiCompletion } from "@/lib/ai/client-stream";
|
||||
|
||||
export default function BaziForm() {
|
||||
const [date, setDate] = useState(nowDateString());
|
||||
@@ -70,20 +68,17 @@ export default function BaziForm() {
|
||||
setIsLoading(true);
|
||||
setShowAi(true);
|
||||
try {
|
||||
const stream = await getBaziAnswer(
|
||||
input,
|
||||
question,
|
||||
location!.name,
|
||||
await streamAiCompletion(
|
||||
{
|
||||
mode: "bazi",
|
||||
payload: {
|
||||
input,
|
||||
question,
|
||||
birthPlaceName: location!.name,
|
||||
},
|
||||
},
|
||||
setCompletion,
|
||||
);
|
||||
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);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { readStreamableValue } from "ai/rsc";
|
||||
import { Compass } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
@@ -18,8 +17,7 @@ import RegionSelect, {
|
||||
} from "@/components/shared/region-select";
|
||||
import { calculateBazi, type BaziChart } from "@/lib/calc/bazi";
|
||||
import type { GuaResult } from "@/lib/calc/hexagram";
|
||||
import { getCombinedAnswer } from "@/app/actions/combined";
|
||||
import { ERROR_PREFIX } from "@/lib/constant";
|
||||
import { streamAiCompletion } from "@/lib/ai/client-stream";
|
||||
|
||||
export default function CombinedForm() {
|
||||
const [birthDate, setBirthDate] = useState("1990-01-01");
|
||||
@@ -109,39 +107,35 @@ export default function CombinedForm() {
|
||||
setShowAi(true);
|
||||
|
||||
try {
|
||||
const stream = await getCombinedAnswer({
|
||||
birth: {
|
||||
date: birthDate,
|
||||
time: unknownHour ? "12:00" : birthTime,
|
||||
gender,
|
||||
longitude: birthLocation!.longitude,
|
||||
unknownHour,
|
||||
await streamAiCompletion(
|
||||
{
|
||||
mode: "combined",
|
||||
payload: {
|
||||
birth: {
|
||||
date: birthDate,
|
||||
time: unknownHour ? "12:00" : birthTime,
|
||||
gender,
|
||||
longitude: birthLocation!.longitude,
|
||||
unknownHour,
|
||||
},
|
||||
birthPlaceName: birthLocation!.name,
|
||||
currentPlaceName: currentLocation!.name,
|
||||
currentLongitude: currentLocation!.longitude,
|
||||
calcDate,
|
||||
calcTime,
|
||||
question,
|
||||
hexagram: withHexagram && guaData
|
||||
? {
|
||||
guaMark: guaData.result.guaMark,
|
||||
guaTitle: guaData.result.guaTitle,
|
||||
guaResult: guaData.result.guaResult,
|
||||
guaChange: guaData.result.guaChange,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
birthPlaceName: birthLocation!.name,
|
||||
currentPlaceName: currentLocation!.name,
|
||||
currentLongitude: currentLocation!.longitude,
|
||||
calcDate,
|
||||
calcTime,
|
||||
question,
|
||||
hexagram: withHexagram && guaData
|
||||
? {
|
||||
guaMark: guaData.result.guaMark,
|
||||
guaTitle: guaData.result.guaTitle,
|
||||
guaResult: guaData.result.guaResult,
|
||||
guaChange: guaData.result.guaChange,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
setCompletion,
|
||||
);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
} finally {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user