kiku-agent 1.3.2 → 1.3.4

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.
Files changed (152) hide show
  1. package/README.md +69 -12
  2. package/package.json +2 -1
  3. package/server/agent/agentCore.js +319 -46
  4. package/server/agent/appPaths.js +31 -0
  5. package/server/agent/audioPlayer.js +339 -12
  6. package/server/agent/autoContext.js +193 -0
  7. package/server/agent/catalogEntityResolver.js +170 -28
  8. package/server/agent/clarifyingQuestions.js +137 -35
  9. package/server/agent/cli.js +177 -48
  10. package/server/agent/cloudAdapters.js +116 -0
  11. package/server/agent/commandRegistry.js +409 -80
  12. package/server/agent/commands.js +1056 -125
  13. package/server/agent/config.js +1088 -690
  14. package/server/agent/contextCompactor.js +213 -21
  15. package/server/agent/cpuMetrics.js +199 -0
  16. package/server/agent/engineService.js +591 -0
  17. package/server/agent/eval/modules/evaluation/runner.js +4 -14
  18. package/server/agent/eval/modules/evaluation/scoring.js +15 -3
  19. package/server/agent/eval/modules/server/evalApi.js +5 -5
  20. package/server/agent/eval/modules/ui/evalUi.js +7 -4
  21. package/server/agent/eventBus.js +1 -0
  22. package/server/agent/ink/main.jsx +196 -191
  23. package/server/agent/jobManager.js +11 -1
  24. package/server/agent/lanDiscovery.js +721 -23
  25. package/server/agent/llmClient.js +317 -71
  26. package/server/agent/lmStudioWakeup.js +517 -65
  27. package/server/agent/locationService.js +797 -0
  28. package/server/agent/mcpQuizData.js +509 -0
  29. package/server/agent/memory/contextAllocator.js +19 -7
  30. package/server/agent/memory.js +3 -1
  31. package/server/agent/modelCompatibility.js +352 -0
  32. package/server/agent/modelDefaults.js +1 -0
  33. package/server/agent/modelManager.js +46 -2
  34. package/server/agent/newsWatch.js +9 -3
  35. package/server/agent/processManager.js +229 -0
  36. package/server/agent/prompts.js +174 -8
  37. package/server/agent/routingInterceptor.js +9 -1
  38. package/server/agent/sanitizer.js +41 -6
  39. package/server/agent/searchQueryPlanner.js +202 -0
  40. package/server/agent/session.js +59 -4
  41. package/server/agent/tests/agentFollowUps.test.js +128 -2
  42. package/server/agent/tests/agentMusicControl.test.js +245 -0
  43. package/server/agent/tests/apiRouter.test.js +1 -1
  44. package/server/agent/tests/autoContext.test.js +176 -0
  45. package/server/agent/tests/bilibiliEmbed.test.js +45 -0
  46. package/server/agent/tests/chatHardening.test.js +121 -0
  47. package/server/agent/tests/chineseWebSearchEscalation.test.js +62 -0
  48. package/server/agent/tests/clarifyingQuestions.test.js +33 -0
  49. package/server/agent/tests/cliAutomation.test.js +38 -1
  50. package/server/agent/tests/cloudProviders.test.js +91 -3
  51. package/server/agent/tests/commands.test.js +92 -3
  52. package/server/agent/tests/contextIntelligence.test.js +50 -3
  53. package/server/agent/tests/cpuMetrics.test.js +101 -0
  54. package/server/agent/tests/decisionAndSearchEnhancements.test.js +32 -0
  55. package/server/agent/tests/edgeCasesScenarios.test.js +82 -0
  56. package/server/agent/tests/engineService.test.js +436 -0
  57. package/server/agent/tests/engineUnload.test.js +94 -0
  58. package/server/agent/tests/envCommand.test.js +69 -0
  59. package/server/agent/tests/evalMetrics.test.js +8 -0
  60. package/server/agent/tests/fetchApi.test.js +99 -0
  61. package/server/agent/tests/hybridWebSearch.test.js +2 -0
  62. package/server/agent/tests/lanDiscovery.test.js +5 -0
  63. package/server/agent/tests/lanInterfaces.test.js +510 -0
  64. package/server/agent/tests/lmStudioWakeup.test.js +278 -0
  65. package/server/agent/tests/locationTool.test.js +278 -0
  66. package/server/agent/tests/memoryRoute.test.js +173 -0
  67. package/server/agent/tests/memoryUiNormalization.test.js +75 -0
  68. package/server/agent/tests/modelCompatibility.test.js +148 -0
  69. package/server/agent/tests/modelManager.test.js +32 -0
  70. package/server/agent/tests/musicControlUiBridge.test.js +113 -0
  71. package/server/agent/tests/nextGenArchitecture.test.js +4 -2
  72. package/server/agent/tests/openDomainPrompt.test.js +197 -0
  73. package/server/agent/tests/openDomainSearch.test.js +121 -0
  74. package/server/agent/tests/processManager.test.js +144 -0
  75. package/server/agent/tests/providerPersistence.test.js +10 -5
  76. package/server/agent/tests/quickActionsAndModals.test.js +180 -0
  77. package/server/agent/tests/searchQueryPlanner.test.js +108 -0
  78. package/server/agent/tests/securityAuditRemediation.test.js +16 -9
  79. package/server/agent/tests/securityAuditRemediations2.test.js +154 -0
  80. package/server/agent/tests/securityHardening.test.js +4 -4
  81. package/server/agent/tests/sessionAndCopy.test.js +39 -4
  82. package/server/agent/tests/sidebarControls.test.js +34 -0
  83. package/server/agent/tests/statefulMemory.test.js +8 -1
  84. package/server/agent/tests/textProtocolLoop.test.js +71 -0
  85. package/server/agent/tests/textToolParsing.test.js +48 -0
  86. package/server/agent/tests/toolRoutingRegressions.test.js +41 -0
  87. package/server/agent/tests/unifiedEndpoints.test.js +13 -6
  88. package/server/agent/tests/v110Features.test.js +1 -1
  89. package/server/agent/tests/vramEstimator.test.js +75 -0
  90. package/server/agent/tests/webChatDocs.test.js +199 -0
  91. package/server/agent/tests/webChatInferenceControl.test.js +298 -0
  92. package/server/agent/tests/webChatInspection.test.js +256 -5
  93. package/server/agent/tests/webChatMcp.test.js +94 -0
  94. package/server/agent/tests/webExtract.test.js +23 -0
  95. package/server/agent/tests/webSources.test.js +46 -12
  96. package/server/agent/textToolProtocol.js +173 -46
  97. package/server/agent/toolRouter.js +318 -44
  98. package/server/agent/tools.js +877 -96
  99. package/server/agent/ui.js +39 -34
  100. package/server/agent/vramService.js +35 -54
  101. package/server/agent/webSearch.js +441 -162
  102. package/server/agent/websearch/orchestrator.js +15 -5
  103. package/server/agent/websearch/resilience/cache.js +9 -2
  104. package/server/agent/websearch/security/ssrf.js +41 -10
  105. package/server/agent/youtubeAuth.js +5 -20
  106. package/server/apiRouter.js +5558 -4422
  107. package/server/mcp/data/discography.js +2 -2
  108. package/server/mcp/examples/antigravity.example.json +17 -0
  109. package/server/mcp/handler.js +8 -4
  110. package/server/mcp/security.js +220 -21
  111. package/server/mcp/server.js +2 -1
  112. package/server/mcp/tests/connection.test.js +14 -8
  113. package/server/mcp/tests/mcpKeyAuth.test.js +156 -0
  114. package/server/mcp/tools.js +7 -1
  115. package/server/supabase_migration_prefix.sql +102 -0
  116. package/server/supabase_schema.sql +18 -18
  117. package/server/web-chat/core/connection.js +179 -218
  118. package/server/web-chat/core/conversationContext.js +9 -2
  119. package/server/web-chat/core/directCatalogEngine.js +52 -28
  120. package/server/web-chat/core/dramaKnowledge.js +33 -22
  121. package/server/web-chat/core/followUpEngine.js +735 -81
  122. package/server/web-chat/core/futureEventGuard.js +5 -1
  123. package/server/web-chat/core/musicControlAction.js +70 -0
  124. package/server/web-chat/core/security.js +32 -21
  125. package/server/web-chat/core/toolInspector.js +6 -6
  126. package/server/web-chat/core/urlGuard.js +45 -0
  127. package/server/web-chat/router.js +204 -157
  128. package/server/web-chat/routes/chatRoute.js +885 -488
  129. package/server/web-chat/routes/engineRoute.js +270 -0
  130. package/server/web-chat/routes/exportRoute.js +1 -1
  131. package/server/web-chat/routes/lanDiscoveryRoute.js +17 -2
  132. package/server/web-chat/routes/lmStudioLoadRoute.js +172 -0
  133. package/server/web-chat/routes/mcpRoute.js +386 -0
  134. package/server/web-chat/routes/memoryRoute.js +253 -0
  135. package/server/web-chat/routes/providerRoute.js +49 -16
  136. package/server/web-chat/routes/staticRoute.js +306 -17
  137. package/server/web-chat/routes/statusRoute.js +194 -73
  138. package/server/web-chat/routes/suggestRoute.js +11 -3
  139. package/server/web-chat/routes/verifyRoute.js +26 -4
  140. package/server/web-chat/server.js +7 -6
  141. package/server/web-chat/ui/docsBuilder.js +3558 -0
  142. package/server/web-chat/ui/htmlBuilder.js +12 -0
  143. package/server/web-chat/ui/scripts/client.js +3401 -1302
  144. package/server/web-chat/ui/scripts/webchatI18n.js +155 -153
  145. package/server/web-chat/ui/settingsBuilder.js +7870 -0
  146. package/server/web-chat/ui/styles/lan-card.css +165 -0
  147. package/server/web-chat/ui/styles/main.css +4289 -447
  148. package/server/web-chat/ui/sw.js +64 -0
  149. package/server/web-chat/ui/templates/body.html +407 -281
  150. package/src/data/awardsData.js +146 -146
  151. package/src/data/musicVideoData.js +6 -4
  152. package/src/lib/vram/estimator.js +195 -19
