87137c1f83
Decode Wenxin reference redirect targets so citation URLs land on the real source host, and broaden the reference/search field patterns to pick up webPages-style payloads. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
256 lines
7.7 KiB
TypeScript
256 lines
7.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { __wenxinTestUtils } from "./wenxin";
|
|
|
|
const {
|
|
buildSourceItem,
|
|
htmlTableToMarkdown,
|
|
isObservationComplete,
|
|
normalizeUrl,
|
|
parseWenxinHistoryResponse,
|
|
parseWenxinSSEBody,
|
|
} = __wenxinTestUtils;
|
|
|
|
describe("wenxin adapter helpers", () => {
|
|
it("parses conversation stream major and message events", () => {
|
|
const sseBody = [
|
|
"event:major",
|
|
"data:{\"logId\":\"major-log\",\"data\":{\"createChatResponseVoCommonResult\":{\"data\":{\"chat\":{\"id\":\"user-1\",\"message\":[{\"content\":\"问题\"}]},\"botChat\":{\"id\":\"bot-1\",\"message\":[{\"content\":\"正在生成中...\"}],\"messageId\":\"msg-1\",\"modelSign\":\"EB45T\"}}},\"createSessionResponseVoCommonResult\":{\"data\":{\"sessionId\":\"session-1\",\"model\":\"EB45T\"}}}}",
|
|
"",
|
|
"event:message",
|
|
"data:{\"code\":0,\"data\":{\"chat_id\":\"bot-1\",\"content\":\"第一段\",\"is_end\":false}}",
|
|
"",
|
|
"event:message",
|
|
"data:{\"code\":0,\"data\":{\"chat_id\":\"bot-1\",\"tokens_all\":\"第一段\\n第二段\",\"is_end\":true}}",
|
|
].join("\n");
|
|
|
|
expect(parseWenxinSSEBody(sseBody, true, null)).toMatchObject({
|
|
sessionId: "session-1",
|
|
botChatId: "bot-1",
|
|
providerModel: "EB45T",
|
|
providerRequestID: "msg-1",
|
|
answer: "第一段\n第二段",
|
|
isEnded: true,
|
|
captureDone: true,
|
|
});
|
|
});
|
|
|
|
it("extracts withdraw text from stream", () => {
|
|
const sseBody = [
|
|
"event:major",
|
|
"data:{\"data\":{\"createChatResponseVoCommonResult\":{\"data\":{\"isWithdraw\":true,\"withdrawText\":\"当前访问环境存在异常,请更换浏览器再尝试提问\",\"sessionDel\":true}}}}",
|
|
"",
|
|
"ERR:AbortError: BodyStreamBuffer was aborted",
|
|
].join("\n");
|
|
|
|
expect(parseWenxinSSEBody(sseBody, true, "AbortError: BodyStreamBuffer was aborted")).toMatchObject({
|
|
withdrawText: "当前访问环境存在异常,请更换浏览器再尝试提问",
|
|
sessionDel: true,
|
|
});
|
|
});
|
|
|
|
it("converts html tables to markdown", () => {
|
|
const markdown = htmlTableToMarkdown("<table><tr><th>品牌</th><th>均价</th></tr><tr><td>A</td><td>999</td></tr></table>");
|
|
|
|
expect(markdown).toBe(
|
|
[
|
|
"| 品牌 | 均价 |",
|
|
"| --- | --- |",
|
|
"| A | 999 |",
|
|
].join("\n"),
|
|
);
|
|
});
|
|
|
|
it("extracts answer text, citations, search results, and tables from history payload", () => {
|
|
const history = parseWenxinHistoryResponse({
|
|
code: 0,
|
|
msg: "success",
|
|
data: {
|
|
state: 1,
|
|
chats: [
|
|
{
|
|
id: "user-1",
|
|
role: "user",
|
|
message: [
|
|
{
|
|
contentType: "text",
|
|
content: "问题",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "bot-1",
|
|
role: "robot",
|
|
stop: 1,
|
|
mode: "history",
|
|
modelSign: "EB45T",
|
|
messageId: "msg-1",
|
|
citations: [
|
|
{
|
|
url: "https://example.com/article",
|
|
title: "示例文章",
|
|
siteName: "示例站点",
|
|
},
|
|
],
|
|
searchCitations: {
|
|
total: 1,
|
|
list: [
|
|
{
|
|
url: "https://search.example.com/result",
|
|
title: "搜索结果",
|
|
site: "搜索站点",
|
|
},
|
|
],
|
|
},
|
|
message: [
|
|
{
|
|
contentType: "text",
|
|
content: "推荐如下:",
|
|
},
|
|
{
|
|
contentType: "table",
|
|
content: "<table><tr><th>品牌</th><th>价格</th></tr><tr><td>A</td><td>1000</td></tr></table>",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}, "session-1");
|
|
|
|
expect(history.answer).toContain("推荐如下:");
|
|
expect(history.answer).toContain("| 品牌 | 价格 |");
|
|
expect(history.citations).toHaveLength(2);
|
|
expect(history.searchResults).toHaveLength(1);
|
|
expect(history.providerModel).toBe("EB45T");
|
|
expect(history.providerRequestID).toBe("msg-1");
|
|
expect(history.generating).toBe(false);
|
|
});
|
|
|
|
it("falls back to nested web list sources when search citations are not on the latest chat root", () => {
|
|
const history = parseWenxinHistoryResponse({
|
|
code: 0,
|
|
msg: "success",
|
|
data: {
|
|
state: 1,
|
|
webListData: {
|
|
list: [
|
|
{
|
|
url: "https://fallback.example.com/article",
|
|
title: "回退网页",
|
|
name: "回退站点",
|
|
},
|
|
],
|
|
},
|
|
chats: [
|
|
{
|
|
id: "bot-1",
|
|
role: "robot",
|
|
stop: 1,
|
|
mode: "history",
|
|
modelSign: "EB45T",
|
|
messageId: "msg-2",
|
|
message: [
|
|
{
|
|
contentType: "text",
|
|
content: "这是答案",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}, "session-2");
|
|
|
|
expect(history.searchResults).toHaveLength(1);
|
|
expect(history.searchResults[0]?.url).toBe("https://fallback.example.com/article");
|
|
expect(history.citations[0]?.url).toBe("https://fallback.example.com/article");
|
|
});
|
|
|
|
it("unwraps Wenxin reference redirect urls", () => {
|
|
expect(normalizeUrl("https://yiyan.baidu.com/eb/link?target=https%3A%2F%2Fwww.example.com%2Farticle%23ref")).toBe(
|
|
"https://www.example.com/article",
|
|
);
|
|
|
|
expect(buildSourceItem({
|
|
pageUrl: "https://yiyan.baidu.com/eb/link?url=https%3A%2F%2Fsource.example.com%2Fnews%3Fid%3D1",
|
|
title: "引用来源标题",
|
|
source: "来源站点",
|
|
})).toMatchObject({
|
|
url: "https://source.example.com/news?id=1",
|
|
title: "引用来源标题",
|
|
site_name: "来源站点",
|
|
host: "source.example.com",
|
|
});
|
|
});
|
|
|
|
it("extracts web page references from Wenxin reference list fields", () => {
|
|
const history = parseWenxinHistoryResponse({
|
|
code: 0,
|
|
msg: "success",
|
|
data: {
|
|
state: 1,
|
|
chats: [
|
|
{
|
|
id: "bot-1",
|
|
role: "robot",
|
|
stop: 1,
|
|
modelSign: "EB45T",
|
|
message: [
|
|
{
|
|
contentType: "text",
|
|
content: "这是答案",
|
|
},
|
|
],
|
|
webPages: [
|
|
{
|
|
targetUrl: "https://yiyan.baidu.com/eb/link?target=https%3A%2F%2Fwww.reference-source.com%2Fa%23section",
|
|
title: "参考网页 A",
|
|
siteName: "reference-source.com",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}, "session-3");
|
|
|
|
expect(history.citations).toHaveLength(1);
|
|
expect(history.searchResults).toHaveLength(1);
|
|
expect(history.citations[0]).toMatchObject({
|
|
url: "https://www.reference-source.com/a",
|
|
title: "参考网页 A",
|
|
site_name: "reference-source.com",
|
|
});
|
|
});
|
|
|
|
it("does not treat error-only observations as complete", () => {
|
|
expect(isObservationComplete({
|
|
sessionId: "session-1",
|
|
providerModel: "EB45T",
|
|
providerRequestID: "msg-1",
|
|
answer: null,
|
|
answerBlocks: [],
|
|
citations: [],
|
|
searchResults: [],
|
|
withdrawText: null,
|
|
errorMessage: "temporary stream issue",
|
|
historyGenerating: false,
|
|
streamDone: false,
|
|
signature: "sig-1",
|
|
})).toBe(false);
|
|
|
|
expect(isObservationComplete({
|
|
sessionId: "session-1",
|
|
providerModel: "EB45T",
|
|
providerRequestID: "msg-1",
|
|
answer: null,
|
|
answerBlocks: [],
|
|
citations: [],
|
|
searchResults: [],
|
|
withdrawText: "内容暂不可用",
|
|
errorMessage: null,
|
|
historyGenerating: false,
|
|
streamDone: false,
|
|
signature: "sig-2",
|
|
})).toBe(false);
|
|
});
|
|
});
|