Demo支持完整UI快照;增加新的demo;其它小改进
Browse files- client/src/demos/gen_attribute/{翻译.json → CN->EN翻译.json} +0 -0
- client/src/demos/gen_attribute/Write a sonnet about love.json +236 -1
- client/src/demos/gen_attribute/写一首绝句,主题是春天.json +250 -1
- client/src/demos/gen_attribute/总结.json +0 -0
- client/src/demos/gen_attribute/过拟合|李白 将进酒.json +0 -0
- client/src/ts/attribution/attributionExcludePromptPatternsStorage.ts +3 -24
- client/src/ts/attribution/excludePromptPatternsUi.ts +2 -16
- client/src/ts/attribution/genAttributeDagPreprocess.ts +19 -9
- client/src/ts/attribution/genAttributeDagView.ts +28 -4
- client/src/ts/attribution/genAttributeDagViewSpiralMode.ts +2 -2
- client/src/ts/demos/genAttributeBundledDemoManifest.generated.ts +1 -1
- client/src/ts/demos/genAttributeBundledDemos.ts +9 -30
- client/src/ts/gen_attribute.ts +311 -39
- client/src/ts/lang/translations.ts +4 -0
- client/src/ts/storage/genAttributeRunCache.ts +232 -14
- client/src/ts/ui/dialog.ts +38 -9
client/src/demos/gen_attribute/{翻译.json → CN->EN翻译.json}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
client/src/demos/gen_attribute/Write a sonnet about love.json
CHANGED
|
@@ -117181,5 +117181,240 @@
|
|
| 117181 |
"token": "<|im_end|>",
|
| 117182 |
"currentText": "In twilight's hush, where shadows play, \nLove whispers soft, a gentle ray. \nIt blooms in hearts, a fleeting flame, \nThat lights the darkest, darkest lane. \n\nIt seeks the soul, with tender grace, \nTo mend the wounds, to heal the face. \nThrough joy and sorrow, it remains, \nA constant flame in life's vast sea. \n\nYet love is not a rose that fades, \nBut seeds that grow in time and space. \nIt bends, it breaks, it grows again, \nA promise etched in every pain. \n\nSo let the world proclaim its might, \nBut in the heart, love's truest light.<|im_end|>"
|
| 117183 |
}
|
| 117184 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117185 |
}
|
|
|
|
| 117181 |
"token": "<|im_end|>",
|
| 117182 |
"currentText": "In twilight's hush, where shadows play, \nLove whispers soft, a gentle ray. \nIt blooms in hearts, a fleeting flame, \nThat lights the darkest, darkest lane. \n\nIt seeks the soul, with tender grace, \nTo mend the wounds, to heal the face. \nThrough joy and sorrow, it remains, \nA constant flame in life's vast sea. \n\nYet love is not a rose that fades, \nBut seeds that grow in time and space. \nIt bends, it breaks, it grows again, \nA promise etched in every pain. \n\nSo let the world proclaim its might, \nBut in the heart, love's truest light.<|im_end|>"
|
| 117183 |
}
|
| 117184 |
+
],
|
| 117185 |
+
"promptSpans": [
|
| 117186 |
+
{
|
| 117187 |
+
"offset": [
|
| 117188 |
+
0,
|
| 117189 |
+
12
|
| 117190 |
+
],
|
| 117191 |
+
"raw": "<|im_start|>"
|
| 117192 |
+
},
|
| 117193 |
+
{
|
| 117194 |
+
"offset": [
|
| 117195 |
+
12,
|
| 117196 |
+
18
|
| 117197 |
+
],
|
| 117198 |
+
"raw": "system"
|
| 117199 |
+
},
|
| 117200 |
+
{
|
| 117201 |
+
"offset": [
|
| 117202 |
+
18,
|
| 117203 |
+
19
|
| 117204 |
+
],
|
| 117205 |
+
"raw": "\n"
|
| 117206 |
+
},
|
| 117207 |
+
{
|
| 117208 |
+
"offset": [
|
| 117209 |
+
19,
|
| 117210 |
+
22
|
| 117211 |
+
],
|
| 117212 |
+
"raw": "You"
|
| 117213 |
+
},
|
| 117214 |
+
{
|
| 117215 |
+
"offset": [
|
| 117216 |
+
22,
|
| 117217 |
+
26
|
| 117218 |
+
],
|
| 117219 |
+
"raw": " are"
|
| 117220 |
+
},
|
| 117221 |
+
{
|
| 117222 |
+
"offset": [
|
| 117223 |
+
26,
|
| 117224 |
+
28
|
| 117225 |
+
],
|
| 117226 |
+
"raw": " a"
|
| 117227 |
+
},
|
| 117228 |
+
{
|
| 117229 |
+
"offset": [
|
| 117230 |
+
28,
|
| 117231 |
+
36
|
| 117232 |
+
],
|
| 117233 |
+
"raw": " helpful"
|
| 117234 |
+
},
|
| 117235 |
+
{
|
| 117236 |
+
"offset": [
|
| 117237 |
+
36,
|
| 117238 |
+
46
|
| 117239 |
+
],
|
| 117240 |
+
"raw": " assistant"
|
| 117241 |
+
},
|
| 117242 |
+
{
|
| 117243 |
+
"offset": [
|
| 117244 |
+
46,
|
| 117245 |
+
47
|
| 117246 |
+
],
|
| 117247 |
+
"raw": "."
|
| 117248 |
+
},
|
| 117249 |
+
{
|
| 117250 |
+
"offset": [
|
| 117251 |
+
47,
|
| 117252 |
+
57
|
| 117253 |
+
],
|
| 117254 |
+
"raw": "<|im_end|>"
|
| 117255 |
+
},
|
| 117256 |
+
{
|
| 117257 |
+
"offset": [
|
| 117258 |
+
57,
|
| 117259 |
+
58
|
| 117260 |
+
],
|
| 117261 |
+
"raw": "\n"
|
| 117262 |
+
},
|
| 117263 |
+
{
|
| 117264 |
+
"offset": [
|
| 117265 |
+
58,
|
| 117266 |
+
70
|
| 117267 |
+
],
|
| 117268 |
+
"raw": "<|im_start|>"
|
| 117269 |
+
},
|
| 117270 |
+
{
|
| 117271 |
+
"offset": [
|
| 117272 |
+
70,
|
| 117273 |
+
74
|
| 117274 |
+
],
|
| 117275 |
+
"raw": "user"
|
| 117276 |
+
},
|
| 117277 |
+
{
|
| 117278 |
+
"offset": [
|
| 117279 |
+
74,
|
| 117280 |
+
75
|
| 117281 |
+
],
|
| 117282 |
+
"raw": "\n"
|
| 117283 |
+
},
|
| 117284 |
+
{
|
| 117285 |
+
"offset": [
|
| 117286 |
+
75,
|
| 117287 |
+
80
|
| 117288 |
+
],
|
| 117289 |
+
"raw": "Write"
|
| 117290 |
+
},
|
| 117291 |
+
{
|
| 117292 |
+
"offset": [
|
| 117293 |
+
80,
|
| 117294 |
+
82
|
| 117295 |
+
],
|
| 117296 |
+
"raw": " a"
|
| 117297 |
+
},
|
| 117298 |
+
{
|
| 117299 |
+
"offset": [
|
| 117300 |
+
82,
|
| 117301 |
+
86
|
| 117302 |
+
],
|
| 117303 |
+
"raw": " son"
|
| 117304 |
+
},
|
| 117305 |
+
{
|
| 117306 |
+
"offset": [
|
| 117307 |
+
86,
|
| 117308 |
+
89
|
| 117309 |
+
],
|
| 117310 |
+
"raw": "net"
|
| 117311 |
+
},
|
| 117312 |
+
{
|
| 117313 |
+
"offset": [
|
| 117314 |
+
89,
|
| 117315 |
+
95
|
| 117316 |
+
],
|
| 117317 |
+
"raw": " about"
|
| 117318 |
+
},
|
| 117319 |
+
{
|
| 117320 |
+
"offset": [
|
| 117321 |
+
95,
|
| 117322 |
+
100
|
| 117323 |
+
],
|
| 117324 |
+
"raw": " love"
|
| 117325 |
+
},
|
| 117326 |
+
{
|
| 117327 |
+
"offset": [
|
| 117328 |
+
100,
|
| 117329 |
+
101
|
| 117330 |
+
],
|
| 117331 |
+
"raw": "."
|
| 117332 |
+
},
|
| 117333 |
+
{
|
| 117334 |
+
"offset": [
|
| 117335 |
+
101,
|
| 117336 |
+
111
|
| 117337 |
+
],
|
| 117338 |
+
"raw": "<|im_end|>"
|
| 117339 |
+
},
|
| 117340 |
+
{
|
| 117341 |
+
"offset": [
|
| 117342 |
+
111,
|
| 117343 |
+
112
|
| 117344 |
+
],
|
| 117345 |
+
"raw": "\n"
|
| 117346 |
+
},
|
| 117347 |
+
{
|
| 117348 |
+
"offset": [
|
| 117349 |
+
112,
|
| 117350 |
+
124
|
| 117351 |
+
],
|
| 117352 |
+
"raw": "<|im_start|>"
|
| 117353 |
+
},
|
| 117354 |
+
{
|
| 117355 |
+
"offset": [
|
| 117356 |
+
124,
|
| 117357 |
+
133
|
| 117358 |
+
],
|
| 117359 |
+
"raw": "assistant"
|
| 117360 |
+
},
|
| 117361 |
+
{
|
| 117362 |
+
"offset": [
|
| 117363 |
+
133,
|
| 117364 |
+
134
|
| 117365 |
+
],
|
| 117366 |
+
"raw": "\n"
|
| 117367 |
+
},
|
| 117368 |
+
{
|
| 117369 |
+
"offset": [
|
| 117370 |
+
134,
|
| 117371 |
+
141
|
| 117372 |
+
],
|
| 117373 |
+
"raw": "<think>"
|
| 117374 |
+
},
|
| 117375 |
+
{
|
| 117376 |
+
"offset": [
|
| 117377 |
+
141,
|
| 117378 |
+
143
|
| 117379 |
+
],
|
| 117380 |
+
"raw": "\n\n"
|
| 117381 |
+
},
|
| 117382 |
+
{
|
| 117383 |
+
"offset": [
|
| 117384 |
+
143,
|
| 117385 |
+
151
|
| 117386 |
+
],
|
| 117387 |
+
"raw": "</think>"
|
| 117388 |
+
},
|
| 117389 |
+
{
|
| 117390 |
+
"offset": [
|
| 117391 |
+
151,
|
| 117392 |
+
153
|
| 117393 |
+
],
|
| 117394 |
+
"raw": "\n\n"
|
| 117395 |
+
}
|
| 117396 |
+
],
|
| 117397 |
+
"draft": {
|
| 117398 |
+
"mode": "raw",
|
| 117399 |
+
"model": "base",
|
| 117400 |
+
"maxTokens": 200
|
| 117401 |
+
},
|
| 117402 |
+
"demoUiOptions": {
|
| 117403 |
+
"layoutMode": "text-flow",
|
| 117404 |
+
"measureWidthPx": 400,
|
| 117405 |
+
"dagCompactness": 0.6,
|
| 117406 |
+
"linearArcAdjacentGapPx": 0,
|
| 117407 |
+
"hideExcludedTokens": false,
|
| 117408 |
+
"edgeTopPCoverage": 0.7,
|
| 117409 |
+
"nodeCiVisualScaleEnabled": true,
|
| 117410 |
+
"edgeWeakenHighSurprisalEnabled": true,
|
| 117411 |
+
"hideInactiveEdges": false,
|
| 117412 |
+
"replayPacingMode": "step",
|
| 117413 |
+
"playbackTotalS": 7,
|
| 117414 |
+
"playbackStepMs": 100,
|
| 117415 |
+
"excludePromptPatternsEnabled": true,
|
| 117416 |
+
"excludePromptPatternsText": "#comment# use '#comment#' to comment lines; support regex\n<\\|im_start\\|>system\\n\n<\\|im_start\\|>user\\n\n<\\|im_start\\|>assistant\\n\n<\\|im_start\\|>assistant\\n\\n\n<\\|im_end\\|>\\n\n<think>\\n\\n\n</think>\\n\\n\n<\\|im_start\\|>system\\n[\\s\\S]*?<\\|im_end\\|>#comment# all system prompt",
|
| 117417 |
+
"excludeGeneratedPatternsEnabled": true,
|
| 117418 |
+
"excludeGeneratedPatternsText": "<think>\\n\n</think>\\n\\n"
|
| 117419 |
+
}
|
| 117420 |
}
|
client/src/demos/gen_attribute/写一首绝句,主题是春天.json
CHANGED
|
@@ -15733,5 +15733,254 @@
|
|
| 15733 |
"token": "<|im_end|>",
|
| 15734 |
"currentText": "春色满园花满枝, \n轻风拂面柳依依。 \n桃红柳绿新晴日, \n一缕芬芳入梦时。<|im_end|>"
|
| 15735 |
}
|
| 15736 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15737 |
}
|
|
|
|
| 15733 |
"token": "<|im_end|>",
|
| 15734 |
"currentText": "春色满园花满枝, \n轻风拂面柳依依。 \n桃红柳绿新晴日, \n一缕芬芳入梦时。<|im_end|>"
|
| 15735 |
}
|
| 15736 |
+
],
|
| 15737 |
+
"promptSpans": [
|
| 15738 |
+
{
|
| 15739 |
+
"offset": [
|
| 15740 |
+
0,
|
| 15741 |
+
12
|
| 15742 |
+
],
|
| 15743 |
+
"raw": "<|im_start|>"
|
| 15744 |
+
},
|
| 15745 |
+
{
|
| 15746 |
+
"offset": [
|
| 15747 |
+
12,
|
| 15748 |
+
18
|
| 15749 |
+
],
|
| 15750 |
+
"raw": "system"
|
| 15751 |
+
},
|
| 15752 |
+
{
|
| 15753 |
+
"offset": [
|
| 15754 |
+
18,
|
| 15755 |
+
19
|
| 15756 |
+
],
|
| 15757 |
+
"raw": "\n"
|
| 15758 |
+
},
|
| 15759 |
+
{
|
| 15760 |
+
"offset": [
|
| 15761 |
+
19,
|
| 15762 |
+
22
|
| 15763 |
+
],
|
| 15764 |
+
"raw": "You"
|
| 15765 |
+
},
|
| 15766 |
+
{
|
| 15767 |
+
"offset": [
|
| 15768 |
+
22,
|
| 15769 |
+
26
|
| 15770 |
+
],
|
| 15771 |
+
"raw": " are"
|
| 15772 |
+
},
|
| 15773 |
+
{
|
| 15774 |
+
"offset": [
|
| 15775 |
+
26,
|
| 15776 |
+
28
|
| 15777 |
+
],
|
| 15778 |
+
"raw": " a"
|
| 15779 |
+
},
|
| 15780 |
+
{
|
| 15781 |
+
"offset": [
|
| 15782 |
+
28,
|
| 15783 |
+
36
|
| 15784 |
+
],
|
| 15785 |
+
"raw": " helpful"
|
| 15786 |
+
},
|
| 15787 |
+
{
|
| 15788 |
+
"offset": [
|
| 15789 |
+
36,
|
| 15790 |
+
46
|
| 15791 |
+
],
|
| 15792 |
+
"raw": " assistant"
|
| 15793 |
+
},
|
| 15794 |
+
{
|
| 15795 |
+
"offset": [
|
| 15796 |
+
46,
|
| 15797 |
+
47
|
| 15798 |
+
],
|
| 15799 |
+
"raw": "."
|
| 15800 |
+
},
|
| 15801 |
+
{
|
| 15802 |
+
"offset": [
|
| 15803 |
+
47,
|
| 15804 |
+
57
|
| 15805 |
+
],
|
| 15806 |
+
"raw": "<|im_end|>"
|
| 15807 |
+
},
|
| 15808 |
+
{
|
| 15809 |
+
"offset": [
|
| 15810 |
+
57,
|
| 15811 |
+
58
|
| 15812 |
+
],
|
| 15813 |
+
"raw": "\n"
|
| 15814 |
+
},
|
| 15815 |
+
{
|
| 15816 |
+
"offset": [
|
| 15817 |
+
58,
|
| 15818 |
+
70
|
| 15819 |
+
],
|
| 15820 |
+
"raw": "<|im_start|>"
|
| 15821 |
+
},
|
| 15822 |
+
{
|
| 15823 |
+
"offset": [
|
| 15824 |
+
70,
|
| 15825 |
+
74
|
| 15826 |
+
],
|
| 15827 |
+
"raw": "user"
|
| 15828 |
+
},
|
| 15829 |
+
{
|
| 15830 |
+
"offset": [
|
| 15831 |
+
74,
|
| 15832 |
+
75
|
| 15833 |
+
],
|
| 15834 |
+
"raw": "\n"
|
| 15835 |
+
},
|
| 15836 |
+
{
|
| 15837 |
+
"offset": [
|
| 15838 |
+
75,
|
| 15839 |
+
76
|
| 15840 |
+
],
|
| 15841 |
+
"raw": "写"
|
| 15842 |
+
},
|
| 15843 |
+
{
|
| 15844 |
+
"offset": [
|
| 15845 |
+
76,
|
| 15846 |
+
78
|
| 15847 |
+
],
|
| 15848 |
+
"raw": "一首"
|
| 15849 |
+
},
|
| 15850 |
+
{
|
| 15851 |
+
"offset": [
|
| 15852 |
+
78,
|
| 15853 |
+
79
|
| 15854 |
+
],
|
| 15855 |
+
"raw": "绝"
|
| 15856 |
+
},
|
| 15857 |
+
{
|
| 15858 |
+
"offset": [
|
| 15859 |
+
79,
|
| 15860 |
+
80
|
| 15861 |
+
],
|
| 15862 |
+
"raw": "句"
|
| 15863 |
+
},
|
| 15864 |
+
{
|
| 15865 |
+
"offset": [
|
| 15866 |
+
80,
|
| 15867 |
+
81
|
| 15868 |
+
],
|
| 15869 |
+
"raw": ","
|
| 15870 |
+
},
|
| 15871 |
+
{
|
| 15872 |
+
"offset": [
|
| 15873 |
+
81,
|
| 15874 |
+
83
|
| 15875 |
+
],
|
| 15876 |
+
"raw": "主题"
|
| 15877 |
+
},
|
| 15878 |
+
{
|
| 15879 |
+
"offset": [
|
| 15880 |
+
83,
|
| 15881 |
+
84
|
| 15882 |
+
],
|
| 15883 |
+
"raw": "是"
|
| 15884 |
+
},
|
| 15885 |
+
{
|
| 15886 |
+
"offset": [
|
| 15887 |
+
84,
|
| 15888 |
+
86
|
| 15889 |
+
],
|
| 15890 |
+
"raw": "春天"
|
| 15891 |
+
},
|
| 15892 |
+
{
|
| 15893 |
+
"offset": [
|
| 15894 |
+
86,
|
| 15895 |
+
87
|
| 15896 |
+
],
|
| 15897 |
+
"raw": "。"
|
| 15898 |
+
},
|
| 15899 |
+
{
|
| 15900 |
+
"offset": [
|
| 15901 |
+
87,
|
| 15902 |
+
97
|
| 15903 |
+
],
|
| 15904 |
+
"raw": "<|im_end|>"
|
| 15905 |
+
},
|
| 15906 |
+
{
|
| 15907 |
+
"offset": [
|
| 15908 |
+
97,
|
| 15909 |
+
98
|
| 15910 |
+
],
|
| 15911 |
+
"raw": "\n"
|
| 15912 |
+
},
|
| 15913 |
+
{
|
| 15914 |
+
"offset": [
|
| 15915 |
+
98,
|
| 15916 |
+
110
|
| 15917 |
+
],
|
| 15918 |
+
"raw": "<|im_start|>"
|
| 15919 |
+
},
|
| 15920 |
+
{
|
| 15921 |
+
"offset": [
|
| 15922 |
+
110,
|
| 15923 |
+
119
|
| 15924 |
+
],
|
| 15925 |
+
"raw": "assistant"
|
| 15926 |
+
},
|
| 15927 |
+
{
|
| 15928 |
+
"offset": [
|
| 15929 |
+
119,
|
| 15930 |
+
120
|
| 15931 |
+
],
|
| 15932 |
+
"raw": "\n"
|
| 15933 |
+
},
|
| 15934 |
+
{
|
| 15935 |
+
"offset": [
|
| 15936 |
+
120,
|
| 15937 |
+
127
|
| 15938 |
+
],
|
| 15939 |
+
"raw": "<think>"
|
| 15940 |
+
},
|
| 15941 |
+
{
|
| 15942 |
+
"offset": [
|
| 15943 |
+
127,
|
| 15944 |
+
129
|
| 15945 |
+
],
|
| 15946 |
+
"raw": "\n\n"
|
| 15947 |
+
},
|
| 15948 |
+
{
|
| 15949 |
+
"offset": [
|
| 15950 |
+
129,
|
| 15951 |
+
137
|
| 15952 |
+
],
|
| 15953 |
+
"raw": "</think>"
|
| 15954 |
+
},
|
| 15955 |
+
{
|
| 15956 |
+
"offset": [
|
| 15957 |
+
137,
|
| 15958 |
+
139
|
| 15959 |
+
],
|
| 15960 |
+
"raw": "\n\n"
|
| 15961 |
+
}
|
| 15962 |
+
],
|
| 15963 |
+
"draft": {
|
| 15964 |
+
"mode": "raw",
|
| 15965 |
+
"model": "base",
|
| 15966 |
+
"maxTokens": 200
|
| 15967 |
+
},
|
| 15968 |
+
"demoUiOptions": {
|
| 15969 |
+
"layoutMode": "text-flow",
|
| 15970 |
+
"measureWidthPx": 400,
|
| 15971 |
+
"dagCompactness": 0.5,
|
| 15972 |
+
"linearArcAdjacentGapPx": 0,
|
| 15973 |
+
"hideExcludedTokens": false,
|
| 15974 |
+
"edgeTopPCoverage": 0.7,
|
| 15975 |
+
"nodeCiVisualScaleEnabled": true,
|
| 15976 |
+
"edgeWeakenHighSurprisalEnabled": true,
|
| 15977 |
+
"hideInactiveEdges": false,
|
| 15978 |
+
"replayPacingMode": "step",
|
| 15979 |
+
"playbackTotalS": 7,
|
| 15980 |
+
"playbackStepMs": 200,
|
| 15981 |
+
"excludePromptPatternsEnabled": true,
|
| 15982 |
+
"excludePromptPatternsText": "#comment# use '#comment#' to comment lines; support regex\n<\\|im_start\\|>system\\n\n<\\|im_start\\|>user\\n\n<\\|im_start\\|>assistant\\n\n<\\|im_start\\|>assistant\\n\\n\n<\\|im_end\\|>\\n\n<think>\\n\\n\n</think>\\n\\n\n<\\|im_start\\|>system\\n[\\s\\S]*?<\\|im_end\\|>#comment# all system prompt",
|
| 15983 |
+
"excludeGeneratedPatternsEnabled": true,
|
| 15984 |
+
"excludeGeneratedPatternsText": "<think>\\n\n</think>\\n\\n"
|
| 15985 |
+
}
|
| 15986 |
}
|
client/src/demos/gen_attribute/总结.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
client/src/demos/gen_attribute/过拟合|李白 将进酒.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
client/src/ts/attribution/attributionExcludePromptPatternsStorage.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
| 1 |
/**
|
| 2 |
-
*
|
| 3 |
-
* Prompt 键名保留历史前缀 `exclude_tokens`
|
| 4 |
*/
|
| 5 |
export const EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY = 'info_radar_attribution_exclude_tokens';
|
| 6 |
export const EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY = 'info_radar_attribution_exclude_tokens_enabled';
|
| 7 |
|
| 8 |
-
export const EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY = 'info_radar_attribution_exclude_generated_tokens';
|
| 9 |
-
export const EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY =
|
| 10 |
-
'info_radar_attribution_exclude_generated_tokens_enabled';
|
| 11 |
-
|
| 12 |
/**
|
| 13 |
* 首次使用(`exclude_tokens` 键从未写入)时 UI 与生效逻辑采用的默认行;`''` 表示用户已显式清空,不再使用本默认。
|
| 14 |
* 与 {@link readStoredEffectiveExcludePromptPatternsText} 的 `null` / 有值区分一致。
|
|
@@ -26,7 +22,7 @@ export const DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT = [
|
|
| 26 |
].join('\n');
|
| 27 |
|
| 28 |
/**
|
| 29 |
-
*
|
| 30 |
*/
|
| 31 |
export const DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT = [
|
| 32 |
'<think>\\n',
|
|
@@ -49,20 +45,3 @@ export function readStoredEffectiveExcludePromptPatternsText(): string {
|
|
| 49 |
return '';
|
| 50 |
}
|
| 51 |
}
|
| 52 |
-
|
| 53 |
-
/**
|
| 54 |
-
* Generate & Attribute「Exclude generated patterns」:未持久化文本键(`null`)时用
|
| 55 |
-
* {@link DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT};已持久化空串表示用户选择排除零条模式。
|
| 56 |
-
*/
|
| 57 |
-
export function readStoredEffectiveExcludeGeneratedPatternsText(): string {
|
| 58 |
-
try {
|
| 59 |
-
const enabledRaw = localStorage.getItem(EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY);
|
| 60 |
-
const enabled = enabledRaw === null ? true : enabledRaw === '1';
|
| 61 |
-
if (!enabled) return '';
|
| 62 |
-
const raw = localStorage.getItem(EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY);
|
| 63 |
-
if (raw === null) return DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT;
|
| 64 |
-
return raw;
|
| 65 |
-
} catch {
|
| 66 |
-
return '';
|
| 67 |
-
}
|
| 68 |
-
}
|
|
|
|
| 1 |
/**
|
| 2 |
+
* 归因页 Exclude prompt:`bindExcludePromptPatternsUi` / {@link readStoredEffectiveExcludePromptPatternsText} 共用键名。
|
| 3 |
+
* Prompt 键名保留历史前缀 `exclude_tokens`。Generate & Attribute 页的排除正则使用独立 `info_radar_gen_attr_exclude_*`,与此处解耦。
|
| 4 |
*/
|
| 5 |
export const EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY = 'info_radar_attribution_exclude_tokens';
|
| 6 |
export const EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY = 'info_radar_attribution_exclude_tokens_enabled';
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
/**
|
| 9 |
* 首次使用(`exclude_tokens` 键从未写入)时 UI 与生效逻辑采用的默认行;`''` 表示用户已显式清空,不再使用本默认。
|
| 10 |
* 与 {@link readStoredEffectiveExcludePromptPatternsText} 的 `null` / 有值区分一致。
|
|
|
|
| 22 |
].join('\n');
|
| 23 |
|
| 24 |
/**
|
| 25 |
+
* Generate & Attribute「Exclude generated」占位默认文案;该页的存储键前缀为 `info_radar_gen_attr_exclude_generated_*`。
|
| 26 |
*/
|
| 27 |
export const DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT = [
|
| 28 |
'<think>\\n',
|
|
|
|
| 45 |
return '';
|
| 46 |
}
|
| 47 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client/src/ts/attribution/excludePromptPatternsUi.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
| 1 |
import {
|
| 2 |
-
DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT,
|
| 3 |
DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT,
|
| 4 |
-
EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY,
|
| 5 |
-
EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY,
|
| 6 |
EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY,
|
| 7 |
EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY,
|
| 8 |
} from './attributionExcludePromptPatternsStorage';
|
|
@@ -92,7 +89,8 @@ function bindExcludePatternsUi(options: BindExcludePatternsUiOptions): void {
|
|
| 92 |
}
|
| 93 |
|
| 94 |
/**
|
| 95 |
-
* Exclude prompt patterns:
|
|
|
|
| 96 |
*/
|
| 97 |
export function bindExcludePromptPatternsUi(options: BindExcludePromptPatternsUiOptions): void {
|
| 98 |
bindExcludePatternsUi({
|
|
@@ -104,15 +102,3 @@ export function bindExcludePromptPatternsUi(options: BindExcludePromptPatternsUi
|
|
| 104 |
defaultTextWhenKeyAbsent: DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT,
|
| 105 |
});
|
| 106 |
}
|
| 107 |
-
|
| 108 |
-
/** Exclude generated patterns:仅 Generate & Attribute 页绑定;storage 键见 {@link attributionExcludePromptPatternsStorage}。 */
|
| 109 |
-
export function bindExcludeGeneratedPatternsUi(options: BindExcludePromptPatternsUiOptions): void {
|
| 110 |
-
bindExcludePatternsUi({
|
| 111 |
-
storageKeys: {
|
| 112 |
-
textKey: EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY,
|
| 113 |
-
enabledKey: EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY,
|
| 114 |
-
},
|
| 115 |
-
...options,
|
| 116 |
-
defaultTextWhenKeyAbsent: DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT,
|
| 117 |
-
});
|
| 118 |
-
}
|
|
|
|
| 1 |
import {
|
|
|
|
| 2 |
DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT,
|
|
|
|
|
|
|
| 3 |
EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY,
|
| 4 |
EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY,
|
| 5 |
} from './attributionExcludePromptPatternsStorage';
|
|
|
|
| 89 |
}
|
| 90 |
|
| 91 |
/**
|
| 92 |
+
* Exclude prompt patterns:归因页绑定;键名见 {@link ./attributionExcludePromptPatternsStorage}。
|
| 93 |
+
* Generate & Attribute 页的排除在 `gen_attribute.ts` 内单独绑定(`info_radar_gen_attr_exclude_*`)。
|
| 94 |
*/
|
| 95 |
export function bindExcludePromptPatternsUi(options: BindExcludePromptPatternsUiOptions): void {
|
| 96 |
bindExcludePatternsUi({
|
|
|
|
| 102 |
defaultTextWhenKeyAbsent: DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT,
|
| 103 |
});
|
| 104 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client/src/ts/attribution/genAttributeDagPreprocess.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
| 1 |
-
import {
|
| 2 |
-
readStoredEffectiveExcludeGeneratedPatternsText,
|
| 3 |
-
readStoredEffectiveExcludePromptPatternsText,
|
| 4 |
-
} from './attributionExcludePromptPatternsStorage';
|
| 5 |
import {
|
| 6 |
collectExcludeRegexMatchIntervals,
|
| 7 |
isOffsetSpanFullyExcluded,
|
|
@@ -120,18 +116,23 @@ export function extractPromptTokenSpans(step: TokenGenStep): PromptTokenSpan[] {
|
|
| 120 |
return [...byKey.values()];
|
| 121 |
}
|
| 122 |
|
| 123 |
-
/**
|
|
|
|
|
|
|
|
|
|
| 124 |
export function collectGenAttrDagExcludeIntervals(
|
| 125 |
intervalCtx: string,
|
| 126 |
promptRegionEnd: number,
|
|
|
|
|
|
|
| 127 |
): [number, number][] {
|
| 128 |
const pe = promptRegionEnd;
|
| 129 |
return [
|
| 130 |
-
...collectExcludeRegexMatchIntervals(intervalCtx,
|
| 131 |
start: 0,
|
| 132 |
end: pe,
|
| 133 |
}),
|
| 134 |
-
...collectExcludeRegexMatchIntervals(intervalCtx,
|
| 135 |
start: pe,
|
| 136 |
end: intervalCtx.length,
|
| 137 |
}),
|
|
@@ -144,17 +145,26 @@ export function collectGenAttrDagExcludeIntervals(
|
|
| 144 |
*
|
| 145 |
* @param excludeIntervalContext 取匹配区间所用的全文(与 DAG 节点 offset 同源)。流式场景传**当前已写出的累积串**
|
| 146 |
*(如 `steps[last].context + steps[last].token`),使跨多 token 才闭合的正则与下标一致;缺省为 `step.context`。
|
|
|
|
|
|
|
| 147 |
*/
|
| 148 |
export function excludeNodeAggregatedEntries(
|
| 149 |
step: TokenGenStep,
|
| 150 |
entries: NodeAggregatedEntry[],
|
| 151 |
-
excludeIntervalContext
|
|
|
|
|
|
|
| 152 |
): NodeAggregatedEntry[] {
|
| 153 |
if (!entries.length) return [];
|
| 154 |
|
| 155 |
const pe = step.promptRegionEnd;
|
| 156 |
const intervalCtx = excludeIntervalContext ?? step.context;
|
| 157 |
-
const excludeIntervals = collectGenAttrDagExcludeIntervals(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
return entries.map((t) => {
|
| 159 |
const [ts, te] = t.offset;
|
| 160 |
const excluded = isOffsetSpanFullyExcluded(ts, te, excludeIntervals);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import {
|
| 2 |
collectExcludeRegexMatchIntervals,
|
| 3 |
isOffsetSpanFullyExcluded,
|
|
|
|
| 116 |
return [...byKey.values()];
|
| 117 |
}
|
| 118 |
|
| 119 |
+
/**
|
| 120 |
+
* 在 `intervalCtx` 上收集排除区间(全串下标);正则全文由调用方提供(Gen Attribute 页与控件一致)。
|
| 121 |
+
* 与 {@link excludeNodeAggregatedEntries} 须传入同一套 `excludePromptPatternsText` / `excludeGeneratedPatternsText`。
|
| 122 |
+
*/
|
| 123 |
export function collectGenAttrDagExcludeIntervals(
|
| 124 |
intervalCtx: string,
|
| 125 |
promptRegionEnd: number,
|
| 126 |
+
excludePromptPatternsText: string,
|
| 127 |
+
excludeGeneratedPatternsText: string,
|
| 128 |
): [number, number][] {
|
| 129 |
const pe = promptRegionEnd;
|
| 130 |
return [
|
| 131 |
+
...collectExcludeRegexMatchIntervals(intervalCtx, excludePromptPatternsText, {
|
| 132 |
start: 0,
|
| 133 |
end: pe,
|
| 134 |
}),
|
| 135 |
+
...collectExcludeRegexMatchIntervals(intervalCtx, excludeGeneratedPatternsText, {
|
| 136 |
start: pe,
|
| 137 |
end: intervalCtx.length,
|
| 138 |
}),
|
|
|
|
| 145 |
*
|
| 146 |
* @param excludeIntervalContext 取匹配区间所用的全文(与 DAG 节点 offset 同源)。流式场景传**当前已写出的累积串**
|
| 147 |
*(如 `steps[last].context + steps[last].token`),使跨多 token 才闭合的正则与下标一致;缺省为 `step.context`。
|
| 148 |
+
* @param excludePromptPatternsText prompt 区 `[0, promptRegionEnd)` 上使用的排除正则全文(勾选关时传 `''`)。
|
| 149 |
+
* @param excludeGeneratedPatternsText 已生成后缀区上使用的排除正则全文(勾选关时传 `''`)。
|
| 150 |
*/
|
| 151 |
export function excludeNodeAggregatedEntries(
|
| 152 |
step: TokenGenStep,
|
| 153 |
entries: NodeAggregatedEntry[],
|
| 154 |
+
excludeIntervalContext: string | undefined,
|
| 155 |
+
excludePromptPatternsText: string,
|
| 156 |
+
excludeGeneratedPatternsText: string,
|
| 157 |
): NodeAggregatedEntry[] {
|
| 158 |
if (!entries.length) return [];
|
| 159 |
|
| 160 |
const pe = step.promptRegionEnd;
|
| 161 |
const intervalCtx = excludeIntervalContext ?? step.context;
|
| 162 |
+
const excludeIntervals = collectGenAttrDagExcludeIntervals(
|
| 163 |
+
intervalCtx,
|
| 164 |
+
pe,
|
| 165 |
+
excludePromptPatternsText,
|
| 166 |
+
excludeGeneratedPatternsText,
|
| 167 |
+
);
|
| 168 |
return entries.map((t) => {
|
| 169 |
const [ts, te] = t.offset;
|
| 170 |
const excluded = isOffsetSpanFullyExcluded(ts, te, excludeIntervals);
|
client/src/ts/attribution/genAttributeDagView.ts
CHANGED
|
@@ -572,11 +572,17 @@ export type InitGenAttributeDagViewOptions = {
|
|
| 572 |
edgeTopPCoverage?: number;
|
| 573 |
/** 进入/退出/切换全屏失败时(常见于移动端不支持元素全屏等)。不传则无提示。 */
|
| 574 |
onFullscreenError?: (message: string) => void;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 575 |
};
|
| 576 |
|
| 577 |
export function initGenAttributeDagView(
|
| 578 |
resultsRoot: D3Sel,
|
| 579 |
-
options
|
| 580 |
): GenAttributeDagHandle {
|
| 581 |
const onDagRefresh = options?.onDagRefresh;
|
| 582 |
const onDagPlaybackToggle = options?.onDagPlaybackToggle;
|
|
@@ -631,6 +637,8 @@ export function initGenAttributeDagView(
|
|
| 631 |
};
|
| 632 |
}
|
| 633 |
|
|
|
|
|
|
|
| 634 |
detachGenAttributeDagPanel.get(rootEl)?.();
|
| 635 |
resultsRoot
|
| 636 |
.selectAll(
|
|
@@ -1089,7 +1097,12 @@ export function initGenAttributeDagView(
|
|
| 1089 |
const prevIdx = firstNewIdx + i - 1;
|
| 1090 |
snapSubwordNode(addedNodes[i]!, prevIdx >= 0 ? nodes[prevIdx]! : null);
|
| 1091 |
}
|
| 1092 |
-
dagExcludeIntervals = collectGenAttrDagExcludeIntervals(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1093 |
if (batchDepth === 0) syncGraphToSvg();
|
| 1094 |
}
|
| 1095 |
|
|
@@ -1150,7 +1163,13 @@ export function initGenAttributeDagView(
|
|
| 1150 |
step: stepProcessed,
|
| 1151 |
targetToken: token,
|
| 1152 |
});
|
| 1153 |
-
const afterExclude = excludeNodeAggregatedEntries(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1154 |
const selected = phase2RankAndSparsify(afterExclude, { cumulativeShare: edgeTopPCoverage });
|
| 1155 |
|
| 1156 |
const mutualInformationRatio = computeMutualInformationRatio(response.target_prob);
|
|
@@ -1197,7 +1216,12 @@ export function initGenAttributeDagView(
|
|
| 1197 |
});
|
| 1198 |
}
|
| 1199 |
|
| 1200 |
-
const excludeIntervals = collectGenAttrDagExcludeIntervals(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1201 |
dagExcludeIntervals = excludeIntervals;
|
| 1202 |
pruneDagLinksTouchingFullyExcludedNodes(graph, links, excludeIntervals);
|
| 1203 |
|
|
|
|
| 572 |
edgeTopPCoverage?: number;
|
| 573 |
/** 进入/退出/切换全屏失败时(常见于移动端不支持元素全屏等)。不传则无提示。 */
|
| 574 |
onFullscreenError?: (message: string) => void;
|
| 575 |
+
/**
|
| 576 |
+
* DAG 归因排除:prompt 区正则的**生效**全文(勾选关则 `''`)。须与 Gen Attribute 页控件同源(仅该页使用本视图)。
|
| 577 |
+
*/
|
| 578 |
+
getEffectiveExcludePromptPatternsText: () => string;
|
| 579 |
+
/** 已生成后缀区排除正则的生效全文(勾选关则 `''`)。 */
|
| 580 |
+
getEffectiveExcludeGeneratedPatternsText: () => string;
|
| 581 |
};
|
| 582 |
|
| 583 |
export function initGenAttributeDagView(
|
| 584 |
resultsRoot: D3Sel,
|
| 585 |
+
options: InitGenAttributeDagViewOptions
|
| 586 |
): GenAttributeDagHandle {
|
| 587 |
const onDagRefresh = options?.onDagRefresh;
|
| 588 |
const onDagPlaybackToggle = options?.onDagPlaybackToggle;
|
|
|
|
| 637 |
};
|
| 638 |
}
|
| 639 |
|
| 640 |
+
const { getEffectiveExcludePromptPatternsText, getEffectiveExcludeGeneratedPatternsText } = options;
|
| 641 |
+
|
| 642 |
detachGenAttributeDagPanel.get(rootEl)?.();
|
| 643 |
resultsRoot
|
| 644 |
.selectAll(
|
|
|
|
| 1097 |
const prevIdx = firstNewIdx + i - 1;
|
| 1098 |
snapSubwordNode(addedNodes[i]!, prevIdx >= 0 ? nodes[prevIdx]! : null);
|
| 1099 |
}
|
| 1100 |
+
dagExcludeIntervals = collectGenAttrDagExcludeIntervals(
|
| 1101 |
+
promptText,
|
| 1102 |
+
promptText.length,
|
| 1103 |
+
getEffectiveExcludePromptPatternsText(),
|
| 1104 |
+
getEffectiveExcludeGeneratedPatternsText(),
|
| 1105 |
+
);
|
| 1106 |
if (batchDepth === 0) syncGraphToSvg();
|
| 1107 |
}
|
| 1108 |
|
|
|
|
| 1163 |
step: stepProcessed,
|
| 1164 |
targetToken: token,
|
| 1165 |
});
|
| 1166 |
+
const afterExclude = excludeNodeAggregatedEntries(
|
| 1167 |
+
step,
|
| 1168 |
+
aggregated,
|
| 1169 |
+
excludeIntervalContext,
|
| 1170 |
+
getEffectiveExcludePromptPatternsText(),
|
| 1171 |
+
getEffectiveExcludeGeneratedPatternsText(),
|
| 1172 |
+
);
|
| 1173 |
const selected = phase2RankAndSparsify(afterExclude, { cumulativeShare: edgeTopPCoverage });
|
| 1174 |
|
| 1175 |
const mutualInformationRatio = computeMutualInformationRatio(response.target_prob);
|
|
|
|
| 1216 |
});
|
| 1217 |
}
|
| 1218 |
|
| 1219 |
+
const excludeIntervals = collectGenAttrDagExcludeIntervals(
|
| 1220 |
+
intervalCtx,
|
| 1221 |
+
step.promptRegionEnd,
|
| 1222 |
+
getEffectiveExcludePromptPatternsText(),
|
| 1223 |
+
getEffectiveExcludeGeneratedPatternsText(),
|
| 1224 |
+
);
|
| 1225 |
dagExcludeIntervals = excludeIntervals;
|
| 1226 |
pruneDagLinksTouchingFullyExcludedNodes(graph, links, excludeIntervals);
|
| 1227 |
|
client/src/ts/attribution/genAttributeDagViewSpiralMode.ts
CHANGED
|
@@ -7,11 +7,11 @@ const SPIRAL_R0 = 80;
|
|
| 7 |
/** 相邻两圈之间的径向间距(px)。 */
|
| 8 |
const SPIRAL_SPACING = 60;
|
| 9 |
/** 每个 token 沿螺旋弧长占据的固定步长(px)。 */
|
| 10 |
-
const SPIRAL_ARC_STEP =
|
| 11 |
/** 螺旋旋转相位(弧度):控制螺旋臂展开方向。0 = 向右,-Math.PI/2 = 向上。 */
|
| 12 |
const SPIRAL_PHASE = Math.PI * 0.6;
|
| 13 |
/** 螺旋上第一个(起始位置)token 的相对视觉放大倍数(仅 spiral 布局)。 */
|
| 14 |
-
const SPIRAL_FIRST_TOKEN_SCALE =
|
| 15 |
// ────────────────────────────────────────────────────────────────────────────
|
| 16 |
|
| 17 |
type SpiralNodeLike = { nodeW: number; nodeH: number };
|
|
|
|
| 7 |
/** 相邻两圈之间的径向间距(px)。 */
|
| 8 |
const SPIRAL_SPACING = 60;
|
| 9 |
/** 每个 token 沿螺旋弧长占据的固定步长(px)。 */
|
| 10 |
+
const SPIRAL_ARC_STEP = 30;
|
| 11 |
/** 螺旋旋转相位(弧度):控制螺旋臂展开方向。0 = 向右,-Math.PI/2 = 向上。 */
|
| 12 |
const SPIRAL_PHASE = Math.PI * 0.6;
|
| 13 |
/** 螺旋上第一个(起始位置)token 的相对视觉放大倍数(仅 spiral 布局)。 */
|
| 14 |
+
const SPIRAL_FIRST_TOKEN_SCALE = 1.5;
|
| 15 |
// ────────────────────────────────────────────────────────────────────────────
|
| 16 |
|
| 17 |
type SpiralNodeLike = { nodeW: number; nodeH: number };
|
client/src/ts/demos/genAttributeBundledDemoManifest.generated.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
/**
|
| 2 |
* Generated by GenAttributeDemoManifestPlugin — do not edit.
|
| 3 |
*/
|
| 4 |
-
export const GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS: readonly string[] = ["Write a sonnet about love","写一首绝句,主题是春天","
|
|
|
|
| 1 |
/**
|
| 2 |
* Generated by GenAttributeDemoManifestPlugin — do not edit.
|
| 3 |
*/
|
| 4 |
+
export const GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS: readonly string[] = ["CN->EN翻译","Write a sonnet about love","写一首绝句,主题是春天","过拟合|李白 将进酒"];
|
client/src/ts/demos/genAttributeBundledDemos.ts
CHANGED
|
@@ -2,8 +2,11 @@
|
|
| 2 |
* Gen Attribute 打包 demo:大 JSON 在 `dist/demos/gen_attribute/`,运行时 fetch;slug 列表构建期内联自 generated 模块。
|
| 3 |
*/
|
| 4 |
|
| 5 |
-
import
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
| 7 |
import { GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS } from './genAttributeBundledDemoManifest.generated';
|
| 8 |
|
| 9 |
const BASE = 'demos/gen_attribute/';
|
|
@@ -18,28 +21,6 @@ function isSafeDemoSlug(s: string): boolean {
|
|
| 18 |
return true;
|
| 19 |
}
|
| 20 |
|
| 21 |
-
function isValidGenAttrCachedRunPayload(v: unknown): v is GenAttrCachedRun {
|
| 22 |
-
if (v == null || typeof v !== 'object') return false;
|
| 23 |
-
const o = v as {
|
| 24 |
-
initialContext?: unknown;
|
| 25 |
-
steps?: unknown;
|
| 26 |
-
completionReason?: unknown;
|
| 27 |
-
};
|
| 28 |
-
if (
|
| 29 |
-
typeof o.initialContext !== 'string' ||
|
| 30 |
-
!Array.isArray(o.steps) ||
|
| 31 |
-
o.steps.length === 0
|
| 32 |
-
) {
|
| 33 |
-
return false;
|
| 34 |
-
}
|
| 35 |
-
if (o.completionReason !== undefined) {
|
| 36 |
-
if (typeof o.completionReason !== 'string' || !isKnownPersistedCompletionReason(o.completionReason)) {
|
| 37 |
-
return false;
|
| 38 |
-
}
|
| 39 |
-
}
|
| 40 |
-
return true;
|
| 41 |
-
}
|
| 42 |
-
|
| 43 |
const payloadCache = new Map<string, GenAttrCachedRun>();
|
| 44 |
const payloadInflight = new Map<string, Promise<GenAttrCachedRun | undefined>>();
|
| 45 |
|
|
@@ -67,12 +48,10 @@ export async function fetchBundledGenAttributeDemoBySlug(
|
|
| 67 |
const r = await fetch(fileUrl);
|
| 68 |
if (!r.ok) return undefined;
|
| 69 |
const raw: unknown = await r.json();
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
payloadCache.set(s, raw);
|
| 75 |
-
return raw;
|
| 76 |
})().finally(() => {
|
| 77 |
payloadInflight.delete(s);
|
| 78 |
});
|
|
|
|
| 2 |
* Gen Attribute 打包 demo:大 JSON 在 `dist/demos/gen_attribute/`,运行时 fetch;slug 列表构建期内联自 generated 模块。
|
| 3 |
*/
|
| 4 |
|
| 5 |
+
import {
|
| 6 |
+
isValidGenAttrCachedRunPayload,
|
| 7 |
+
parseGenAttrCachedRunPayload,
|
| 8 |
+
type GenAttrCachedRun,
|
| 9 |
+
} from '../storage/genAttributeRunCache';
|
| 10 |
import { GEN_ATTRIBUTE_BUNDLED_DEMO_SLUGS } from './genAttributeBundledDemoManifest.generated';
|
| 11 |
|
| 12 |
const BASE = 'demos/gen_attribute/';
|
|
|
|
| 21 |
return true;
|
| 22 |
}
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
const payloadCache = new Map<string, GenAttrCachedRun>();
|
| 25 |
const payloadInflight = new Map<string, Promise<GenAttrCachedRun | undefined>>();
|
| 26 |
|
|
|
|
| 48 |
const r = await fetch(fileUrl);
|
| 49 |
if (!r.ok) return undefined;
|
| 50 |
const raw: unknown = await r.json();
|
| 51 |
+
const parsed = parseGenAttrCachedRunPayload(raw, `bundled demo slug=${s}`);
|
| 52 |
+
if (!parsed) return undefined;
|
| 53 |
+
payloadCache.set(s, parsed);
|
| 54 |
+
return parsed;
|
|
|
|
|
|
|
| 55 |
})().finally(() => {
|
| 56 |
payloadInflight.delete(s);
|
| 57 |
});
|
client/src/ts/gen_attribute.ts
CHANGED
|
@@ -44,14 +44,21 @@ import { fetchTokenize } from './attribution/predictionAttributeClient';
|
|
| 44 |
import { completionFinishReasonLabel, type CompletionFinishReason } from './utils/generationEndReasonLabel';
|
| 45 |
import {
|
| 46 |
buildCachedContentUrlParam,
|
|
|
|
| 47 |
getCachedEntryByContentKey,
|
| 48 |
listCachedHistoryRows,
|
| 49 |
removeCachedEntryByContentKey,
|
| 50 |
save,
|
| 51 |
touchCachedEntryByContentKey,
|
|
|
|
|
|
|
| 52 |
type GenAttrCacheKey,
|
|
|
|
| 53 |
} from './storage/genAttributeRunCache';
|
| 54 |
-
import {
|
|
|
|
|
|
|
|
|
|
| 55 |
import { initCachedHistoryQueryDropdown, type CachedHistorySelectContext } from './utils/cachedHistoryUi';
|
| 56 |
import {
|
| 57 |
DEFAULT_CONTENT_URL_PARAM,
|
|
@@ -69,7 +76,6 @@ import {
|
|
| 69 |
} from './demos/genAttributeBundledDemos';
|
| 70 |
import { extractErrorMessage } from './utils/errorUtils';
|
| 71 |
import { exportJsonFile } from './storage/localFileIO';
|
| 72 |
-
import type { GenAttrCachedRun, GenAttrRunDraft } from './storage/genAttributeRunCache';
|
| 73 |
import {
|
| 74 |
GEN_ATTR_RAW_INPUT_HISTORY_KEY,
|
| 75 |
GEN_ATTR_SYSTEM_INPUT_HISTORY_KEY,
|
|
@@ -107,6 +113,13 @@ const GEN_ATTR_DAG_LINEAR_ARC_GAP_STORAGE_KEY =
|
|
| 107 |
'info_radar_gen_attr_dag_linear_arc_adjacent_gap';
|
| 108 |
const GEN_ATTR_DAG_COMPACTNESS_STORAGE_KEY = 'info_radar_gen_attr_dag_compactness';
|
| 109 |
const GEN_ATTR_DAG_EDGE_TOP_P_COVERAGE_STORAGE_KEY = 'info_radar_gen_attr_dag_edge_top_p_coverage';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
/** 步进回放节奏:`total`=整段剩余回放总时长内均分间隔;`step`=固定每步间隔(ms)。 */
|
| 112 |
type DagReplayPacingMode = 'total' | 'step';
|
|
@@ -392,8 +405,69 @@ const dagEdgeWeakenHighSurprisalInput = document.getElementById(
|
|
| 392 |
const dagHideInactiveEdgesInput = document.getElementById(
|
| 393 |
'gen_attr_dag_hide_inactive_edges'
|
| 394 |
) as HTMLInputElement | null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
const completeReasonEl = d3.select('#gen_attr_complete_reason');
|
| 396 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
if (modelVariantSelect) modelVariantSelect.value = readStoredModelVariant();
|
| 398 |
if (maxTokensInput) maxTokensInput.value = String(readStoredMaxTokens());
|
| 399 |
const initialDagLayoutMode = readStoredDagLayoutMode();
|
|
@@ -625,6 +699,29 @@ function teacherForcingContinuationForRun(): string | undefined {
|
|
| 625 |
return t.length > 0 ? t : undefined;
|
| 626 |
}
|
| 627 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 628 |
function syncTeacherForcingRow(): void {
|
| 629 |
if (genAttrTeacherForcingBlock) {
|
| 630 |
genAttrTeacherForcingBlock.hidden = !isGenAttrTeacherForcingUiOn();
|
|
@@ -903,6 +1000,8 @@ const dagHandle = initGenAttributeDagView(d3.select('#results'), {
|
|
| 903 |
hideExcludedTokens: initialDagHideExcludedTokens,
|
| 904 |
edgeTopPCoverage: initialDagEdgeTopPCoverage,
|
| 905 |
onFullscreenError: (message) => showToast(message, 'error'),
|
|
|
|
|
|
|
| 906 |
});
|
| 907 |
|
| 908 |
dagLayoutModeSelect?.addEventListener('change', () => {
|
|
@@ -994,6 +1093,152 @@ dagLinearArcIntervalInput?.addEventListener('change', () => {
|
|
| 994 |
dagHandle.setLinearArcAdjacentGapPx(n, { skipRefit: isDagBusy() });
|
| 995 |
});
|
| 996 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 997 |
window.addEventListener('pagehide', (ev) => {
|
| 998 |
if (ev.persisted) return;
|
| 999 |
dagHandle.detach();
|
|
@@ -1007,16 +1252,40 @@ function onExcludePatternsEffectiveChange(): void {
|
|
| 1007 |
dagHandle.clearNodeSelection();
|
| 1008 |
}
|
| 1009 |
|
| 1010 |
-
|
| 1011 |
-
|
| 1012 |
-
|
| 1013 |
-
|
| 1014 |
-
|
| 1015 |
-
|
| 1016 |
-
|
| 1017 |
-
|
| 1018 |
-
|
| 1019 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1020 |
|
| 1021 |
function currentModelVariant(): PredictionAttributeModelVariant {
|
| 1022 |
const v = modelVariantSelect?.value;
|
|
@@ -1306,6 +1575,7 @@ async function applyGenAttrCachedRun(
|
|
| 1306 |
|
| 1307 |
stopDagPlayback();
|
| 1308 |
dagHandle.reset();
|
|
|
|
| 1309 |
runnerHandle = createHydratedTokenGenHandle(rec.steps);
|
| 1310 |
lastRunInitialContext = rec.initialContext;
|
| 1311 |
lastRunInputSnapshot = getInputSnapshotForRun();
|
|
@@ -1475,7 +1745,11 @@ void (async () => {
|
|
| 1475 |
try {
|
| 1476 |
const rec = await fetchBundledGenAttributeDemoBySlug(demoRaw);
|
| 1477 |
if (!isStaleGenAttrCachedApply(applyGen) && rec && isGenAttrRunPayloadValidForUi(rec)) {
|
| 1478 |
-
await applyGenAttrCachedRun(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1479 |
if (!isStaleGenAttrCachedApply(applyGen)) {
|
| 1480 |
applied = true;
|
| 1481 |
}
|
|
@@ -1593,20 +1867,7 @@ async function runGeneration(): Promise<void> {
|
|
| 1593 |
const stopAfterTF = isStopAfterTeacherForcingOn();
|
| 1594 |
const maxTokens = currentMaxTokens();
|
| 1595 |
const tokenizeModel = currentModelVariant();
|
| 1596 |
-
const
|
| 1597 |
-
? { teacherForcing: teacherForcingText, stopAfterTeacherForcing: stopAfterTF }
|
| 1598 |
-
: {};
|
| 1599 |
-
const runDraft: GenAttrRunDraft = isSkipChatTemplate()
|
| 1600 |
-
? { mode: 'raw', model: tokenizeModel, maxTokens, ...tfDraftFields }
|
| 1601 |
-
: {
|
| 1602 |
-
mode: 'chat',
|
| 1603 |
-
model: tokenizeModel,
|
| 1604 |
-
maxTokens,
|
| 1605 |
-
system: systemPromptTextarea?.value ?? '',
|
| 1606 |
-
user: userPromptTextarea?.value ?? '',
|
| 1607 |
-
useSystem: isGenAttrUseSystemPrompt(),
|
| 1608 |
-
...tfDraftFields,
|
| 1609 |
-
};
|
| 1610 |
const prompt = getActivePromptValue();
|
| 1611 |
analyzeProgressEl.text('Assembling prompt…').style('display', null);
|
| 1612 |
initialContext = await resolveInitialContext(signal);
|
|
@@ -1773,18 +2034,29 @@ function syncGenAttrExportDemoBtn(): void {
|
|
| 1773 |
syncGenAttrExportDemoBtn();
|
| 1774 |
adminManager.onAdminModeChange(() => syncGenAttrExportDemoBtn());
|
| 1775 |
exportDemoBtn?.addEventListener('click', () => {
|
| 1776 |
-
|
| 1777 |
-
|
| 1778 |
-
|
| 1779 |
-
|
| 1780 |
-
|
| 1781 |
-
|
| 1782 |
-
|
| 1783 |
-
|
| 1784 |
-
|
| 1785 |
-
|
| 1786 |
-
|
| 1787 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1788 |
});
|
| 1789 |
|
| 1790 |
initChatPanelLayout({ storageKey: PANEL_SPLIT_STORAGE_KEY_GEN_ATTRIBUTE });
|
|
|
|
| 44 |
import { completionFinishReasonLabel, type CompletionFinishReason } from './utils/generationEndReasonLabel';
|
| 45 |
import {
|
| 46 |
buildCachedContentUrlParam,
|
| 47 |
+
buildGenAttrExportedDemoPayload,
|
| 48 |
getCachedEntryByContentKey,
|
| 49 |
listCachedHistoryRows,
|
| 50 |
removeCachedEntryByContentKey,
|
| 51 |
save,
|
| 52 |
touchCachedEntryByContentKey,
|
| 53 |
+
type GenAttrCachedRun,
|
| 54 |
+
type GenAttrDemoUiOptions,
|
| 55 |
type GenAttrCacheKey,
|
| 56 |
+
type GenAttrRunDraft,
|
| 57 |
} from './storage/genAttributeRunCache';
|
| 58 |
+
import {
|
| 59 |
+
DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT,
|
| 60 |
+
DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT,
|
| 61 |
+
} from './attribution/attributionExcludePromptPatternsStorage';
|
| 62 |
import { initCachedHistoryQueryDropdown, type CachedHistorySelectContext } from './utils/cachedHistoryUi';
|
| 63 |
import {
|
| 64 |
DEFAULT_CONTENT_URL_PARAM,
|
|
|
|
| 76 |
} from './demos/genAttributeBundledDemos';
|
| 77 |
import { extractErrorMessage } from './utils/errorUtils';
|
| 78 |
import { exportJsonFile } from './storage/localFileIO';
|
|
|
|
| 79 |
import {
|
| 80 |
GEN_ATTR_RAW_INPUT_HISTORY_KEY,
|
| 81 |
GEN_ATTR_SYSTEM_INPUT_HISTORY_KEY,
|
|
|
|
| 113 |
'info_radar_gen_attr_dag_linear_arc_adjacent_gap';
|
| 114 |
const GEN_ATTR_DAG_COMPACTNESS_STORAGE_KEY = 'info_radar_gen_attr_dag_compactness';
|
| 115 |
const GEN_ATTR_DAG_EDGE_TOP_P_COVERAGE_STORAGE_KEY = 'info_radar_gen_attr_dag_edge_top_p_coverage';
|
| 116 |
+
/** 仅此页:与 Attribution 的 `exclude_tokens` 无关。 */
|
| 117 |
+
const GEN_ATTR_EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY = 'info_radar_gen_attr_exclude_prompt_patterns';
|
| 118 |
+
const GEN_ATTR_EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY =
|
| 119 |
+
'info_radar_gen_attr_exclude_prompt_patterns_enabled';
|
| 120 |
+
const GEN_ATTR_EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY = 'info_radar_gen_attr_exclude_generated_patterns';
|
| 121 |
+
const GEN_ATTR_EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY =
|
| 122 |
+
'info_radar_gen_attr_exclude_generated_patterns_enabled';
|
| 123 |
|
| 124 |
/** 步进回放节奏:`total`=整段剩余回放总时长内均分间隔;`step`=固定每步间隔(ms)。 */
|
| 125 |
type DagReplayPacingMode = 'total' | 'step';
|
|
|
|
| 405 |
const dagHideInactiveEdgesInput = document.getElementById(
|
| 406 |
'gen_attr_dag_hide_inactive_edges'
|
| 407 |
) as HTMLInputElement | null;
|
| 408 |
+
const genAttrExcludePromptPatternsTa = document.getElementById(
|
| 409 |
+
'gen_attr_exclude_prompt_patterns'
|
| 410 |
+
) as HTMLTextAreaElement | null;
|
| 411 |
+
const genAttrExcludePromptPatternsEnable = document.getElementById(
|
| 412 |
+
'gen_attr_exclude_prompt_patterns_enable'
|
| 413 |
+
) as HTMLInputElement | null;
|
| 414 |
+
const genAttrExcludeGeneratedPatternsTa = document.getElementById(
|
| 415 |
+
'gen_attr_exclude_generated_patterns'
|
| 416 |
+
) as HTMLTextAreaElement | null;
|
| 417 |
+
const genAttrExcludeGeneratedPatternsEnable = document.getElementById(
|
| 418 |
+
'gen_attr_exclude_generated_patterns_enable'
|
| 419 |
+
) as HTMLInputElement | null;
|
| 420 |
const completeReasonEl = d3.select('#gen_attr_complete_reason');
|
| 421 |
|
| 422 |
+
function syncGenAttrExcludePatternTextareasDisabled(): void {
|
| 423 |
+
if (genAttrExcludePromptPatternsTa) {
|
| 424 |
+
genAttrExcludePromptPatternsTa.disabled = !genAttrExcludePromptPatternsEnable?.checked;
|
| 425 |
+
}
|
| 426 |
+
if (genAttrExcludeGeneratedPatternsTa) {
|
| 427 |
+
genAttrExcludeGeneratedPatternsTa.disabled = !genAttrExcludeGeneratedPatternsEnable?.checked;
|
| 428 |
+
}
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
function hydrateGenAttrExcludePatternsFromGenAttrStorage(): void {
|
| 432 |
+
try {
|
| 433 |
+
const savedPrompt = localStorage.getItem(GEN_ATTR_EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY);
|
| 434 |
+
if (genAttrExcludePromptPatternsTa) {
|
| 435 |
+
genAttrExcludePromptPatternsTa.value =
|
| 436 |
+
savedPrompt !== null ? savedPrompt : DEFAULT_EXCLUDE_PROMPT_PATTERNS_TEXT;
|
| 437 |
+
}
|
| 438 |
+
const promptEnRaw = localStorage.getItem(GEN_ATTR_EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY);
|
| 439 |
+
if (genAttrExcludePromptPatternsEnable) {
|
| 440 |
+
genAttrExcludePromptPatternsEnable.checked = promptEnRaw === null ? true : promptEnRaw === '1';
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
const savedGen = localStorage.getItem(GEN_ATTR_EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY);
|
| 444 |
+
if (genAttrExcludeGeneratedPatternsTa) {
|
| 445 |
+
genAttrExcludeGeneratedPatternsTa.value =
|
| 446 |
+
savedGen !== null ? savedGen : DEFAULT_EXCLUDE_GENERATED_PATTERNS_TEXT;
|
| 447 |
+
}
|
| 448 |
+
const genEnRaw = localStorage.getItem(GEN_ATTR_EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY);
|
| 449 |
+
if (genAttrExcludeGeneratedPatternsEnable) {
|
| 450 |
+
genAttrExcludeGeneratedPatternsEnable.checked = genEnRaw === null ? true : genEnRaw === '1';
|
| 451 |
+
}
|
| 452 |
+
} catch {
|
| 453 |
+
/* ignore */
|
| 454 |
+
}
|
| 455 |
+
syncGenAttrExcludePatternTextareasDisabled();
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
hydrateGenAttrExcludePatternsFromGenAttrStorage();
|
| 459 |
+
|
| 460 |
+
/** 与 DAG 同源:DAG 预处理按当前控件即时读取,不读 Attribution 的 localStorage。 */
|
| 461 |
+
function genAttrEffectiveExcludePromptPatternsText(): string {
|
| 462 |
+
if (!genAttrExcludePromptPatternsEnable?.checked) return '';
|
| 463 |
+
return genAttrExcludePromptPatternsTa?.value ?? '';
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
function genAttrEffectiveExcludeGeneratedPatternsText(): string {
|
| 467 |
+
if (!genAttrExcludeGeneratedPatternsEnable?.checked) return '';
|
| 468 |
+
return genAttrExcludeGeneratedPatternsTa?.value ?? '';
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
if (modelVariantSelect) modelVariantSelect.value = readStoredModelVariant();
|
| 472 |
if (maxTokensInput) maxTokensInput.value = String(readStoredMaxTokens());
|
| 473 |
const initialDagLayoutMode = readStoredDagLayoutMode();
|
|
|
|
| 699 |
return t.length > 0 ? t : undefined;
|
| 700 |
}
|
| 701 |
|
| 702 |
+
/** 与 IndexedDB `save` 使用同一快照逻辑(须在 `autoMoveFirstTeacherForcingTokenToPromptIfNeeded` 之后调用)。 */
|
| 703 |
+
function buildGenAttrRunDraftForCache(): GenAttrRunDraft {
|
| 704 |
+
const teacherForcingText = teacherForcingContinuationForRun();
|
| 705 |
+
const stopAfterTF = isStopAfterTeacherForcingOn();
|
| 706 |
+
const maxTokens = currentMaxTokens();
|
| 707 |
+
const tokenizeModel = currentModelVariant();
|
| 708 |
+
const tfDraftFields =
|
| 709 |
+
teacherForcingText !== undefined
|
| 710 |
+
? { teacherForcing: teacherForcingText, stopAfterTeacherForcing: stopAfterTF }
|
| 711 |
+
: {};
|
| 712 |
+
return isSkipChatTemplate()
|
| 713 |
+
? { mode: 'raw', model: tokenizeModel, maxTokens, ...tfDraftFields }
|
| 714 |
+
: {
|
| 715 |
+
mode: 'chat',
|
| 716 |
+
model: tokenizeModel,
|
| 717 |
+
maxTokens,
|
| 718 |
+
system: systemPromptTextarea?.value ?? '',
|
| 719 |
+
user: userPromptTextarea?.value ?? '',
|
| 720 |
+
useSystem: isGenAttrUseSystemPrompt(),
|
| 721 |
+
...tfDraftFields,
|
| 722 |
+
};
|
| 723 |
+
}
|
| 724 |
+
|
| 725 |
function syncTeacherForcingRow(): void {
|
| 726 |
if (genAttrTeacherForcingBlock) {
|
| 727 |
genAttrTeacherForcingBlock.hidden = !isGenAttrTeacherForcingUiOn();
|
|
|
|
| 1000 |
hideExcludedTokens: initialDagHideExcludedTokens,
|
| 1001 |
edgeTopPCoverage: initialDagEdgeTopPCoverage,
|
| 1002 |
onFullscreenError: (message) => showToast(message, 'error'),
|
| 1003 |
+
getEffectiveExcludePromptPatternsText: genAttrEffectiveExcludePromptPatternsText,
|
| 1004 |
+
getEffectiveExcludeGeneratedPatternsText: genAttrEffectiveExcludeGeneratedPatternsText,
|
| 1005 |
});
|
| 1006 |
|
| 1007 |
dagLayoutModeSelect?.addEventListener('change', () => {
|
|
|
|
| 1093 |
dagHandle.setLinearArcAdjacentGapPx(n, { skipRefit: isDagBusy() });
|
| 1094 |
});
|
| 1095 |
|
| 1096 |
+
/** 读取当前演示用 UI(DAG 与排除正则等),供 Export demo 写入 `demoUiOptions`。 */
|
| 1097 |
+
function readGenAttrDemoUiOptionsFromControls(): GenAttrDemoUiOptions {
|
| 1098 |
+
const rawW = parseInt(dagMeasureWidthInput?.value ?? '', 10);
|
| 1099 |
+
const measureWidthPx = Number.isFinite(rawW)
|
| 1100 |
+
? clampDagMeasureWidth(rawW)
|
| 1101 |
+
: GEN_ATTR_DAG_MEASURE_WIDTH_DEFAULT;
|
| 1102 |
+
const rawC = parseFloat(dagCompactnessInput?.value ?? '');
|
| 1103 |
+
const dagCompactness = Number.isFinite(rawC)
|
| 1104 |
+
? clampDagCompactness(rawC)
|
| 1105 |
+
: DAG_COMPACTNESS_DEFAULT;
|
| 1106 |
+
const rawGap = parseInt(dagLinearArcIntervalInput?.value ?? '', 10);
|
| 1107 |
+
const linearArcAdjacentGapPx = Number.isFinite(rawGap)
|
| 1108 |
+
? clampLinearArcAdjacentGap(rawGap)
|
| 1109 |
+
: LINEAR_ARC_ADJACENT_GAP_DEFAULT;
|
| 1110 |
+
const rawTop = parseFloat(dagEdgeTopPCoverageInput?.value ?? '');
|
| 1111 |
+
const edgeTopPCoverage = Number.isFinite(rawTop)
|
| 1112 |
+
? clampDagEdgeTopPCoverage(rawTop)
|
| 1113 |
+
: DAG_EDGE_TOP_P_COVERAGE_DEFAULT;
|
| 1114 |
+
const rawTotal = parseInt(dagPlaybackTotalSInput?.value ?? '', 10);
|
| 1115 |
+
const playbackTotalS = Number.isFinite(rawTotal)
|
| 1116 |
+
? clampDagPlaybackTotalS(rawTotal)
|
| 1117 |
+
: GEN_ATTR_DAG_PLAYBACK_TOTAL_S_DEFAULT;
|
| 1118 |
+
const rawStep = parseInt(dagPlaybackStepMsInput?.value ?? '', 10);
|
| 1119 |
+
const playbackStepMs = Number.isFinite(rawStep)
|
| 1120 |
+
? clampDagPlaybackStepMs(rawStep)
|
| 1121 |
+
: GEN_ATTR_DAG_PLAYBACK_STEP_MS_DEFAULT;
|
| 1122 |
+
return {
|
| 1123 |
+
layoutMode: currentDagLayoutMode(),
|
| 1124 |
+
measureWidthPx,
|
| 1125 |
+
dagCompactness,
|
| 1126 |
+
linearArcAdjacentGapPx,
|
| 1127 |
+
hideExcludedTokens: dagHideExcludedTokensInput?.checked ?? false,
|
| 1128 |
+
edgeTopPCoverage,
|
| 1129 |
+
nodeCiVisualScaleEnabled: dagNodeCiVisualScaleInput?.checked ?? true,
|
| 1130 |
+
edgeWeakenHighSurprisalEnabled: dagEdgeWeakenHighSurprisalInput?.checked ?? true,
|
| 1131 |
+
hideInactiveEdges: dagHideInactiveEdgesInput?.checked ?? false,
|
| 1132 |
+
replayPacingMode: currentDagReplayPacingMode(),
|
| 1133 |
+
playbackTotalS,
|
| 1134 |
+
playbackStepMs,
|
| 1135 |
+
excludePromptPatternsEnabled: genAttrExcludePromptPatternsEnable?.checked ?? true,
|
| 1136 |
+
excludePromptPatternsText: genAttrExcludePromptPatternsTa?.value ?? '',
|
| 1137 |
+
excludeGeneratedPatternsEnabled: genAttrExcludeGeneratedPatternsEnable?.checked ?? true,
|
| 1138 |
+
excludeGeneratedPatternsText: genAttrExcludeGeneratedPatternsTa?.value ?? '',
|
| 1139 |
+
};
|
| 1140 |
+
}
|
| 1141 |
+
|
| 1142 |
+
/**
|
| 1143 |
+
* 从 `demoUiOptions` 还原排除控件(仅 DOM);与施加 DAG demo 不回写 GEN_ATTR_LS 的策略一致,`replay` 读当前控件生效。
|
| 1144 |
+
*/
|
| 1145 |
+
function applyGenAttrExcludePatternsFromDemoUiSnap(snap: Partial<GenAttrDemoUiOptions>): void {
|
| 1146 |
+
const {
|
| 1147 |
+
excludePromptPatternsEnabled,
|
| 1148 |
+
excludePromptPatternsText,
|
| 1149 |
+
excludeGeneratedPatternsEnabled,
|
| 1150 |
+
excludeGeneratedPatternsText,
|
| 1151 |
+
} = snap;
|
| 1152 |
+
if (
|
| 1153 |
+
excludePromptPatternsEnabled === undefined &&
|
| 1154 |
+
excludePromptPatternsText === undefined &&
|
| 1155 |
+
excludeGeneratedPatternsEnabled === undefined &&
|
| 1156 |
+
excludeGeneratedPatternsText === undefined
|
| 1157 |
+
) {
|
| 1158 |
+
return;
|
| 1159 |
+
}
|
| 1160 |
+
if (excludePromptPatternsEnabled !== undefined && genAttrExcludePromptPatternsEnable) {
|
| 1161 |
+
genAttrExcludePromptPatternsEnable.checked = excludePromptPatternsEnabled;
|
| 1162 |
+
}
|
| 1163 |
+
if (excludePromptPatternsText !== undefined && genAttrExcludePromptPatternsTa) {
|
| 1164 |
+
genAttrExcludePromptPatternsTa.value = excludePromptPatternsText;
|
| 1165 |
+
}
|
| 1166 |
+
if (excludeGeneratedPatternsEnabled !== undefined && genAttrExcludeGeneratedPatternsEnable) {
|
| 1167 |
+
genAttrExcludeGeneratedPatternsEnable.checked = excludeGeneratedPatternsEnabled;
|
| 1168 |
+
}
|
| 1169 |
+
if (excludeGeneratedPatternsText !== undefined && genAttrExcludeGeneratedPatternsTa) {
|
| 1170 |
+
genAttrExcludeGeneratedPatternsTa.value = excludeGeneratedPatternsText;
|
| 1171 |
+
}
|
| 1172 |
+
syncGenAttrExcludePatternTextareasDisabled();
|
| 1173 |
+
}
|
| 1174 |
+
|
| 1175 |
+
/**
|
| 1176 |
+
* 按记录中的 `demoUiOptions` 还原 DAG 面板与排除控件(后者仅 DOM)。
|
| 1177 |
+
*/
|
| 1178 |
+
function applyGenAttrDemoUiOptionsFromRecord(rec: GenAttrCachedRun): void {
|
| 1179 |
+
const snap = rec.demoUiOptions;
|
| 1180 |
+
if (!snap) return;
|
| 1181 |
+
const mode = snap.layoutMode;
|
| 1182 |
+
if (mode) {
|
| 1183 |
+
if (dagLayoutModeSelect) {
|
| 1184 |
+
dagLayoutModeSelect.value = mode;
|
| 1185 |
+
}
|
| 1186 |
+
applyDagLayoutModeUi();
|
| 1187 |
+
dagHandle.setLayoutMode(mode);
|
| 1188 |
+
}
|
| 1189 |
+
|
| 1190 |
+
if (snap.measureWidthPx !== undefined) {
|
| 1191 |
+
const w = clampDagMeasureWidth(snap.measureWidthPx);
|
| 1192 |
+
if (dagMeasureWidthInput) dagMeasureWidthInput.value = String(w);
|
| 1193 |
+
dagHandle.setMeasureWidthPx(w);
|
| 1194 |
+
}
|
| 1195 |
+
if (snap.dagCompactness !== undefined) {
|
| 1196 |
+
const c = clampDagCompactness(snap.dagCompactness);
|
| 1197 |
+
if (dagCompactnessInput) dagCompactnessInput.value = String(c);
|
| 1198 |
+
dagHandle.setDagCompactness(c);
|
| 1199 |
+
}
|
| 1200 |
+
if (snap.linearArcAdjacentGapPx !== undefined) {
|
| 1201 |
+
const n = clampLinearArcAdjacentGap(snap.linearArcAdjacentGapPx);
|
| 1202 |
+
if (dagLinearArcIntervalInput) dagLinearArcIntervalInput.value = String(n);
|
| 1203 |
+
dagHandle.setLinearArcAdjacentGapPx(n);
|
| 1204 |
+
}
|
| 1205 |
+
if (snap.edgeTopPCoverage !== undefined) {
|
| 1206 |
+
const c = clampDagEdgeTopPCoverage(snap.edgeTopPCoverage);
|
| 1207 |
+
if (dagEdgeTopPCoverageInput) dagEdgeTopPCoverageInput.value = String(c);
|
| 1208 |
+
dagHandle.setEdgeTopPCoverage(c);
|
| 1209 |
+
}
|
| 1210 |
+
if (snap.hideExcludedTokens !== undefined) {
|
| 1211 |
+
if (dagHideExcludedTokensInput) dagHideExcludedTokensInput.checked = snap.hideExcludedTokens;
|
| 1212 |
+
dagHandle.setHideExcludedTokens(snap.hideExcludedTokens);
|
| 1213 |
+
}
|
| 1214 |
+
if (snap.nodeCiVisualScaleEnabled !== undefined) {
|
| 1215 |
+
if (dagNodeCiVisualScaleInput) dagNodeCiVisualScaleInput.checked = snap.nodeCiVisualScaleEnabled;
|
| 1216 |
+
setDagNodeCiVisualScaleEnabled(snap.nodeCiVisualScaleEnabled);
|
| 1217 |
+
}
|
| 1218 |
+
if (snap.edgeWeakenHighSurprisalEnabled !== undefined) {
|
| 1219 |
+
if (dagEdgeWeakenHighSurprisalInput) dagEdgeWeakenHighSurprisalInput.checked = snap.edgeWeakenHighSurprisalEnabled;
|
| 1220 |
+
setDagEdgeWeakenHighSurprisalEnabled(snap.edgeWeakenHighSurprisalEnabled);
|
| 1221 |
+
}
|
| 1222 |
+
if (snap.hideInactiveEdges !== undefined) {
|
| 1223 |
+
if (dagHideInactiveEdgesInput) dagHideInactiveEdgesInput.checked = snap.hideInactiveEdges;
|
| 1224 |
+
applyDagHideInactiveEdges(snap.hideInactiveEdges);
|
| 1225 |
+
}
|
| 1226 |
+
if (snap.replayPacingMode !== undefined) {
|
| 1227 |
+
if (dagReplayModeSelect) dagReplayModeSelect.value = snap.replayPacingMode;
|
| 1228 |
+
applyDagReplaySpeedUi();
|
| 1229 |
+
}
|
| 1230 |
+
if (snap.playbackTotalS !== undefined) {
|
| 1231 |
+
const s = clampDagPlaybackTotalS(snap.playbackTotalS);
|
| 1232 |
+
if (dagPlaybackTotalSInput) dagPlaybackTotalSInput.value = String(s);
|
| 1233 |
+
}
|
| 1234 |
+
if (snap.playbackStepMs !== undefined) {
|
| 1235 |
+
const ms = clampDagPlaybackStepMs(snap.playbackStepMs);
|
| 1236 |
+
if (dagPlaybackStepMsInput) dagPlaybackStepMsInput.value = String(ms);
|
| 1237 |
+
}
|
| 1238 |
+
|
| 1239 |
+
applyGenAttrExcludePatternsFromDemoUiSnap(snap);
|
| 1240 |
+
}
|
| 1241 |
+
|
| 1242 |
window.addEventListener('pagehide', (ev) => {
|
| 1243 |
if (ev.persisted) return;
|
| 1244 |
dagHandle.detach();
|
|
|
|
| 1252 |
dagHandle.clearNodeSelection();
|
| 1253 |
}
|
| 1254 |
|
| 1255 |
+
function bindExcludePatternControls(
|
| 1256 |
+
enableEl: HTMLInputElement | null,
|
| 1257 |
+
textEl: HTMLTextAreaElement | null,
|
| 1258 |
+
textKey: string,
|
| 1259 |
+
enabledKey: string,
|
| 1260 |
+
): void {
|
| 1261 |
+
enableEl?.addEventListener('change', () => {
|
| 1262 |
+
try {
|
| 1263 |
+
if (textEl) localStorage.setItem(textKey, textEl.value);
|
| 1264 |
+
localStorage.setItem(enabledKey, enableEl.checked ? '1' : '0');
|
| 1265 |
+
} catch { /* ignore */ }
|
| 1266 |
+
syncGenAttrExcludePatternTextareasDisabled();
|
| 1267 |
+
onExcludePatternsEffectiveChange();
|
| 1268 |
+
});
|
| 1269 |
+
textEl?.addEventListener('blur', () => {
|
| 1270 |
+
try {
|
| 1271 |
+
localStorage.setItem(textKey, textEl.value);
|
| 1272 |
+
} catch { /* ignore */ }
|
| 1273 |
+
onExcludePatternsEffectiveChange();
|
| 1274 |
+
});
|
| 1275 |
+
}
|
| 1276 |
+
|
| 1277 |
+
bindExcludePatternControls(
|
| 1278 |
+
genAttrExcludePromptPatternsEnable,
|
| 1279 |
+
genAttrExcludePromptPatternsTa,
|
| 1280 |
+
GEN_ATTR_EXCLUDE_PROMPT_PATTERNS_STORAGE_KEY,
|
| 1281 |
+
GEN_ATTR_EXCLUDE_PROMPT_PATTERNS_ENABLED_STORAGE_KEY,
|
| 1282 |
+
);
|
| 1283 |
+
bindExcludePatternControls(
|
| 1284 |
+
genAttrExcludeGeneratedPatternsEnable,
|
| 1285 |
+
genAttrExcludeGeneratedPatternsTa,
|
| 1286 |
+
GEN_ATTR_EXCLUDE_GENERATED_PATTERNS_STORAGE_KEY,
|
| 1287 |
+
GEN_ATTR_EXCLUDE_GENERATED_PATTERNS_ENABLED_STORAGE_KEY,
|
| 1288 |
+
);
|
| 1289 |
|
| 1290 |
function currentModelVariant(): PredictionAttributeModelVariant {
|
| 1291 |
const v = modelVariantSelect?.value;
|
|
|
|
| 1575 |
|
| 1576 |
stopDagPlayback();
|
| 1577 |
dagHandle.reset();
|
| 1578 |
+
applyGenAttrDemoUiOptionsFromRecord(rec);
|
| 1579 |
runnerHandle = createHydratedTokenGenHandle(rec.steps);
|
| 1580 |
lastRunInitialContext = rec.initialContext;
|
| 1581 |
lastRunInputSnapshot = getInputSnapshotForRun();
|
|
|
|
| 1745 |
try {
|
| 1746 |
const rec = await fetchBundledGenAttributeDemoBySlug(demoRaw);
|
| 1747 |
if (!isStaleGenAttrCachedApply(applyGen) && rec && isGenAttrRunPayloadValidForUi(rec)) {
|
| 1748 |
+
await applyGenAttrCachedRun(
|
| 1749 |
+
rec,
|
| 1750 |
+
{ afterUrl: { kind: 'demo', slug: demoRaw } },
|
| 1751 |
+
applyGen
|
| 1752 |
+
);
|
| 1753 |
if (!isStaleGenAttrCachedApply(applyGen)) {
|
| 1754 |
applied = true;
|
| 1755 |
}
|
|
|
|
| 1867 |
const stopAfterTF = isStopAfterTeacherForcingOn();
|
| 1868 |
const maxTokens = currentMaxTokens();
|
| 1869 |
const tokenizeModel = currentModelVariant();
|
| 1870 |
+
const runDraft = buildGenAttrRunDraftForCache();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1871 |
const prompt = getActivePromptValue();
|
| 1872 |
analyzeProgressEl.text('Assembling prompt…').style('display', null);
|
| 1873 |
initialContext = await resolveInitialContext(signal);
|
|
|
|
| 2034 |
syncGenAttrExportDemoBtn();
|
| 2035 |
adminManager.onAdminModeChange(() => syncGenAttrExportDemoBtn());
|
| 2036 |
exportDemoBtn?.addEventListener('click', () => {
|
| 2037 |
+
void (async () => {
|
| 2038 |
+
const h = runnerHandle;
|
| 2039 |
+
const ic = lastRunInitialContext;
|
| 2040 |
+
if (!h || !ic || h.tokenCount < 1) {
|
| 2041 |
+
showToast(tr('No run to export'), 'error');
|
| 2042 |
+
return;
|
| 2043 |
+
}
|
| 2044 |
+
await autoMoveFirstTeacherForcingTokenToPromptIfNeeded();
|
| 2045 |
+
try {
|
| 2046 |
+
const payload = buildGenAttrExportedDemoPayload({
|
| 2047 |
+
initialContext: ic,
|
| 2048 |
+
steps: h.getAllSteps(),
|
| 2049 |
+
promptSpans: currentRunPromptSpans,
|
| 2050 |
+
completionReason: lastRunCompletionReason ?? undefined,
|
| 2051 |
+
draft: buildGenAttrRunDraftForCache(),
|
| 2052 |
+
demoUiOptions: readGenAttrDemoUiOptionsFromControls(),
|
| 2053 |
+
});
|
| 2054 |
+
void exportJsonFile(payload, `genattr-${Date.now()}.json`);
|
| 2055 |
+
} catch (e: unknown) {
|
| 2056 |
+
const msg = e instanceof Error ? e.message : String(e);
|
| 2057 |
+
showToast(msg, 'error');
|
| 2058 |
+
}
|
| 2059 |
+
})();
|
| 2060 |
});
|
| 2061 |
|
| 2062 |
initChatPanelLayout({ storageKey: PANEL_SPLIT_STORAGE_KEY_GEN_ATTRIBUTE });
|
client/src/ts/lang/translations.ts
CHANGED
|
@@ -87,6 +87,10 @@ export const translations: Translations = {
|
|
| 87 |
'Analyze&Upload': '分析并上传',
|
| 88 |
'Analyze URL': '分析 URL',
|
| 89 |
'Analyze URL content': '分析 URL 内容',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
'Cancel': '取消',
|
| 91 |
'Clear': '清空',
|
| 92 |
'Compare results': '对比结果',
|
|
|
|
| 87 |
'Analyze&Upload': '分析并上传',
|
| 88 |
'Analyze URL': '分析 URL',
|
| 89 |
'Analyze URL content': '分析 URL 内容',
|
| 90 |
+
'Please enter a URL.': '请输入 URL。',
|
| 91 |
+
'Invalid input': '输入无效',
|
| 92 |
+
'Invalid URL': 'URL 无效',
|
| 93 |
+
'This does not look like a valid URL. Check for typos.': '这不像有效的网址,请检查是否输错。',
|
| 94 |
'Cancel': '取消',
|
| 95 |
'Clear': '清空',
|
| 96 |
'Compare results': '对比结果',
|
client/src/ts/storage/genAttributeRunCache.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
|
|
| 1 |
import type { TokenGenStep } from '../attribution/tokenGenAttributionRunner';
|
| 2 |
import type { PromptTokenSpan } from '../attribution/genAttributeDagPreprocess';
|
| 3 |
import {
|
| 4 |
canonicalizeCompletionFinishReason,
|
| 5 |
isCompletionFinishReason,
|
|
|
|
| 6 |
type CompletionFinishReason,
|
| 7 |
} from '../utils/generationEndReasonLabel';
|
| 8 |
import {
|
|
@@ -37,7 +39,12 @@ export type GenAttrRunDraft = {
|
|
| 37 |
stopAfterTeacherForcing?: boolean;
|
| 38 |
};
|
| 39 |
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
initialContext: string;
|
| 42 |
steps: TokenGenStep[];
|
| 43 |
/** 完整 prompt token spans(offset + raw),与 /api/tokenize 同源;旧缓存无此字段时由调用方从 step 0 归因降级。 */
|
|
@@ -49,7 +56,37 @@ export type GenAttrCachedRun = {
|
|
| 49 |
};
|
| 50 |
|
| 51 |
/**
|
| 52 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
* 原则:draft 中存储的可变参数均纳入 key,同参数不同结果不应互相覆盖。
|
| 54 |
*/
|
| 55 |
export type GenAttrCacheKey = {
|
|
@@ -77,15 +114,15 @@ function keyHash(key: GenAttrCacheKey): string {
|
|
| 77 |
return buildContentKeyFromBusinessKey(normalizeKey(key));
|
| 78 |
}
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
completionReason?: CompletionFinishReason
|
| 86 |
-
draft?: GenAttrRunDraft
|
| 87 |
-
):
|
| 88 |
-
const { initialContext } =
|
| 89 |
let reasonToStore: CompletionFinishReason | undefined;
|
| 90 |
if (completionReason !== undefined) {
|
| 91 |
const c = canonicalizeCompletionFinishReason(completionReason);
|
|
@@ -94,13 +131,192 @@ export async function save(
|
|
| 94 |
}
|
| 95 |
reasonToStore = c;
|
| 96 |
}
|
| 97 |
-
|
| 98 |
initialContext,
|
| 99 |
steps,
|
| 100 |
...(promptSpans.length > 0 ? { promptSpans } : {}),
|
| 101 |
...(reasonToStore !== undefined ? { completionReason: reasonToStore } : {}),
|
| 102 |
...(draft !== undefined ? { draft } : {}),
|
| 103 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
await upsertEntry({
|
| 105 |
namespace: NAMESPACE,
|
| 106 |
businessKeyJson: JSON.stringify(normalizeKey(key)),
|
|
@@ -113,13 +329,15 @@ export async function save(
|
|
| 113 |
|
| 114 |
export async function get(key: GenAttrCacheKey): Promise<GenAttrCachedRun | undefined> {
|
| 115 |
const row = await getByContentKey<GenAttrCachedRun>(NAMESPACE, keyHash(key));
|
| 116 |
-
|
|
|
|
| 117 |
}
|
| 118 |
|
| 119 |
export async function getCachedEntryByContentKey(raw: string): Promise<GenAttrCachedRun | undefined> {
|
| 120 |
if (!raw) return undefined;
|
| 121 |
const row = await getByContentKey<GenAttrCachedRun>(NAMESPACE, raw);
|
| 122 |
-
|
|
|
|
| 123 |
}
|
| 124 |
|
| 125 |
export function buildCachedContentUrlParam(key: GenAttrCacheKey): string {
|
|
|
|
| 1 |
+
import type { DagLayoutMode } from '../attribution/genAttributeDagView';
|
| 2 |
import type { TokenGenStep } from '../attribution/tokenGenAttributionRunner';
|
| 3 |
import type { PromptTokenSpan } from '../attribution/genAttributeDagPreprocess';
|
| 4 |
import {
|
| 5 |
canonicalizeCompletionFinishReason,
|
| 6 |
isCompletionFinishReason,
|
| 7 |
+
isKnownPersistedCompletionReason,
|
| 8 |
type CompletionFinishReason,
|
| 9 |
} from '../utils/generationEndReasonLabel';
|
| 10 |
import {
|
|
|
|
| 39 |
stopAfterTeacherForcing?: boolean;
|
| 40 |
};
|
| 41 |
|
| 42 |
+
/**
|
| 43 |
+
* Payload 中与 **正文 / demo UI 选项** 中的 UI 无关的内容(一次 run 的 JSON 主体):
|
| 44 |
+
* - **Key 语义**(去重哈希)单独由 {@link GenAttrCacheKey} 表示并排他参与 `contentKey`;
|
| 45 |
+
* - UI 控件快照见可选字段 {@link GenAttrCachedRun.demoUiOptions}(仅导出 demo)。
|
| 46 |
+
*/
|
| 47 |
+
export type GenAttrCachedRunContentFields = {
|
| 48 |
initialContext: string;
|
| 49 |
steps: TokenGenStep[];
|
| 50 |
/** 完整 prompt token spans(offset + raw),与 /api/tokenize 同源;旧缓存无此字段时由调用方从 step 0 归因降级。 */
|
|
|
|
| 56 |
};
|
| 57 |
|
| 58 |
/**
|
| 59 |
+
* Gen Attribute 页 **演示用 UI** 快照(DAG 几何与勾选、回放节奏、归因排除正则等;与正文 key 无关)。
|
| 60 |
+
* **Export demo** 写入完整对象;加载时可为 {@link Partial},按存在字段逐项应用。
|
| 61 |
+
*/
|
| 62 |
+
export type GenAttrDemoUiOptions = {
|
| 63 |
+
layoutMode: DagLayoutMode;
|
| 64 |
+
measureWidthPx: number;
|
| 65 |
+
dagCompactness: number;
|
| 66 |
+
linearArcAdjacentGapPx: number;
|
| 67 |
+
hideExcludedTokens: boolean;
|
| 68 |
+
edgeTopPCoverage: number;
|
| 69 |
+
nodeCiVisualScaleEnabled: boolean;
|
| 70 |
+
edgeWeakenHighSurprisalEnabled: boolean;
|
| 71 |
+
hideInactiveEdges: boolean;
|
| 72 |
+
replayPacingMode: 'total' | 'step';
|
| 73 |
+
playbackTotalS: number;
|
| 74 |
+
playbackStepMs: number;
|
| 75 |
+
/** 排除 prompt token 归因:使能与正则文本(仅 Gen Attribute,`info_radar_gen_attr_exclude_prompt_*`)。 */
|
| 76 |
+
excludePromptPatternsEnabled: boolean;
|
| 77 |
+
excludePromptPatternsText: string;
|
| 78 |
+
/** 排除生成 token 归因:使能与正则文本(`info_radar_gen_attr_exclude_generated_*`)。 */
|
| 79 |
+
excludeGeneratedPatternsEnabled: boolean;
|
| 80 |
+
excludeGeneratedPatternsText: string;
|
| 81 |
+
};
|
| 82 |
+
|
| 83 |
+
/** 单条记录 JSON:内容字段 + 可选 `demoUiOptions`(仅导出 demo 写入)。 */
|
| 84 |
+
export type GenAttrCachedRun = GenAttrCachedRunContentFields & {
|
| 85 |
+
demoUiOptions?: Partial<GenAttrDemoUiOptions>;
|
| 86 |
+
};
|
| 87 |
+
|
| 88 |
+
/**
|
| 89 |
+
* 缓存业务 **key 字段**:涵盖所有影响 `steps` 内容的生成参数(决定 `contentKey`)。
|
| 90 |
* 原则:draft 中存储的可变参数均纳入 key,同参数不同结果不应互相覆盖。
|
| 91 |
*/
|
| 92 |
export type GenAttrCacheKey = {
|
|
|
|
| 114 |
return buildContentKeyFromBusinessKey(normalizeKey(key));
|
| 115 |
}
|
| 116 |
|
| 117 |
+
/** 构造「内容字段」:供 IndexedDB `save` 与导出 demo 的共有主体(不含 demo UI)。 */
|
| 118 |
+
export function buildGenAttrCachedRunContentPayload(params: {
|
| 119 |
+
initialContext: string;
|
| 120 |
+
steps: TokenGenStep[];
|
| 121 |
+
promptSpans: PromptTokenSpan[];
|
| 122 |
+
completionReason?: CompletionFinishReason;
|
| 123 |
+
draft?: GenAttrRunDraft;
|
| 124 |
+
}): GenAttrCachedRunContentFields {
|
| 125 |
+
const { initialContext, steps, promptSpans, completionReason, draft } = params;
|
| 126 |
let reasonToStore: CompletionFinishReason | undefined;
|
| 127 |
if (completionReason !== undefined) {
|
| 128 |
const c = canonicalizeCompletionFinishReason(completionReason);
|
|
|
|
| 131 |
}
|
| 132 |
reasonToStore = c;
|
| 133 |
}
|
| 134 |
+
return {
|
| 135 |
initialContext,
|
| 136 |
steps,
|
| 137 |
...(promptSpans.length > 0 ? { promptSpans } : {}),
|
| 138 |
...(reasonToStore !== undefined ? { completionReason: reasonToStore } : {}),
|
| 139 |
...(draft !== undefined ? { draft } : {}),
|
| 140 |
};
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
/**
|
| 144 |
+
* 仅 **Export demo** 使用:在内容字段上附加 **demoUiOptions** 全量(history 不得调用)。
|
| 145 |
+
*/
|
| 146 |
+
export function buildGenAttrExportedDemoPayload(
|
| 147 |
+
params: {
|
| 148 |
+
initialContext: string;
|
| 149 |
+
steps: TokenGenStep[];
|
| 150 |
+
promptSpans: PromptTokenSpan[];
|
| 151 |
+
completionReason?: CompletionFinishReason;
|
| 152 |
+
draft?: GenAttrRunDraft;
|
| 153 |
+
demoUiOptions: GenAttrDemoUiOptions;
|
| 154 |
+
}
|
| 155 |
+
): GenAttrCachedRun {
|
| 156 |
+
const { demoUiOptions, ...contentParams } = params;
|
| 157 |
+
return { ...buildGenAttrCachedRunContentPayload(contentParams), demoUiOptions };
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function isValidPromptSpansPayload(v: unknown): boolean {
|
| 161 |
+
if (!Array.isArray(v)) return false;
|
| 162 |
+
for (const item of v) {
|
| 163 |
+
if (item == null || typeof item !== 'object') return false;
|
| 164 |
+
const o = item as Record<string, unknown>;
|
| 165 |
+
const off = o.offset;
|
| 166 |
+
if (!Array.isArray(off) || off.length !== 2) return false;
|
| 167 |
+
if (typeof off[0] !== 'number' || !Number.isFinite(off[0])) return false;
|
| 168 |
+
if (typeof off[1] !== 'number' || !Number.isFinite(off[1])) return false;
|
| 169 |
+
if (typeof o.raw !== 'string') return false;
|
| 170 |
+
if (o.token_id !== undefined && (typeof o.token_id !== 'number' || !Number.isFinite(o.token_id))) {
|
| 171 |
+
return false;
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
return true;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
function isValidGenAttrRunDraftPayload(v: unknown): boolean {
|
| 178 |
+
if (v == null || typeof v !== 'object') return false;
|
| 179 |
+
const d = v as Record<string, unknown>;
|
| 180 |
+
if (d.mode !== 'raw' && d.mode !== 'chat') return false;
|
| 181 |
+
if (d.model !== undefined && typeof d.model !== 'string') return false;
|
| 182 |
+
if (d.maxTokens !== undefined && (typeof d.maxTokens !== 'number' || !Number.isFinite(d.maxTokens))) {
|
| 183 |
+
return false;
|
| 184 |
+
}
|
| 185 |
+
if (d.system !== undefined && typeof d.system !== 'string') return false;
|
| 186 |
+
if (d.user !== undefined && typeof d.user !== 'string') return false;
|
| 187 |
+
if (d.useSystem !== undefined && typeof d.useSystem !== 'boolean') return false;
|
| 188 |
+
if (d.teacherForcing !== undefined && typeof d.teacherForcing !== 'string') return false;
|
| 189 |
+
if (d.stopAfterTeacherForcing !== undefined && typeof d.stopAfterTeacherForcing !== 'boolean') {
|
| 190 |
+
return false;
|
| 191 |
+
}
|
| 192 |
+
return true;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
function isDagLayoutModePayload(v: unknown): v is DagLayoutMode {
|
| 196 |
+
return v === 'text-flow' || v === 'linear-arc' || v === 'spiral';
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
function isValidDemoUiOptionsPayload(v: unknown): v is Partial<GenAttrDemoUiOptions> {
|
| 200 |
+
if (v == null || typeof v !== 'object') return false;
|
| 201 |
+
const d = v as Record<string, unknown>;
|
| 202 |
+
if (d.layoutMode !== undefined && !isDagLayoutModePayload(d.layoutMode)) return false;
|
| 203 |
+
if (d.measureWidthPx !== undefined && (typeof d.measureWidthPx !== 'number' || !Number.isFinite(d.measureWidthPx))) {
|
| 204 |
+
return false;
|
| 205 |
+
}
|
| 206 |
+
if (d.dagCompactness !== undefined && (typeof d.dagCompactness !== 'number' || !Number.isFinite(d.dagCompactness))) {
|
| 207 |
+
return false;
|
| 208 |
+
}
|
| 209 |
+
if (
|
| 210 |
+
d.linearArcAdjacentGapPx !== undefined &&
|
| 211 |
+
(typeof d.linearArcAdjacentGapPx !== 'number' || !Number.isFinite(d.linearArcAdjacentGapPx))
|
| 212 |
+
) {
|
| 213 |
+
return false;
|
| 214 |
+
}
|
| 215 |
+
if (d.hideExcludedTokens !== undefined && typeof d.hideExcludedTokens !== 'boolean') return false;
|
| 216 |
+
if (
|
| 217 |
+
d.edgeTopPCoverage !== undefined &&
|
| 218 |
+
(typeof d.edgeTopPCoverage !== 'number' || !Number.isFinite(d.edgeTopPCoverage))
|
| 219 |
+
) {
|
| 220 |
+
return false;
|
| 221 |
+
}
|
| 222 |
+
if (d.nodeCiVisualScaleEnabled !== undefined && typeof d.nodeCiVisualScaleEnabled !== 'boolean') {
|
| 223 |
+
return false;
|
| 224 |
+
}
|
| 225 |
+
if (d.edgeWeakenHighSurprisalEnabled !== undefined && typeof d.edgeWeakenHighSurprisalEnabled !== 'boolean') {
|
| 226 |
+
return false;
|
| 227 |
+
}
|
| 228 |
+
if (d.hideInactiveEdges !== undefined && typeof d.hideInactiveEdges !== 'boolean') return false;
|
| 229 |
+
if (d.replayPacingMode !== undefined && d.replayPacingMode !== 'total' && d.replayPacingMode !== 'step') {
|
| 230 |
+
return false;
|
| 231 |
+
}
|
| 232 |
+
if (d.playbackTotalS !== undefined && (typeof d.playbackTotalS !== 'number' || !Number.isFinite(d.playbackTotalS))) {
|
| 233 |
+
return false;
|
| 234 |
+
}
|
| 235 |
+
if (d.playbackStepMs !== undefined && (typeof d.playbackStepMs !== 'number' || !Number.isFinite(d.playbackStepMs))) {
|
| 236 |
+
return false;
|
| 237 |
+
}
|
| 238 |
+
if (
|
| 239 |
+
d.excludePromptPatternsEnabled !== undefined &&
|
| 240 |
+
typeof d.excludePromptPatternsEnabled !== 'boolean'
|
| 241 |
+
) {
|
| 242 |
+
return false;
|
| 243 |
+
}
|
| 244 |
+
if (d.excludePromptPatternsText !== undefined && typeof d.excludePromptPatternsText !== 'string') {
|
| 245 |
+
return false;
|
| 246 |
+
}
|
| 247 |
+
if (
|
| 248 |
+
d.excludeGeneratedPatternsEnabled !== undefined &&
|
| 249 |
+
typeof d.excludeGeneratedPatternsEnabled !== 'boolean'
|
| 250 |
+
) {
|
| 251 |
+
return false;
|
| 252 |
+
}
|
| 253 |
+
if (
|
| 254 |
+
d.excludeGeneratedPatternsText !== undefined &&
|
| 255 |
+
typeof d.excludeGeneratedPatternsText !== 'string'
|
| 256 |
+
) {
|
| 257 |
+
return false;
|
| 258 |
+
}
|
| 259 |
+
return true;
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
/**
|
| 263 |
+
* 打包 demo JSON 与 Cached history 负载对齐:`steps` 仅要求非空数组(细粒度由运行时承担)。
|
| 264 |
+
*/
|
| 265 |
+
export function isValidGenAttrCachedRunPayload(v: unknown): v is GenAttrCachedRun {
|
| 266 |
+
if (v == null || typeof v !== 'object') return false;
|
| 267 |
+
const o = v as Record<string, unknown>;
|
| 268 |
+
if (typeof o.initialContext !== 'string' || !Array.isArray(o.steps) || o.steps.length === 0) {
|
| 269 |
+
return false;
|
| 270 |
+
}
|
| 271 |
+
if (o.completionReason !== undefined) {
|
| 272 |
+
if (typeof o.completionReason !== 'string' || !isKnownPersistedCompletionReason(o.completionReason)) {
|
| 273 |
+
return false;
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
if (o.promptSpans !== undefined && !isValidPromptSpansPayload(o.promptSpans)) {
|
| 277 |
+
return false;
|
| 278 |
+
}
|
| 279 |
+
if (o.draft !== undefined && !isValidGenAttrRunDraftPayload(o.draft)) {
|
| 280 |
+
return false;
|
| 281 |
+
}
|
| 282 |
+
if (o.demoUiOptions !== undefined && !isValidDemoUiOptionsPayload(o.demoUiOptions)) {
|
| 283 |
+
return false;
|
| 284 |
+
}
|
| 285 |
+
return true;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
/**
|
| 289 |
+
* 加载 demo 与加载 IndexedDB 历史共用的入口:`unknown` → 合法则返回记录,否则打日志并返回 `undefined`。
|
| 290 |
+
*/
|
| 291 |
+
export function parseGenAttrCachedRunPayload(
|
| 292 |
+
raw: unknown,
|
| 293 |
+
contextForLog?: string
|
| 294 |
+
): GenAttrCachedRun | undefined {
|
| 295 |
+
if (!isValidGenAttrCachedRunPayload(raw)) {
|
| 296 |
+
const suffix =
|
| 297 |
+
contextForLog !== undefined && contextForLog.length > 0 ? ` (${contextForLog})` : '';
|
| 298 |
+
console.warn(`[genAttributeRunCache] invalid GenAttrCachedRun payload${suffix}`);
|
| 299 |
+
return undefined;
|
| 300 |
+
}
|
| 301 |
+
return raw;
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
export async function save(
|
| 305 |
+
key: GenAttrCacheKey,
|
| 306 |
+
steps: TokenGenStep[],
|
| 307 |
+
promptSpans: PromptTokenSpan[],
|
| 308 |
+
status: 'partial' | 'complete' = steps.length > 0 ? 'partial' : 'complete',
|
| 309 |
+
completionReason?: CompletionFinishReason,
|
| 310 |
+
draft?: GenAttrRunDraft
|
| 311 |
+
): Promise<void> {
|
| 312 |
+
const { initialContext } = key;
|
| 313 |
+
const payload = buildGenAttrCachedRunContentPayload({
|
| 314 |
+
initialContext,
|
| 315 |
+
steps,
|
| 316 |
+
promptSpans,
|
| 317 |
+
completionReason,
|
| 318 |
+
draft,
|
| 319 |
+
});
|
| 320 |
await upsertEntry({
|
| 321 |
namespace: NAMESPACE,
|
| 322 |
businessKeyJson: JSON.stringify(normalizeKey(key)),
|
|
|
|
| 329 |
|
| 330 |
export async function get(key: GenAttrCacheKey): Promise<GenAttrCachedRun | undefined> {
|
| 331 |
const row = await getByContentKey<GenAttrCachedRun>(NAMESPACE, keyHash(key));
|
| 332 |
+
if (!row) return undefined;
|
| 333 |
+
return parseGenAttrCachedRunPayload(row.payload, 'get(GenAttrCacheKey)');
|
| 334 |
}
|
| 335 |
|
| 336 |
export async function getCachedEntryByContentKey(raw: string): Promise<GenAttrCachedRun | undefined> {
|
| 337 |
if (!raw) return undefined;
|
| 338 |
const row = await getByContentKey<GenAttrCachedRun>(NAMESPACE, raw);
|
| 339 |
+
if (!row) return undefined;
|
| 340 |
+
return parseGenAttrCachedRunPayload(row.payload, `contentKey=${raw}`);
|
| 341 |
}
|
| 342 |
|
| 343 |
export function buildCachedContentUrlParam(key: GenAttrCacheKey): string {
|
client/src/ts/ui/dialog.ts
CHANGED
|
@@ -579,6 +579,34 @@ export function createNamePathTextContent(
|
|
| 579 |
};
|
| 580 |
}
|
| 581 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
/**
|
| 583 |
* 创建 URL 输入弹窗内容(URL 输入框)
|
| 584 |
*/
|
|
@@ -596,7 +624,8 @@ export function createUrlInputContent(
|
|
| 596 |
.text(label);
|
| 597 |
|
| 598 |
const input = container.append('input')
|
| 599 |
-
.attr('type', '
|
|
|
|
| 600 |
.attr('class', 'dialog-input')
|
| 601 |
.attr('value', defaultValue)
|
| 602 |
.attr('placeholder', placeholder || 'https://example.com');
|
|
@@ -626,21 +655,21 @@ export function createUrlInputContent(
|
|
| 626 |
return {
|
| 627 |
getValue: () => {
|
| 628 |
const inputNode = input.node() as HTMLInputElement;
|
| 629 |
-
|
|
|
|
| 630 |
},
|
| 631 |
validate: () => {
|
| 632 |
const inputNode = input.node() as HTMLInputElement;
|
| 633 |
-
const
|
| 634 |
-
|
| 635 |
-
|
| 636 |
return false;
|
| 637 |
}
|
| 638 |
-
|
| 639 |
-
new URL(value);
|
| 640 |
return true;
|
| 641 |
-
} catch {
|
| 642 |
-
return false;
|
| 643 |
}
|
|
|
|
|
|
|
| 644 |
},
|
| 645 |
focus: () => {
|
| 646 |
const inputNode = input.node() as HTMLInputElement;
|
|
|
|
| 579 |
};
|
| 580 |
}
|
| 581 |
|
| 582 |
+
/**
|
| 583 |
+
* 将地址栏风格的输入转为绝对 URL:缺协议时补 `https://`;`//host/path` 先去掉协议相对前缀再补全。
|
| 584 |
+
* 若无法解析则返回 null。
|
| 585 |
+
*/
|
| 586 |
+
function normalizeUserFetchUrl(raw: string): string | null {
|
| 587 |
+
const trimmed = raw.trim();
|
| 588 |
+
if (!trimmed) {
|
| 589 |
+
return null;
|
| 590 |
+
}
|
| 591 |
+
try {
|
| 592 |
+
new URL(trimmed);
|
| 593 |
+
return trimmed;
|
| 594 |
+
} catch {
|
| 595 |
+
if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(trimmed)) {
|
| 596 |
+
return null;
|
| 597 |
+
}
|
| 598 |
+
const rest = trimmed.replace(/^\/+/, '');
|
| 599 |
+
if (!rest) {
|
| 600 |
+
return null;
|
| 601 |
+
}
|
| 602 |
+
try {
|
| 603 |
+
return new URL(`https://${rest}`).href;
|
| 604 |
+
} catch {
|
| 605 |
+
return null;
|
| 606 |
+
}
|
| 607 |
+
}
|
| 608 |
+
}
|
| 609 |
+
|
| 610 |
/**
|
| 611 |
* 创建 URL 输入弹窗内容(URL 输入框)
|
| 612 |
*/
|
|
|
|
| 624 |
.text(label);
|
| 625 |
|
| 626 |
const input = container.append('input')
|
| 627 |
+
.attr('type', 'text')
|
| 628 |
+
.attr('inputmode', 'url')
|
| 629 |
.attr('class', 'dialog-input')
|
| 630 |
.attr('value', defaultValue)
|
| 631 |
.attr('placeholder', placeholder || 'https://example.com');
|
|
|
|
| 655 |
return {
|
| 656 |
getValue: () => {
|
| 657 |
const inputNode = input.node() as HTMLInputElement;
|
| 658 |
+
const raw = inputNode?.value?.trim() || '';
|
| 659 |
+
return normalizeUserFetchUrl(raw) ?? '';
|
| 660 |
},
|
| 661 |
validate: () => {
|
| 662 |
const inputNode = input.node() as HTMLInputElement;
|
| 663 |
+
const raw = inputNode?.value?.trim() || '';
|
| 664 |
+
if (raw.length === 0) {
|
| 665 |
+
showAlertDialog(tr('Invalid input'), tr('Please enter a URL.'));
|
| 666 |
return false;
|
| 667 |
}
|
| 668 |
+
if (normalizeUserFetchUrl(raw)) {
|
|
|
|
| 669 |
return true;
|
|
|
|
|
|
|
| 670 |
}
|
| 671 |
+
showAlertDialog(tr('Invalid URL'), tr('This does not look like a valid URL. Check for typos.'));
|
| 672 |
+
return false;
|
| 673 |
},
|
| 674 |
focus: () => {
|
| 675 |
const inputNode = input.node() as HTMLInputElement;
|