@@ -1,146 +1,146 @@
1
- export const awardsData = [
2
- {
3
- id: "award-2024-weibo",
4
- year: 2024,
5
- titleTh: "นักแสดงหญิงยอดนิยมแห่งปี (Popular Actress of the Year)",
6
- titleCn: "年度人气女演员",
7
- ceremony: "Weibo TV & Internet Video Summit 2024",
8
- work: "In Blossom (ฮวาเจียนลิ่ง ในม่านเมฆ)",
9
- category: "Acting"
10
- },
11
- {
12
- id: "award-2023-weibo-night",
13
- year: 2023,
14
- titleTh: "รางวัลบุคคลผู้ทรงอิทธิพลแห่งปี (Influential Figure of the Year)",
15
- titleCn: "年度影响力艺人",
16
- ceremony: "Weibo Night 2023",
17
- work: "Beauty of Resilience (ตำนานเลือดฟีนิกซ์)",
18
- category: "Honor"
19
- },
20
- {
21
- id: "award-2022-erc",
22
- year: 2022,
23
- titleTh: "นักร้องยอดนิยมแห่งปี (Popular Singer of the Year)",
24
- titleCn: "年度最佳人气歌手",
25
- ceremony: "29th ERC Chinese Top Ten Awards (第29届东方风云榜)",
26
- work: "Single '0.2s' & Solo Music Portfolio",
27
- category: "Music"
28
- },
29
- {
30
- id: "award-2021-tencent",
31
- year: 2021,
32
- titleTh: "นักแสดงนำหญิงซีรีส์ยอดเยี่ยมแห่งปี (Outstanding Actress)",
33
- titleCn: "年度卓越女演员",
34
- ceremony: "Tencent Video Starlight Awards 2021",
35
- work: "Rebirth for You (มู่หนานจือ)",
36
- category: "Acting"
37
- },
38
- {
39
- id: "award-2020-erc-stage",
40
- year: 2020,
41
- titleTh: "การแสดงบนเวทียอดเยี่ยมแห่งปี (Best Stage Interpretation)",
42
- titleCn: "最佳舞台演绎",
43
- ceremony: "27th ERC Chinese Top Ten Awards (第27届东方风云榜)",
44
- work: "Love Emergency Report (恋爱告急)",
45
- category: "Music"
46
- },
47
- {
48
- id: "award-2020-iqiyi-breakthrough",
49
- year: 2020,
50
- titleTh: "นักแสดงหญิงผู้ก้าวกระโดดยอดเยี่ยม (Breakthrough Actress)",
51
- titleCn: "年度飞跃女演员",
52
- ceremony: "iQIYI Scream Night 2020",
53
- work: "The Blooms at Ruyi Pavilion & In a Class of Her Own",
54
- category: "Acting"
55
- },
56
- {
57
- id: "award-2019-china-canada",
58
- year: 2019,
59
- titleTh: "นักแสดงนำหญิงยอดเยี่ยม (Best Actress - Web Series)",
60
- titleCn: "最佳网剧女主角",
61
- ceremony: "China Canada Television Festival Award 2019 (中加电视节)",
62
- work: "Legend of Yunxi (ตำนานหยุนซี)",
63
- category: "Acting"
64
- },
65
- {
66
- id: "award-2019-erc-trend",
67
- year: 2019,
68
- titleTh: "นักร้องผู้นำเทรนด์แห่งปี (Trend Singer Award)",
69
- titleCn: "年度风尚歌手",
70
- ceremony: "26th ERC Chinese Top Ten Awards (第26届东方风云榜)",
71
- work: "Solo Music & OST Works",
72
- category: "Music"
73
- },
74
- {
75
- id: "award-2019-iqiyi-drama-actress",
76
- year: 2019,
77
- titleTh: "นักแสดงหญิงยอดนิยมด้านการแสดง (Drama Actress of the Year)",
78
- titleCn: "年度戏剧人气女艺人",
79
- ceremony: "iQIYI Scream Night 2019",
80
- work: "The Legend of White Snake (ตำนานนางพญางูขาว)",
81
- category: "Acting"
82
- },
83
- {
84
- id: "award-2018-weibo-improved",
85
- year: 2018,
86
- titleTh: "ศิลปินผู้มีพัฒนาการก้าวกระโดดแห่งปี (Improved Artist of the Year)",
87
- titleCn: "年度进取艺人",
88
- ceremony: "Weibo Awards Ceremony 2018",
89
- work: "Legend of Yunxi & Solo Career Launch",
90
- category: "Honor"
91
- },
92
- {
93
- id: "award-2017-may4th-medal",
94
- year: 2017,
95
- titleTh: "เหรียญรางวัลเกียรติยศเยาวชน 4 พฤษภาคม (May 4th Youth Medal)",
96
- titleCn: "五四优秀青年勋章",
97
- ceremony: "Chinese Communist Youth League (共青团中央)",
98
- work: "เยาวชนตัวอย่างผู้สร้างคุณประโยชน์ทางศิลปวัฒนธรรม",
99
- category: "State Honor"
100
- },
101
- {
102
- id: "award-2017-erc-breakthrough",
103
- year: 2017,
104
- titleTh: "ศิลปินผู้มีพัฒนาการโดดเด่นยอดเยี่ยม (Best Breakthrough Artist)",
105
- titleCn: "最佳飞跃艺人",
106
- ceremony: "24th ERC Chinese Top Ten Awards (第24届东方风云榜)",
107
- work: "EP Everyday & Music Solo Works",
108
- category: "Music"
109
- },
110
- {
111
- id: "award-2017-migu-top10",
112
- year: 2017,
113
- titleTh: "1 ใน 10 เพลงยอดเยี่ยมแห่งปี (Top 10 Songs of the Year)",
114
- titleCn: "年度十大金曲",
115
- ceremony: "11th Migu Music Awards (第11届音乐盛典咪咕汇)",
116
- work: "Think of You (想你了)",
117
- category: "Music"
118
- },
119
- {
120
- id: "award-2017-snh48-champion",
121
- year: 2017,
122
- titleTh: "ชนะเลิศอันดับ 1 การเลือกตั้งทั่วไป SNH48 ครั้งที่ 4 (แชมป์ 2 สมัยซ้อน)",
123
- titleCn: "SNH48第四届偶像年度人气总决选冠军 (晋升明星殿堂)",
124
- ceremony: "SNH48 4th General Election",
125
- work: "บรรจุเข้าสู่ SNH48 Star Hall of Fame (วิหารแห่งดาว)",
126
- category: "Hall of Fame"
127
- },
128
- {
129
- id: "award-2016-tencent-new-actress",
130
- year: 2016,
131
- titleTh: "นักแสดงโทรทัศน์หน้าใหม่ยอดเยี่ยมแห่งปี (New Television Actress)",
132
- titleCn: "年度新锐电视剧女演员",
133
- ceremony: "Tencent Video Star Awards 2016 (腾讯视频星光大赏)",
134
- work: "Novoland: The Castle in the Sky (九州·天空城)",
135
- category: "Acting"
136
- },
137
- {
138
- id: "award-2016-snh48-champion",
139
- year: 2016,
140
- titleTh: "ชนะเลิศอันดับ 1 การเลือกตั้งทั่วไป SNH48 ครั้งที่ 3 (คะแนนเกิน 230,000)",
141
- titleCn: "SNH48第三届偶像年度人气总决选冠军",
142
- ceremony: "SNH48 3rd General Election",
143
- work: "เซ็นเตอร์เพลง Princess's Cloak (公主披风)",
144
- category: "Idol & Acting"
145
- }
146
- ];
1
+ export const awardsData = [
2
+ {
3
+ id: "award-2024-weibo",
4
+ year: 2024,
5
+ titleTh: "นักแสดงหญิงยอดนิยมแห่งปี (Popular Actress of the Year)",
6
+ titleCn: "年度人气女演员",
7
+ ceremony: "Weibo TV & Internet Video Summit 2024",
8
+ work: "In Blossom (ฮวาเจียนลิ่ง ในม่านเมฆ)",
9
+ category: "Acting"
10
+ },
11
+ {
12
+ id: "award-2023-weibo-night",
13
+ year: 2023,
14
+ titleTh: "รางวัลบุคคลผู้ทรงอิทธิพลแห่งปี (Influential Figure of the Year)",
15
+ titleCn: "年度影响力艺人",
16
+ ceremony: "Weibo Night 2023",
17
+ work: "Beauty of Resilience (ตำนานเลือดฟีนิกซ์)",
18
+ category: "Honor"
19
+ },
20
+ {
21
+ id: "award-2022-erc",
22
+ year: 2022,
23
+ titleTh: "นักร้องยอดนิยมแห่งปี (Popular Singer of the Year)",
24
+ titleCn: "年度最佳人气歌手",
25
+ ceremony: "29th ERC Chinese Top Ten Awards (第29届东方风云榜)",
26
+ work: "Single '0.2s' & Solo Music Portfolio",
27
+ category: "Music"
28
+ },
29
+ {
30
+ id: "award-2021-tencent",
31
+ year: 2021,
32
+ titleTh: "นักแสดงนำหญิงซีรีส์ยอดเยี่ยมแห่งปี (Outstanding Actress)",
33
+ titleCn: "年度卓越女演员",
34
+ ceremony: "Tencent Video Starlight Awards 2021",
35
+ work: "Rebirth for You (มู่หนานจือ)",
36
+ category: "Acting"
37
+ },
38
+ {
39
+ id: "award-2020-erc-stage",
40
+ year: 2020,
41
+ titleTh: "การแสดงบนเวทียอดเยี่ยมแห่งปี (Best Stage Interpretation)",
42
+ titleCn: "最佳舞台演绎",
43
+ ceremony: "27th ERC Chinese Top Ten Awards (第27届东方风云榜)",
44
+ work: "Love Emergency Report (恋爱告急)",
45
+ category: "Music"
46
+ },
47
+ {
48
+ id: "award-2020-iqiyi-breakthrough",
49
+ year: 2020,
50
+ titleTh: "นักแสดงหญิงผู้ก้าวกระโดดยอดเยี่ยม (Breakthrough Actress)",
51
+ titleCn: "年度飞跃女演员",
52
+ ceremony: "iQIYI Scream Night 2020",
53
+ work: "The Blooms at Ruyi Pavilion & In a Class of Her Own",
54
+ category: "Acting"
55
+ },
56
+ {
57
+ id: "award-2019-china-canada",
58
+ year: 2019,
59
+ titleTh: "นักแสดงนำหญิงยอดเยี่ยม (Best Actress - Web Series)",
60
+ titleCn: "最佳网剧女主角",
61
+ ceremony: "China Canada Television Festival Award 2019 (中加电视节)",
62
+ work: "Legend of Yunxi (ตำนานหยุนซี)",
63
+ category: "Acting"
64
+ },
65
+ {
66
+ id: "award-2019-erc-trend",
67
+ year: 2019,
68
+ titleTh: "นักร้องผู้นำเทรนด์แห่งปี (Trend Singer Award)",
69
+ titleCn: "年度风尚歌手",
70
+ ceremony: "26th ERC Chinese Top Ten Awards (第26届东方风云榜)",
71
+ work: "Solo Music & OST Works",
72
+ category: "Music"
73
+ },
74
+ {
75
+ id: "award-2019-iqiyi-drama-actress",
76
+ year: 2019,
77
+ titleTh: "นักแสดงหญิงยอดนิยมด้านการแสดง (Drama Actress of the Year)",
78
+ titleCn: "年度戏剧人气女艺人",
79
+ ceremony: "iQIYI Scream Night 2019",
80
+ work: "The Legend of White Snake (ตำนานนางพญางูขาว)",
81
+ category: "Acting"
82
+ },
83
+ {
84
+ id: "award-2018-weibo-improved",
85
+ year: 2018,
86
+ titleTh: "ศิลปินผู้มีพัฒนาการก้าวกระโดดแห่งปี (Improved Artist of the Year)",
87
+ titleCn: "年度进取艺人",
88
+ ceremony: "Weibo Awards Ceremony 2018",
89
+ work: "Legend of Yunxi & Solo Career Launch",
90
+ category: "Honor"
91
+ },
92
+ {
93
+ id: "award-2017-may4th-medal",
94
+ year: 2017,
95
+ titleTh: "เหรียญรางวัลเกียรติยศเยาวชน 4 พฤษภาคม (May 4th Youth Medal)",
96
+ titleCn: "五四优秀青年勋章",
97
+ ceremony: "Chinese Communist Youth League (共青团中央)",
98
+ work: "เยาวชนตัวอย่างผู้สร้างคุณประโยชน์ทางศิลปวัฒนธรรม",
99
+ category: "State Honor"
100
+ },
101
+ {
102
+ id: "award-2017-erc-breakthrough",
103
+ year: 2017,
104
+ titleTh: "ศิลปินผู้มีพัฒนาการโดดเด่นยอดเยี่ยม (Best Breakthrough Artist)",
105
+ titleCn: "最佳飞跃艺人",
106
+ ceremony: "24th ERC Chinese Top Ten Awards (第24届东方风云榜)",
107
+ work: "EP Everyday & Music Solo Works",
108
+ category: "Music"
109
+ },
110
+ {
111
+ id: "award-2017-migu-top10",
112
+ year: 2017,
113
+ titleTh: "1 ใน 10 เพลงยอดเยี่ยมแห่งปี (Top 10 Songs of the Year)",
114
+ titleCn: "年度十大金曲",
115
+ ceremony: "11th Migu Music Awards (第11届音乐盛典咪咕汇)",
116
+ work: "Think of You (想你了)",
117
+ category: "Music"
118
+ },
119
+ {
120
+ id: "award-2017-snh48-champion",
121
+ year: 2017,
122
+ titleTh: "ชนะเลิศอันดับ 1 การเลือกตั้งทั่วไป SNH48 ครั้งที่ 4 (แชมป์ 2 สมัยซ้อน)",
123
+ titleCn: "SNH48第四届偶像年度人气总决选冠军 (晋升明星殿堂)",
124
+ ceremony: "SNH48 4th General Election",
125
+ work: "บรรจุเข้าสู่ SNH48 Star Hall of Fame (วิหารแห่งดาว)",
126
+ category: "Hall of Fame"
127
+ },
128
+ {
129
+ id: "award-2016-tencent-new-actress",
130
+ year: 2016,
131
+ titleTh: "นักแสดงโทรทัศน์หน้าใหม่ยอดเยี่ยมแห่งปี (New Television Actress)",
132
+ titleCn: "年度新锐电视剧女演员",
133
+ ceremony: "Tencent Video Star Awards 2016 (腾讯视频星光大赏)",
134
+ work: "Novoland: The Castle in the Sky (九州·天空城)",
135
+ category: "Acting"
136
+ },
137
+ {
138
+ id: "award-2016-snh48-champion",
139
+ year: 2016,
140
+ titleTh: "ชนะเลิศอันดับ 1 การเลือกตั้งทั่วไป SNH48 ครั้งที่ 3 (คะแนนเกิน 230,000)",
141
+ titleCn: "SNH48第三届偶像年度人气总决选冠军",
142
+ ceremony: "SNH48 3rd General Election",
143
+ work: "เซ็นเตอร์เพลง Princess's Cloak (公主披风)",
144
+ category: "Idol & Acting"
145
+ }
146
+ ];
@@ -59,12 +59,12 @@ export const musicVideoData = [
59
59
  titlePinyin: "Lu Zhu Zai Chen Xi Qian Xiao San",
60
60
  releaseYear: 2026,
61
61
  category: "OST",
62
- dramaName: "Veil of Shadows (《月鳞绮纪》) — เพลงลูกทรงของลู่หวู่อี",
63
- aliases: ["Dewdrops", "Dewdrops Dissipate Before Dawn", "Dew Before Dawn", "露珠在晨曦前消散", "หยาดน้ำค้างสลายก่อนรุ่งอรุณ", "หยาดน้ำค้าง", "D-vEPdasbLo"],
64
- keywords: ["dewdrops", "veil of shadows", "lu wuyi", "月鳞绮纪", "露珠", "เพลงลูกทรง"],
62
+ dramaName: "Veil of Shadows (《月鳞绮纪》) / In Blossom (《花间令》)",
63
+ aliases: ["Dewdrops", "Dewdrops Dissipate Before Dawn", "Dew Before Dawn", "In Blossom", "花间令", "露水", "露珠在晨曦前消散", "หยาดน้ำค้างสลายก่อนรุ่งอรุณ", "หยาดน้ำค้าง", "D-vEPdasbLo"],
64
+ keywords: ["dewdrops", "in blossom", "veil of shadows", "lu wuyi", "花间令", "月鳞绮纪", "露珠", "露水", "เพลงลูกทรง"],
65
65
  youtubeId: "D-vEPdasbLo",
66
66
  duration: "04:42",
67
- description: "เพลงลูกทรงของลู่หวู่อีจากซีรีส์ Veil of Shadows เวอร์ชันทางการเผยแพร่โดย Tencent Music",
67
+ description: "เพลงลูกทรงของลู่หวู่อีจากซีรีส์ Veil of Shadows / In Blossom เวอร์ชันทางการเผยแพร่โดย Tencent Music",
68
68
  isFeatured: true
69
69
  },
