kiku-agent 1.0.11 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +300 -495
- package/package.json +12 -11
- package/public/fonts/GoogleSans-Bold.ttf +0 -0
- package/public/fonts/GoogleSans-Bold.woff2 +0 -0
- package/public/fonts/GoogleSans-BoldItalic.ttf +0 -0
- package/public/fonts/GoogleSans-Italic.ttf +0 -0
- package/public/fonts/GoogleSans-Latin-400.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-500.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-600.woff2 +0 -0
- package/public/fonts/GoogleSans-Latin-700.woff2 +0 -0
- package/public/fonts/GoogleSans-Medium.ttf +0 -0
- package/public/fonts/GoogleSans-Medium.woff2 +0 -0
- package/public/fonts/GoogleSans-Regular.ttf +0 -0
- package/public/fonts/GoogleSans-Regular.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-400.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-500.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-600.woff2 +0 -0
- package/public/fonts/GoogleSans-Thai-700.woff2 +0 -0
- package/public/fonts/NotoSansThai-Bold.woff2 +0 -0
- package/public/fonts/NotoSansThai-Regular.woff2 +0 -0
- package/public/fonts/google-sans-official.css +900 -0
- package/public/images/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
- package/public/images/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
- package/public/images/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
- package/public/images/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/public/images/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
- package/public/images/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
- package/public/images/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
- package/public/images/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
- package/public/images/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-399915.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-424041.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-588166.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-672198.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-700522.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-839722.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026825-460586.jpg +0 -0
- package/public/images/douyin-update/thumbs//346/212/226/351/237/263202694-689407.jpg +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-399915.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-424041.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-588166.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-672198.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-700522.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026820-839722.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/2632026825-460586.mp4 +0 -0
- package/public/images/douyin-update//346/212/226/351/237/263202694-689407.mp4 +0 -0
- package/public/images/general/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
- package/public/images/general/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
- package/public/images/general/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
- package/public/images/general/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
- package/public/images/general/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
- package/public/images/general/jujingyi_portrait.jpg +0 -0
- package/public/images/jujingyi_portrait.jpg +0 -0
- package/public/images/series/a-millenniums-wait/poster.jpg +0 -0
- package/public/images/series/beauty-of-resilience/poster.jpg +0 -0
- package/public/images/series/detective-samoyeds/poster.jpg +0 -0
- package/public/images/series/everyday/poster.jpg +0 -0
- package/public/images/series/fu-rong-ancient-painting/poster.jpg +0 -0
- package/public/images/series/in-a-class-of-her-own/poster.jpg +0 -0
- package/public/images/series/in-blossom/poster.jpg +0 -0
- package/public/images/series/legend-of-yunxi/poster.jpg +0 -0
- package/public/images/series/lets-fight/poster-2.jpg +0 -0
- package/public/images/series/lets-fight/poster.jpg +0 -0
- package/public/images/series/love-emergency/poster.jpg +0 -0
- package/public/images/series/love-under-the-full-moon/poster.jpg +0 -0
- package/public/images/series/money-bastion/poster.jpg +0 -0
- package/public/images/series/mr-swimmer/poster.jpg +0 -0
- package/public/images/series/novoland-castle-in-the-sky/poster.jpg +0 -0
- package/public/images/series/please-give-me-a-pair-of-wings/poster.jpg +0 -0
- package/public/images/series/rebirth-for-you/poster.jpg +0 -0
- package/public/images/series/sword-and-fairy-4/poster.jpg +0 -0
- package/public/images/series/the-blooms-at-ruyi-pavilion/poster.jpg +0 -0
- package/public/images/series/the-eternal-fragrance/poster-2.jpg +0 -0
- package/public/images/series/the-eternal-fragrance/poster.jpg +0 -0
- package/public/images/series/the-fallen-flowers/poster.jpg +0 -0
- package/public/images/series/the-legend-of-white-snake/poster.jpg +0 -0
- package/public/images/series/veil-of-shadows/poster.jpg +0 -0
- package/public/images/series/wanhua-sin/poster-2.jpg +0 -0
- package/public/images/series/wanhua-sin/poster.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj10yogj62u03s0npe02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj2pdvyj61li25jkjl02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj71177j62672w9e8302.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj7twpfj62192197rw02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj8jfu8j62c03401kx02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj9dhdsj622k2l77wh02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkja31s4j62bw3407r802.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjcf331j61402ey1kx02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjdx8gqj62602pihdt02.jpg +0 -0
- package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjfvxnzj62432n57wh02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixniv2yj627y4fxb2b02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixsvbdaj625k2vfu1102.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixxxdaoj62c03501l202.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixzeef9j62c0340npd02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy0vs6tj626t4rbhdu02.jpg +0 -0
- package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy21wwuj62bt34hx6q02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly4wr85j62z629y7wh02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly67oilj623u2t5npd02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly9qtw8j62c0340u0x02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyem4m4j63402c01kz02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlygkoq4j627s2yeavd02.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyjh0daj62bw340nm102.jpg +0 -0
- package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlylejlhj62bw3401kx02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiaa8b7fj627j2y67wh02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiagw3ezj61s02ebu0x02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiaivng3j61rv2ebx6p02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiajuixvj62bw340qv502.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaiapyartj62bw340hdt02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaibkwu4ij61vy2iq1kx02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaic1fvkpj61u92eo1ky02.jpg +0 -0
- package/public/images/weibo-update/today/0040jbadgy1igaic3cruhj62822ywb2902.jpg +0 -0
- package/public/manifest.json +25 -0
- package/public/mcp-logo-128.png +0 -0
- package/public/mcp-logo-64.png +0 -0
- package/public/mcp-logo.svg +16 -0
- package/server/agent/agentCore.js +542 -74
- package/server/agent/appPaths.js +1 -0
- package/server/agent/audioPlayer.js +4 -2
- package/server/agent/citations.js +294 -0
- package/server/agent/clarificationAnalytics.js +477 -0
- package/server/agent/clarifyingQuestions.js +1089 -0
- package/server/agent/cli.js +549 -125
- package/server/agent/commandFeatures.js +1 -0
- package/server/agent/commandRegistry.js +946 -879
- package/server/agent/commands.js +546 -272
- package/server/agent/config.js +581 -412
- package/server/agent/contextCompactor.js +2 -2
- package/server/agent/debate.js +235 -0
- package/server/agent/eventBus.js +3 -0
- package/server/agent/feedbackLearning.js +217 -0
- package/server/agent/llmClient.js +512 -394
- package/server/agent/mcpClient.js +182 -32
- package/server/agent/memory.js +283 -224
- package/server/agent/modelManager.js +79 -39
- package/server/agent/profiles.js +178 -0
- package/server/agent/publicTools.js +22 -0
- package/server/agent/queryDetails.js +68 -8
- package/server/agent/quizData.js +398 -384
- package/server/agent/research.js +121 -79
- package/server/agent/sanitizer.js +104 -13
- package/server/agent/session.js +319 -11
- package/server/agent/subagent.js +8 -3
- package/server/agent/suggestionTui.js +294 -0
- package/server/agent/suggestions.js +69 -3
- package/server/agent/telemetry.js +119 -0
- package/server/agent/terminalImage.js +146 -27
- package/server/agent/theme.js +17 -0
- package/server/agent/toolRouter.js +16 -1
- package/server/agent/tools.js +424 -225
- package/server/agent/ui.js +486 -64
- package/server/agent/version.js +2 -4
- package/server/agent/webSearch.js +111 -11
- package/server/agent/youtubeAuth.js +24 -6
- package/server/apiRouter.js +4222 -0
- package/server/mcp/data/discography.js +25 -16
- package/server/mcp/data/gallery.js +308 -20
- package/server/mcp/data/profile.js +74 -44
- package/server/mcp/data/timeline.js +1 -1
- package/server/mcp/data/works.js +51 -17
- package/server/mcp/dataAdapter.js +5 -3
- package/server/mcp/handler.js +5 -11
- package/server/mcp/prompts.js +86 -0
- package/server/mcp/security.js +18 -15
- package/server/mcp/server.js +7 -2
- package/server/mcp/tools.js +66 -2
- package/server/web-chat/core/browserLauncher.js +18 -0
- package/server/web-chat/core/connection.js +194 -0
- package/server/web-chat/core/conversationContext.js +82 -0
- package/server/web-chat/core/directCatalogEngine.js +196 -0
- package/server/web-chat/core/dramaKnowledge.js +142 -0
- package/server/web-chat/core/errorHandler.js +51 -0
- package/server/web-chat/core/followUpEngine.js +182 -0
- package/server/web-chat/core/futureEventGuard.js +80 -0
- package/server/web-chat/core/memorySnapshot.js +12 -0
- package/server/web-chat/core/security.js +146 -0
- package/server/web-chat/core/toolInspector.js +52 -0
- package/server/web-chat/router.js +120 -0
- package/server/web-chat/routes/chatRoute.js +397 -0
- package/server/web-chat/routes/clarifyConfigRoute.js +98 -0
- package/server/web-chat/routes/clarifyRoute.js +83 -0
- package/server/web-chat/routes/compareRoute.js +79 -0
- package/server/web-chat/routes/exportRoute.js +59 -0
- package/server/web-chat/routes/feedbackRoute.js +57 -0
- package/server/web-chat/routes/newsRoute.js +33 -0
- package/server/web-chat/routes/quizRoute.js +11 -0
- package/server/web-chat/routes/staticRoute.js +103 -0
- package/server/web-chat/routes/statusRoute.js +52 -0
- package/server/web-chat/routes/suggestRoute.js +77 -0
- package/server/web-chat/routes/timelineRoute.js +34 -0
- package/server/web-chat/routes/verifyRoute.js +57 -0
- package/server/web-chat/server.js +116 -0
- package/server/web-chat/ui/htmlBuilder.js +91 -0
- package/server/web-chat/ui/previews/agentBubble.preview.html +134 -0
- package/server/web-chat/ui/scripts/agentBubbleSystem.js +320 -0
- package/server/web-chat/ui/scripts/client.js +8397 -0
- package/server/web-chat/ui/scripts/kikuDb.js +177 -0
- package/server/web-chat/ui/scripts/suggestionsData.js +118 -0
- package/server/web-chat/ui/scripts/webchatI18n.js +291 -0
- package/server/web-chat/ui/styles/main.css +8568 -0
- package/server/web-chat/ui/templates/body.html +796 -0
- package/server/webChatHtml.js +1 -0
- package/server/webChatServer.js +6 -0
- package/src/data/discographyData.js +892 -866
- package/src/data/galleryData.js +1805 -561
- package/src/data/profileData.js +79 -2
- package/src/data/socialUpdatesData.js +106 -47
- package/src/data/timelineData.js +1 -1
- package/src/data/worksData.js +25 -4
- package/tokens.css +97 -0
- package/server/agent/updateManager.js +0 -456
package/server/agent/appPaths.js
CHANGED
|
@@ -21,3 +21,4 @@ export const SESSION_DIR = path.join(KIKU_DATA_DIR, 'sessions');
|
|
|
21
21
|
export const EXPORT_DIR = path.join(KIKU_DATA_DIR, 'exports');
|
|
22
22
|
export const PROVIDER_KEYS_FILE = path.join(KIKU_DATA_DIR, 'provider_keys.json');
|
|
23
23
|
export const SETTINGS_FILE = path.join(KIKU_DATA_DIR, 'settings.json');
|
|
24
|
+
export const PROFILES_FILE = path.join(KIKU_DATA_DIR, 'profiles.json');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { existsSync, unlinkSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { join, dirname, basename } from 'node:path';
|
|
4
|
-
import { ensureYouTubeCookies, getCookieStatus } from './youtubeAuth.js';
|
|
4
|
+
import { ensureYouTubeCookies, getCookieStatus, isCookieFileValid } from './youtubeAuth.js';
|
|
5
5
|
|
|
6
6
|
let _ffmpeg = null;
|
|
7
7
|
async function loadFfmpeg() {
|
|
@@ -66,7 +66,9 @@ function buildStrategies(cookieResult) {
|
|
|
66
66
|
const strategies = [];
|
|
67
67
|
|
|
68
68
|
const status = getCookieStatus();
|
|
69
|
-
const cookiesFile = process.env.KIKU_YT_COOKIES
|
|
69
|
+
const cookiesFile = (process.env.KIKU_YT_COOKIES && isCookieFileValid(process.env.KIKU_YT_COOKIES))
|
|
70
|
+
? process.env.KIKU_YT_COOKIES
|
|
71
|
+
: (status.envCookies && isCookieFileValid(status.envCookies) ? status.envCookies : null);
|
|
70
72
|
if (cookiesFile) {
|
|
71
73
|
strategies.push({ cookies: cookiesFile });
|
|
72
74
|
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
function normalizeForMatch(str) {
|
|
2
|
+
if (!str || typeof str !== 'string') return '';
|
|
3
|
+
return str.toLowerCase().replace(/[\s\-_'"《》〈〉「」『』()()\[\]【】]/g, '');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function extractCitations(answerText = '', recordedTools = []) {
|
|
8
|
+
if (!Array.isArray(recordedTools) || recordedTools.length === 0) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const normAnswer = normalizeForMatch(answerText || '');
|
|
13
|
+
const candidateCitations = [];
|
|
14
|
+
const seenKeys = new Set();
|
|
15
|
+
|
|
16
|
+
for (const tool of recordedTools) {
|
|
17
|
+
if (!tool || !tool.result) continue;
|
|
18
|
+
const toolName = tool.name;
|
|
19
|
+
const res = tool.result;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if ((toolName === 'search_works' || toolName === 'get_work_detail') && Array.isArray(res.works || res.results)) {
|
|
23
|
+
const list = res.works || res.results;
|
|
24
|
+
for (const item of list) {
|
|
25
|
+
if (!item) continue;
|
|
26
|
+
const key = `work:${item.titleCn || item.titleTh || item.id}`;
|
|
27
|
+
if (seenKeys.has(key)) continue;
|
|
28
|
+
|
|
29
|
+
const titleTh = item.titleTh || '';
|
|
30
|
+
const titleCn = item.titleCn || '';
|
|
31
|
+
const titleEn = item.titleEn || '';
|
|
32
|
+
const year = item.year || item.releaseYear || '';
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const isMentioned = !normAnswer ||
|
|
36
|
+
(titleTh && normAnswer.includes(normalizeForMatch(titleTh))) ||
|
|
37
|
+
(titleCn && normAnswer.includes(normalizeForMatch(titleCn))) ||
|
|
38
|
+
(titleEn && normAnswer.includes(normalizeForMatch(titleEn))) ||
|
|
39
|
+
(item.role && normAnswer.includes(normalizeForMatch(item.role)));
|
|
40
|
+
|
|
41
|
+
if (isMentioned) {
|
|
42
|
+
seenKeys.add(key);
|
|
43
|
+
const displayTitle = [titleTh, titleCn ? `《${titleCn}》` : ''].filter(Boolean).join(' / ') || titleEn || 'ผลงานการแสดง';
|
|
44
|
+
candidateCitations.push({
|
|
45
|
+
id: 0,
|
|
46
|
+
type: 'work',
|
|
47
|
+
title: displayTitle,
|
|
48
|
+
category: item.category || 'ซีรีส์/ภาพยนตร์',
|
|
49
|
+
year: year ? Number(year) : null,
|
|
50
|
+
role: item.role || item.character || null,
|
|
51
|
+
source: `catalog:works (${toolName})`,
|
|
52
|
+
tool: toolName,
|
|
53
|
+
verified: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if ((toolName === 'search_discography' || toolName === 'get_song_detail') && Array.isArray(res.songs || res.results)) {
|
|
61
|
+
const list = res.songs || res.results;
|
|
62
|
+
for (const item of list) {
|
|
63
|
+
if (!item) continue;
|
|
64
|
+
const key = `song:${item.title || item.titleCn || item.id}`;
|
|
65
|
+
if (seenKeys.has(key)) continue;
|
|
66
|
+
|
|
67
|
+
const title = item.title || item.titleCn || '';
|
|
68
|
+
const titleTh = item.titleTh || '';
|
|
69
|
+
const isMentioned = !normAnswer ||
|
|
70
|
+
(title && normAnswer.includes(normalizeForMatch(title))) ||
|
|
71
|
+
(titleTh && normAnswer.includes(normalizeForMatch(titleTh)));
|
|
72
|
+
|
|
73
|
+
if (isMentioned) {
|
|
74
|
+
seenKeys.add(key);
|
|
75
|
+
const displayTitle = titleTh && titleTh !== title ? `${titleTh} (${title})` : title;
|
|
76
|
+
candidateCitations.push({
|
|
77
|
+
id: 0,
|
|
78
|
+
type: 'song',
|
|
79
|
+
title: displayTitle,
|
|
80
|
+
category: item.category || 'เพลง/OST',
|
|
81
|
+
album: item.album || null,
|
|
82
|
+
year: item.year || (item.releaseDate ? parseInt(item.releaseDate, 10) : null),
|
|
83
|
+
source: `catalog:discography (${toolName})`,
|
|
84
|
+
tool: toolName,
|
|
85
|
+
verified: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if (toolName === 'get_career_timeline' && Array.isArray(res.timeline || res.milestones || res.events)) {
|
|
93
|
+
const list = res.timeline || res.milestones || res.events;
|
|
94
|
+
for (const item of list.slice(0, 5)) {
|
|
95
|
+
if (!item) continue;
|
|
96
|
+
const key = `timeline:${item.year || ''}:${item.event || item.title || ''}`;
|
|
97
|
+
if (seenKeys.has(key)) continue;
|
|
98
|
+
|
|
99
|
+
const eventText = item.event || item.title || item.milestone || '';
|
|
100
|
+
const normEvent = normalizeForMatch(eventText);
|
|
101
|
+
const yearMentioned = item.year && normAnswer.includes(String(item.year));
|
|
102
|
+
const eventMentioned = !normAnswer || (normEvent && normAnswer.includes(normEvent));
|
|
103
|
+
const partialEventMentioned = normEvent && normEvent.length > 5 && normAnswer.includes(normEvent.slice(0, 8));
|
|
104
|
+
const isMentioned = eventMentioned || partialEventMentioned || (yearMentioned && normAnswer.length > 0);
|
|
105
|
+
|
|
106
|
+
if (isMentioned) {
|
|
107
|
+
seenKeys.add(key);
|
|
108
|
+
candidateCitations.push({
|
|
109
|
+
id: 0,
|
|
110
|
+
type: 'timeline',
|
|
111
|
+
title: eventText,
|
|
112
|
+
category: 'หมุดหมายเส้นทางอาชีพ',
|
|
113
|
+
year: item.year ? Number(item.year) : null,
|
|
114
|
+
source: `catalog:timeline (${toolName})`,
|
|
115
|
+
tool: toolName,
|
|
116
|
+
verified: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if (toolName === 'get_character_roles' && Array.isArray(res.roles || res.results)) {
|
|
124
|
+
const list = res.roles || res.results;
|
|
125
|
+
for (const item of list) {
|
|
126
|
+
if (!item) continue;
|
|
127
|
+
const key = `role:${item.characterName || item.name}:${item.workTitle || ''}`;
|
|
128
|
+
if (seenKeys.has(key)) continue;
|
|
129
|
+
|
|
130
|
+
const charName = item.characterName || item.name || '';
|
|
131
|
+
if (charName && (!normAnswer || normAnswer.includes(normalizeForMatch(charName)))) {
|
|
132
|
+
seenKeys.add(key);
|
|
133
|
+
candidateCitations.push({
|
|
134
|
+
id: 0,
|
|
135
|
+
type: 'character',
|
|
136
|
+
title: `${charName} (จากเรื่อง ${item.workTitle || item.work || ''})`,
|
|
137
|
+
category: 'บทบาทตัวละคร',
|
|
138
|
+
year: item.year ? Number(item.year) : null,
|
|
139
|
+
source: `catalog:roles (${toolName})`,
|
|
140
|
+
tool: toolName,
|
|
141
|
+
verified: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if (toolName === 'get_awards' && Array.isArray(res.awards || res.results)) {
|
|
149
|
+
const list = res.awards || res.results;
|
|
150
|
+
for (const item of list) {
|
|
151
|
+
if (!item) continue;
|
|
152
|
+
const key = `award:${item.award || item.title}:${item.year || ''}`;
|
|
153
|
+
if (seenKeys.has(key)) continue;
|
|
154
|
+
|
|
155
|
+
const awardName = item.award || item.title || '';
|
|
156
|
+
if (awardName && (!normAnswer || normAnswer.includes(normalizeForMatch(awardName)))) {
|
|
157
|
+
seenKeys.add(key);
|
|
158
|
+
candidateCitations.push({
|
|
159
|
+
id: 0,
|
|
160
|
+
type: 'award',
|
|
161
|
+
title: awardName,
|
|
162
|
+
category: 'รางวัล & เกียรติยศ',
|
|
163
|
+
year: item.year ? Number(item.year) : null,
|
|
164
|
+
source: `catalog:awards (${toolName})`,
|
|
165
|
+
tool: toolName,
|
|
166
|
+
verified: true,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if ((toolName === 'verify_claim' || toolName === 'debate_claim') && res.claim) {
|
|
174
|
+
const key = `verify:${res.claim}`;
|
|
175
|
+
if (!seenKeys.has(key)) {
|
|
176
|
+
seenKeys.add(key);
|
|
177
|
+
candidateCitations.push({
|
|
178
|
+
id: 0,
|
|
179
|
+
type: 'verification',
|
|
180
|
+
title: `การตรวจสอบข้อเท็จจริง: "${res.claim.slice(0, 60)}"`,
|
|
181
|
+
category: `ผลสรุป: ${res.verdict || res.status || 'Verified'}`,
|
|
182
|
+
source: `verifier:independent (${toolName})`,
|
|
183
|
+
tool: toolName,
|
|
184
|
+
verified: res.verdict === 'TRUE' || res.status === 'verified',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if (toolName === 'web_search' && Array.isArray(res.results)) {
|
|
191
|
+
for (const r of res.results) {
|
|
192
|
+
if (!r || !r.title) continue;
|
|
193
|
+
const key = `web:${r.url || r.title}`;
|
|
194
|
+
if (seenKeys.has(key)) continue;
|
|
195
|
+
seenKeys.add(key);
|
|
196
|
+
let hostname = 'Web Search';
|
|
197
|
+
try { if (r.url) hostname = new URL(r.url).hostname; } catch {}
|
|
198
|
+
candidateCitations.push({
|
|
199
|
+
id: 0,
|
|
200
|
+
type: 'web',
|
|
201
|
+
title: r.title,
|
|
202
|
+
url: r.url || null,
|
|
203
|
+
category: 'เว็บข่าวสาร / แหล่งข้อมูลภายนอก',
|
|
204
|
+
source: hostname,
|
|
205
|
+
tool: toolName,
|
|
206
|
+
verified: true,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
return candidateCitations.slice(0, 8).map((cit, idx) => ({
|
|
214
|
+
...cit,
|
|
215
|
+
id: idx + 1,
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Formats citations as a clean markdown/terminal reference list.
|
|
221
|
+
*
|
|
222
|
+
* @param {Array<Object>} citations
|
|
223
|
+
* @param {Object} [options]
|
|
224
|
+
* @returns {string}
|
|
225
|
+
*/
|
|
226
|
+
export function formatCitationsBlock(citations = [], { lang = 'th', plain = false } = {}) {
|
|
227
|
+
if (!Array.isArray(citations) || citations.length === 0) return '';
|
|
228
|
+
|
|
229
|
+
const header = lang === 'zh'
|
|
230
|
+
? '📖 参考来源与证据引用 (Sources & Citations):'
|
|
231
|
+
: lang === 'en'
|
|
232
|
+
? '📖 Sources & Evidence Citations:'
|
|
233
|
+
: '📖 แหล่งข้อมูลและหลักฐานอ้างอิง (Sources & Citations):';
|
|
234
|
+
|
|
235
|
+
const items = citations.map(c => {
|
|
236
|
+
const yearStr = c.year ? ` [${c.year}]` : '';
|
|
237
|
+
const catStr = c.category ? ` • ${c.category}` : '';
|
|
238
|
+
const srcStr = c.url ? ` • ${c.url}` : ` • ${c.source}`;
|
|
239
|
+
return `[${c.id}] ${c.title}${yearStr}${catStr}${srcStr}`;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return `${header}\n${items.join('\n')}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Resolves a citation reference like "[1]", "1", or an entity substring
|
|
247
|
+
* to the cited item from a previous conversation turn.
|
|
248
|
+
*
|
|
249
|
+
* @param {string} query - The user's query or reference string
|
|
250
|
+
* @param {Array<Object>} citations - Array of citation objects
|
|
251
|
+
* @returns {Object|null}
|
|
252
|
+
*/
|
|
253
|
+
export function resolveCitationReference(query = '', citations = []) {
|
|
254
|
+
if (!query || !Array.isArray(citations) || citations.length === 0) return null;
|
|
255
|
+
|
|
256
|
+
const trimmed = query.trim();
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
const bracketMatch = trimmed.match(/\[(\d+)\]/) || trimmed.match(/#(\d+)/) || trimmed.match(/^(\d+)$/);
|
|
260
|
+
if (bracketMatch) {
|
|
261
|
+
const targetId = parseInt(bracketMatch[1], 10);
|
|
262
|
+
return citations.find(c => c.id === targetId) || null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
const normQuery = normalizeForMatch(trimmed);
|
|
267
|
+
if (normQuery.length >= 2) {
|
|
268
|
+
for (const c of citations) {
|
|
269
|
+
const normTitle = normalizeForMatch(c.title || '');
|
|
270
|
+
if (normTitle && (normTitle.includes(normQuery) || normQuery.includes(normTitle))) {
|
|
271
|
+
return c;
|
|
272
|
+
}
|
|
273
|
+
const parts = String(c.title || '').split(/[\(\)《》\/]/).map(p => normalizeForMatch(p)).filter(p => p.length >= 2);
|
|
274
|
+
for (const p of parts) {
|
|
275
|
+
if (normQuery.includes(p) || p.includes(normQuery)) {
|
|
276
|
+
return c;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const stopWords = new Set(['เล่า', 'เรื่อง', 'ย่อ', 'ของ', 'ใน', 'ให้', 'ฟัง', 'หน่อย', 'ครับ', 'ค่ะ', 'นะ', 'มี', 'กี่', 'ตอน', 'คือ', 'ใคร', 'ปี', 'ไหน', 'tell', 'about', 'more', 'what', 'when', 'who', 'show', 'info']);
|
|
282
|
+
const words = trimmed.split(/[\s,.;!?]+/).map(w => normalizeForMatch(w)).filter(w => w.length >= 2 && !stopWords.has(w));
|
|
283
|
+
for (const word of words) {
|
|
284
|
+
for (const c of citations) {
|
|
285
|
+
const normTitle = normalizeForMatch(c.title || '');
|
|
286
|
+
if (normTitle.includes(word)) {
|
|
287
|
+
return c;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return null;
|
|
294
|
+
}
|