feat: extract parseJSON()

This commit is contained in:
Li Xin 2025-04-22 11:02:18 +08:00
parent b36f36f530
commit 5ee1632489
5 changed files with 30 additions and 9 deletions

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { LoadingOutlined } from "@ant-design/icons"; import { LoadingOutlined } from "@ant-design/icons";
import { parse } from "best-effort-json-parser";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Download, Headphones } from "lucide-react"; import { Download, Headphones } from "lucide-react";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
@ -23,6 +22,7 @@ import {
useResearchTitle, useResearchTitle,
useStore, useStore,
} from "~/core/store"; } from "~/core/store";
import { parseJSON } from "~/core/utils";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
import { LoadingAnimation } from "./loading-animation"; import { LoadingAnimation } from "./loading-animation";
@ -281,7 +281,7 @@ function PlanCard({
thought?: string; thought?: string;
steps?: { title?: string; description?: string }[]; steps?: { title?: string; description?: string }[];
}>(() => { }>(() => {
return parse(message.content ?? ""); return parseJSON(message.content ?? "", {});
}, [message.content]); }, [message.content]);
const handleAccept = useCallback(async () => { const handleAccept = useCallback(async () => {
await sendMessage( await sendMessage(
@ -326,7 +326,7 @@ function PlanCard({
<CardFooter className="flex justify-end"> <CardFooter className="flex justify-end">
{!message.isStreaming && interruptMessage?.options?.length && ( {!message.isStreaming && interruptMessage?.options?.length && (
<motion.div <motion.div
className="flex gap-4" className="flex gap-2"
initial={{ opacity: 0, y: 12 }} initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.3 }} transition={{ duration: 0.3, delay: 0.3 }}
@ -364,7 +364,7 @@ function PodcastCard({
message: Message; message: Message;
}) { }) {
const data = useMemo(() => { const data = useMemo(() => {
return parse(message.content ?? ""); return JSON.parse(message.content ?? "");
}, [message.content]); }, [message.content]);
const title = useMemo<string | undefined>(() => data?.title, [data]); const title = useMemo<string | undefined>(() => data?.title, [data]);
const audioUrl = useMemo<string | undefined>(() => data?.audioUrl, [data]); const audioUrl = useMemo<string | undefined>(() => data?.audioUrl, [data]);

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { PythonOutlined } from "@ant-design/icons"; import { PythonOutlined } from "@ant-design/icons";
import { parse } from "best-effort-json-parser";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import { BookOpenText, Search } from "lucide-react"; import { BookOpenText, Search } from "lucide-react";
@ -13,6 +12,7 @@ import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs";
import { Skeleton } from "~/components/ui/skeleton"; import { Skeleton } from "~/components/ui/skeleton";
import type { ToolCallRuntime } from "~/core/messages"; import type { ToolCallRuntime } from "~/core/messages";
import { useMessage, useStore } from "~/core/store"; import { useMessage, useStore } from "~/core/store";
import { parseJSON } from "~/core/utils";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
import { FavIcon } from "./fav-icon"; import { FavIcon } from "./fav-icon";
@ -112,7 +112,7 @@ function WebSearchToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
const searchResults = useMemo<SearchResult[]>(() => { const searchResults = useMemo<SearchResult[]>(() => {
let results: SearchResult[] | undefined = undefined; let results: SearchResult[] | undefined = undefined;
try { try {
results = toolCall.result ? parse(toolCall.result) : undefined; results = toolCall.result ? parseJSON(toolCall.result, []) : undefined;
} catch { } catch {
results = undefined; results = undefined;
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { parse } from "best-effort-json-parser";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { create } from "zustand"; import { create } from "zustand";
@ -9,6 +8,7 @@ import { chatStream } from "../api";
import { generatePodcast } from "../api/podcast"; import { generatePodcast } from "../api/podcast";
import type { Message } from "../messages"; import type { Message } from "../messages";
import { mergeMessage } from "../messages"; import { mergeMessage } from "../messages";
import { parseJSON } from "../utils";
const THREAD_ID = nanoid(); const THREAD_ID = nanoid();
@ -230,7 +230,7 @@ export async function listenToPodcast(researchId: string) {
const reportMessageId = useStore.getState().researchReportIds.get(researchId); const reportMessageId = useStore.getState().researchReportIds.get(researchId);
if (planMessageId && reportMessageId) { if (planMessageId && reportMessageId) {
const planMessage = getMessage(planMessageId)!; const planMessage = getMessage(planMessageId)!;
const title = (JSON.parse(planMessage.content) as { title: string }).title; const title = parseJSON(planMessage.content, { title: "Untitled" }).title;
const reportMessage = getMessage(reportMessageId); const reportMessage = getMessage(reportMessageId);
if (reportMessage?.content) { if (reportMessage?.content) {
appendMessage({ appendMessage({
@ -269,7 +269,9 @@ export function useResearchTitle(researchId: string) {
const planMessage = useMessage( const planMessage = useMessage(
useStore.getState().researchPlanIds.get(researchId), useStore.getState().researchPlanIds.get(researchId),
); );
return planMessage ? parse(planMessage.content).title : undefined; return planMessage
? parseJSON(planMessage.content, { title: "" }).title
: undefined;
} }
export function useMessage(messageId: string | null | undefined) { export function useMessage(messageId: string | null | undefined) {

View File

@ -2,3 +2,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
export * from "./time"; export * from "./time";
export * from "./json";
export * from "./deep-clone";

View File

@ -0,0 +1,17 @@
import { parse } from "best-effort-json-parser";
export function parseJSON<T>(json: string | null | undefined, fallback: T) {
if (!json) {
return fallback;
}
try {
const raw = json
.trim()
.replace(/^```json\s*/, "")
.replace(/^```\s*/, "")
.replace(/\s*```$/, "");
return parse(raw) as T;
} catch {
return fallback;
}
}