70
70
  {
@@ -283,6 +283,8 @@ export const musicVideoData = [
283
283
  releaseYear: 2019,
284
284
  category: "Single",
285
285
  dramaName: "Dance Pop EP",
286
+ aliases: ["Heartbeat Alert", "Love Emergency", "Love Emergency Report", "恋爱告急", "แจ้งเตือนหัวใจฉุกเฉิน", "Heartbeat", "5MLPdCmnlmE"],
287
+ keywords: ["heartbeat alert", "love emergency", "恋爱告急", "แจ้งเตือนหัวใจฉุกเฉิน"],
286
288
  youtubeId: "5MLPdCmnlmE",
287
289
  duration: "03:35",
288
290
  description: "เพลงแดนซ์ป็อปไวรัลสุดสดใส คว้ารางวัล Best Stage Interpretation",
@@ -115,31 +115,96 @@ export function formatGb(bytes, digits = 2) {
115
115
  }
116
116
 
117
117
 
118
- export function estimateWeightsBytes({ paramCount = null, paramsB = null, quant = 'q4_k_m', weightBytesOverride = null } = {}) {
118
+ export const KV_CACHE_DTYPE_BYTES = Object.freeze({
119
+ fp32: 4.0,
120
+ f32: 4.0,
121
+ fp16: 2.0,
122
+ f16: 2.0,
123
+ bf16: 2.0,
124
+ q8_0: 1.0625,
125
+ q8: 1.0625,
126
+ q4_0: 0.5625,
127
+ q4: 0.5625,
128
+ q4_1: 0.625,
129
+ q5_0: 0.6875,
130
+ q5_1: 0.75,
131
+ });
132
+
133
+ export function resolveKvBytesPerValue(cacheBits = 16, cacheType = null) {
134
+ if (cacheType && typeof cacheType === 'string') {
135
+ const key = cacheType.toLowerCase().replace(/[^a-z0-9_]/g, '');
136
+ if (KV_CACHE_DTYPE_BYTES[key] !== undefined) return KV_CACHE_DTYPE_BYTES[key];
137
+ }
138
+ if (typeof cacheBits === 'string') {
139
+ const key = cacheBits.toLowerCase().replace(/[^a-z0-9_]/g, '');
140
+ if (KV_CACHE_DTYPE_BYTES[key] !== undefined) return KV_CACHE_DTYPE_BYTES[key];
141
+ }
142
+ const n = Number(cacheBits);
143
+ if (Number.isFinite(n) && n > 0) {
144
+ return n / 8;
145
+ }
146
+ return 2.0;
147
+ }
148
+
149
+ export function estimateWeightsBytes({
150
+ paramCount = null,
151
+ paramsB = null,
152
+ quant = 'q4_k_m',
153
+ weightBytesOverride = null,
154
+ gpuLayers = null,
155
+ totalLayers = null,
156
+ } = {}) {
119
157
  const bpw = parseBpw(quant);
158
+ let rawBytes = null;
159
+ let count = paramCount;
120
160
  if (weightBytesOverride !== null && weightBytesOverride !== undefined && Number.isFinite(Number(weightBytesOverride)) && Number(weightBytesOverride) > 0) {
121
- let count = paramCount;
161
+ rawBytes = Math.round(Number(weightBytesOverride));
162
+ if (count === null || count === undefined) {
163
+ count = paramsB !== null && paramsB !== undefined && Number.isFinite(Number(paramsB))
164
+ ? Math.round(Number(paramsB) * 1e9)
165
+ : null;
166
+ }
167
+ } else {
122
168
  if (count === null || count === undefined) {
123
169
  count = paramsB !== null && paramsB !== undefined && Number.isFinite(Number(paramsB))
124
170
  ? Math.round(Number(paramsB) * 1e9)
125
171
  : null;
126
172
  }
127
- return { bytes: Math.round(Number(weightBytesOverride)), bpw, paramCount: count, source: 'override' };
173
+ if (count && count > 0 && bpw !== null) {
174
+ rawBytes = Math.round(count * bpw / 8);
175
+ }
128
176
  }
129
- let count = paramCount;
130
- if (count === null || count === undefined) {
131
- count = paramsB !== null && paramsB !== undefined && Number.isFinite(Number(paramsB))
132
- ? Math.round(Number(paramsB) * 1e9)
133
- : null;
177
+
178
+ if (rawBytes === null) {
179
+ return { bytes: null, bpw, paramCount: count };
180
+ }
181
+
182
+ if (gpuLayers !== null && gpuLayers !== undefined && totalLayers !== null && totalLayers !== undefined) {
183
+ const gL = Number(gpuLayers);
184
+ const tL = Number(totalLayers);
185
+ if (Number.isFinite(gL) && Number.isFinite(tL) && tL > 0 && gL >= 0 && gL < tL) {
186
+ const fraction = Math.min(1, Math.max(0, gL / tL));
187
+ return {
188
+ bytes: Math.round(rawBytes * fraction),
189
+ bpw,
190
+ paramCount: count,
191
+ totalWeightBytes: rawBytes,
192
+ offloadFraction: fraction,
193
+ gpuLayers: gL,
194
+ totalLayers: tL,
195
+ source: weightBytesOverride ? 'override-fractional' : 'fractional',
196
+ };
197
+ }
134
198
  }
135
- if (!count || count <= 0 || bpw === null) return { bytes: null, bpw, paramCount: count };
136
- return { bytes: Math.round(count * bpw / 8), bpw, paramCount: count };
199
+
200
+ return { bytes: rawBytes, bpw, paramCount: count, source: weightBytesOverride ? 'override' : undefined };
137
201
  }
138
202
 
139
203
 
140
- export function computeKvBytesPerToken({ layers, kvHeads, headDim, cacheBits = 16 } = {}) {
204
+ export function computeKvBytesPerToken({ layers, kvHeads, headDim, cacheBits = 16, cacheType = null } = {}) {
141
205
  if (!layers || !kvHeads || !headDim) return null;
142
- return 2 * layers * kvHeads * headDim * (Number(cacheBits || 16) / 8);
206
+ const bytesPerVal = resolveKvBytesPerValue(cacheBits, cacheType);
207
+ return 2 * layers * kvHeads * headDim * bytesPerVal;
143
208
  }
144
209
 
145
210
  export function estimateKvCacheBytes({
@@ -149,10 +214,12 @@ export function estimateKvCacheBytes({
149
214
  contextLength = null,
150
215
  batchSize = 1,
151
216
  cacheBits = 16,
217
+ cacheType = null,
152
218
  swa = null,
153
219
  } = {}) {
220
+ const bytesPerVal = resolveKvBytesPerValue(cacheBits, cacheType);
154
221
  const perLayerPerToken = (layers && kvHeads && headDim)
155
- ? 2 * kvHeads * headDim * (Number(cacheBits || 16) / 8)
222
+ ? 2 * kvHeads * headDim * bytesPerVal
156
223
  : null;
157
224
  const bytesPerToken = perLayerPerToken !== null ? perLayerPerToken * layers : null;
158
225
  const ctx = Number(contextLength);
@@ -197,16 +264,25 @@ export function estimateActivationBytes({
197
264
  queryHeads = null,
198
265
  kvHeads = null,
199
266
  activationBits = 16,
267
+ contextLength = null,
200
268
  } = {}) {
269
+ const batch = Math.max(1, Number(batchSize) || 1);
270
+ const ctx = Number(contextLength) || 0;
271
+ let dynamicScratchGb = DEFAULT_ACTIVATION_GB;
272
+ if (ctx >= 32768) {
273
+ dynamicScratchGb = Math.max(dynamicScratchGb, 0.70);
274
+ } else if (ctx >= 16384) {
275
+ dynamicScratchGb = Math.max(dynamicScratchGb, 0.50);
276
+ }
277
+
201
278
  if (!hiddenSize) {
202
- return { analyticBytes: null, reservedBytes: Math.round(DEFAULT_ACTIVATION_GB * GIB) };
279
+ return { analyticBytes: null, reservedBytes: Math.round(dynamicScratchGb * GIB) };
203
280
  }
204
281
  const gqaFactor = queryHeads && kvHeads ? 5 * (queryHeads / kvHeads) : 5;
205
- const batch = Math.max(1, Number(batchSize) || 1);
206
282
  const analyticBytes = Math.round(
207
283
  batch * hiddenSize * (34 + gqaFactor) * (Number(activationBits || 16) / 8)
208
284
  );
209
- return { analyticBytes, reservedBytes: Math.round(DEFAULT_ACTIVATION_GB * GIB) };
285
+ return { analyticBytes, reservedBytes: Math.round(dynamicScratchGb * GIB) };
210
286
  }
211
287
 
212
288
  /**
@@ -219,10 +295,14 @@ export function estimateActivationBytes({
219
295
  * @param {string} [input.quant='q4_k_m'] Weight quantization.
220
296
  * @param {number} [input.contextLength] Runtime sequence limit (text tokens).
221
297
  * @param {number} [input.batchSize=1]
222
- * @param {number} [input.cacheBits=16] KV cache precision (FP16=16, Q8_0=8).
298
+ * @param {number} [input.cacheBits=16] KV cache precision (FP16=16, Q8_0=8, or 'q8_0').
299
+ * @param {string} [input.cacheType] KV cache type ('fp16', 'q8_0', 'q4_0').
300
+ * @param {number} [input.gpuLayers] Number of layers offloaded to GPU.
301
+ * @param {number} [input.totalLayers] Total model layers.
223
302
  * @param {number} [input.layers|kvHeads|queryHeads|headDim|hiddenSize]
224
303
  * @param {object} [input.vision] { images, tokensPerImage, encoderParamsB }
225
304
  * @param {number} [input.activationGb] Override static activation reservation.
305
+ * @param {number} [input.scratchGb] Alias for activationGb.
226
306
  * @param {number} [input.overheadGb] Override driver/runtime overhead.
227
307
  * @returns {object} Breakdown in bytes + binary GB, warnings included.
228
308
  */
@@ -235,6 +315,8 @@ export function estimateVramFootprint(input = {}) {
235
315
  paramsB: input.paramsB,
236
316
  quant,
237
317
  weightBytesOverride: input.weightBytesOverride,
318
+ gpuLayers: input.gpuLayers,
319
+ totalLayers: input.totalLayers || input.layers,
238
320
  });
239
321
 
240
322
  const vision = input.vision || null;
@@ -262,6 +344,7 @@ export function estimateVramFootprint(input = {}) {
262
344
  contextLength: effectiveContextLength || null,
263
345
  batchSize: input.batchSize,
264
346
  cacheBits: input.cacheBits,
347
+ cacheType: input.cacheType,
265
348
  swa: input.swa,
266
349
  });
267
350
 
@@ -271,9 +354,11 @@ export function estimateVramFootprint(input = {}) {
271
354
  queryHeads: input.queryHeads,
272
355
  kvHeads: input.kvHeads,
273
356
  activationBits: 16,
357
+ contextLength: effectiveContextLength || textCtx,
274
358
  });
275
- const activationBytes = input.activationGb !== undefined && input.activationGb !== null
276
- ? Math.round(Number(input.activationGb) * GIB)
359
+ const actOverride = input.activationGb ?? input.scratchGb;
360
+ const activationBytes = actOverride !== undefined && actOverride !== null
361
+ ? Math.round(Number(actOverride) * GIB)
277
362
  : activation.reservedBytes;
278
363
  const overheadBytes = input.overheadGb !== undefined && input.overheadGb !== null
279
364
  ? Math.round(Number(input.overheadGb) * GIB)
@@ -462,3 +547,94 @@ export function recommendAdjustments(estimate, availableBytes, { targetZone = 'G
462
547
 
463
548
  return suggestions;
464
549
  }
550
+
551
+ /**
552
+ * Simulate VRAM distribution across multiple GPUs in layer-split mode.
553
+ *
554
+ * @param {object} options
555
+ * @param {number} [options.totalBytes]
556
+ * @param {number} [options.weightsBytes]
557
+ * @param {number} [options.kvCacheBytes]
558
+ * @param {number} [options.activationBytes]
559
+ * @param {number} [options.overheadBytes]
560
+ * @param {number} [options.gpuCount=2]
561
+ * @param {string|number[]} [options.tensorSplit] e.g. '20,12' or [20, 12]
562
+ * @param {number} [options.mainGpu=0]
563
+ * @param {number} [options.mainGpuScratchHeadroomGb=2.0] Headroom for scratchpad/context on main GPU
564
+ * @returns {object} Breakdown per GPU
565
+ */
566
+ export function simulateMultiGpuFootprint({
567
+ totalBytes = null,
568
+ weightsBytes = null,
569
+ kvCacheBytes = null,
570
+ activationBytes = null,
571
+ overheadBytes = null,
572
+ gpuCount = 2,
573
+ tensorSplit = null,
574
+ mainGpu = 0,
575
+ mainGpuScratchHeadroomGb = 2.0,
576
+ } = {}) {
577
+ const count = Math.max(1, Number(gpuCount) || 2);
578
+ const mg = Math.max(0, Math.min(count - 1, Number(mainGpu) || 0));
579
+
580
+ let ratios = [];
581
+ if (typeof tensorSplit === 'string' && tensorSplit.trim()) {
582
+ ratios = tensorSplit.split(',').map(s => Number(s.trim())).filter(n => Number.isFinite(n) && n >= 0);
583
+ } else if (Array.isArray(tensorSplit)) {
584
+ ratios = tensorSplit.map(n => Number(n)).filter(n => Number.isFinite(n) && n >= 0);
585
+ }
586
+
587
+ if (ratios.length < count) {
588
+ while (ratios.length < count) {
589
+ ratios.push(1);
590
+ }
591
+ } else if (ratios.length > count) {
592
+ ratios = ratios.slice(0, count);
593
+ }
594
+
595
+ const sumRatios = ratios.reduce((a, b) => a + b, 0) || count;
596
+ const normalizedRatios = ratios.map(r => r / sumRatios);
597
+
598
+ const wBytes = weightsBytes ?? 0;
599
+ const kvBytes = kvCacheBytes ?? 0;
600
+ const actBytes = activationBytes ?? Math.round(DEFAULT_ACTIVATION_GB * GIB);
601
+ const ovBytes = overheadBytes ?? Math.round(DEFAULT_OVERHEAD_GB * GIB);
602
+ const mgHeadroomBytes = Math.round(Number(mainGpuScratchHeadroomGb || 2.0) * GIB);
603
+
604
+ const gpus = normalizedRatios.map((ratio, idx) => {
605
+ const isMain = idx === mg;
606
+ const gpuWeights = Math.round(wBytes * ratio);
607
+ const gpuKv = Math.round(kvBytes * ratio);
608
+ const gpuAct = isMain ? actBytes + mgHeadroomBytes : Math.round(actBytes * 0.5);
609
+ const gpuOv = ovBytes;
610
+ const gpuTotal = gpuWeights + gpuKv + gpuAct + gpuOv;
611
+
612
+ return {
613
+ gpuIndex: idx,
614
+ isMain,
615
+ ratio: Number((ratio * 100).toFixed(1)),
616
+ ratioRaw: ratios[idx],
617
+ weightsBytes: gpuWeights,
618
+ kvCacheBytes: gpuKv,
619
+ activationBytes: gpuAct,
620
+ overheadBytes: gpuOv,
621
+ totalBytes: gpuTotal,
622
+ gb: {
623
+ weights: Number((gpuWeights / GIB).toFixed(2)),
624
+ kvCache: Number((gpuKv / GIB).toFixed(2)),
625
+ activation: Number((gpuAct / GIB).toFixed(2)),
626
+ overhead: Number((gpuOv / GIB).toFixed(2)),
627
+ total: Number((gpuTotal / GIB).toFixed(2)),
628
+ },
629
+ };
630
+ });
631
+
632
+ return {
633
+ gpuCount: count,
634
+ mainGpu: mg,
635
+ tensorSplit: ratios.join(','),
636
+ gpus,
637
+ totalAllocatedBytes: gpus.reduce((acc, g) => acc + g.totalBytes, 0),
638
+ };
639
+ }
640
+