@xiaobuyu/nodesign 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -106
- package/bin/nodesign.js +0 -0
- package/package.json +1 -1
- package/server/api/README.md +45 -45
- package/server/api/_guard.js +75 -75
- package/server/api/admin.js +187 -187
- package/server/api/assets/docx-page.js +103 -103
- package/server/api/assets/helpers.js +42 -22
- package/server/api/assets/notes.js +194 -144
- package/server/api/assets.js +891 -896
- package/server/api/board.js +134 -57
- package/server/api/browse.js +138 -138
- package/server/api/canvas.js +444 -444
- package/server/api/chatai.js +324 -324
- package/server/api/exports/build-standalone.js +980 -980
- package/server/api/exports/cards.js +118 -118
- package/server/api/exports/docx-pdf.js +46 -46
- package/server/api/exports/export-page.js +142 -142
- package/server/api/exports/handoff.js +147 -147
- package/server/api/exports.js +690 -690
- package/server/api/instruction.js +75 -75
- package/server/api/me.js +116 -116
- package/server/api/memory.js +185 -185
- package/server/api/pending-changes.js +366 -366
- package/server/api/plugins.js +164 -164
- package/server/api/projects.js +220 -220
- package/server/api/publish.js +64 -64
- package/server/api/recent.js +73 -73
- package/server/api/sessions.js +589 -589
- package/server/api/skills.js +77 -77
- package/server/api/standalone-fit.js +168 -168
- package/server/api/turn-compose.js +180 -180
- package/server/api/turn-inflight.js +44 -44
- package/server/auth/README.md +17 -17
- package/server/auth/middleware.js +138 -138
- package/server/auth/origin-guard.js +71 -71
- package/server/auth/session.js +108 -108
- package/server/auth/tier.js +113 -113
- package/server/auth/users-store.js +348 -348
- package/server/edit/README.md +29 -29
- package/server/engine/README.md +113 -113
- package/server/engine/agent/agent-shared.js +550 -550
- package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
- package/server/engine/agent/auto-mode-rules.js +78 -78
- package/server/engine/agent/context.js +418 -418
- package/server/engine/agent/events.js +465 -464
- package/server/engine/agent/hooks/canvas-validate.js +199 -199
- package/server/engine/agent/hooks/failure.js +120 -120
- package/server/engine/agent/hooks/file-events.js +75 -75
- package/server/engine/agent/hooks/lifecycle.js +233 -233
- package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
- package/server/engine/agent/hooks/post-guidance.js +206 -206
- package/server/engine/agent/hooks/post-subagent-report.js +104 -104
- package/server/engine/agent/hooks/post-trim.js +58 -58
- package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
- package/server/engine/agent/hooks/pre-defaults.js +84 -84
- package/server/engine/agent/hooks/pre-injectors.js +277 -277
- package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
- package/server/engine/agent/hooks/pre-starter-files.js +61 -61
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
- package/server/engine/agent/hooks/site-validate.js +94 -94
- package/server/engine/agent/hooks/tool-prompts.js +31 -31
- package/server/engine/agent/hooks/turn-state-memory.js +56 -56
- package/server/engine/agent/hooks/user-prompt-submit.js +258 -215
- package/server/engine/agent/hooks.js +354 -354
- package/server/engine/agent/init-contract.js +69 -69
- package/server/engine/agent/isolation.js +159 -159
- package/server/engine/agent/model-table.js +292 -292
- package/server/engine/agent/plugin-loader.js +266 -266
- package/server/engine/agent/prompts/nodesign-prelude.md +388 -336
- package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
- package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
- package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
- package/server/engine/agent/prompts/tools/site-reference.md +106 -106
- package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
- package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
- package/server/engine/agent/session-loop.js +1035 -1024
- package/server/engine/agent/session-model.js +164 -164
- package/server/engine/agent/skill.js +204 -204
- package/server/engine/agent/task-events.js +78 -78
- package/server/engine/agents/ds-extractor.md +132 -132
- package/server/engine/agents/explorer.md +190 -190
- package/server/engine/agents/index.js +230 -230
- package/server/engine/agents/schemas/design-system.json +156 -156
- package/server/engine/agents/schemas/tweak-schema.json +113 -113
- package/server/engine/agents/tweak-proposer.md +127 -127
- package/server/engine/agents/vision-checker.md +254 -254
- package/server/engine/browse/capture.js +422 -422
- package/server/engine/browse/card.js +137 -137
- package/server/engine/browse/handover.js +59 -59
- package/server/engine/browse/page-digest.js +119 -119
- package/server/engine/browse/refs.js +196 -196
- package/server/engine/browse/registry.js +307 -307
- package/server/engine/browse/screencast.js +170 -170
- package/server/engine/browse/state.js +121 -121
- package/server/engine/chatai/chat-log.js +79 -79
- package/server/engine/chatai/index.js +193 -193
- package/server/engine/chatai/openai-compat.js +152 -152
- package/server/engine/chatai/orchestrate.js +309 -309
- package/server/engine/chatai/perform.js +40 -40
- package/server/engine/chatai/summarize.js +79 -79
- package/server/engine/mcp/capability-gate.js +52 -52
- package/server/engine/mcp/index.js +351 -339
- package/server/engine/mcp/param-sanitizer.js +142 -142
- package/server/engine/mcp/tools/arrange-on-board.js +124 -124
- package/server/engine/mcp/tools/artifact-session.js +278 -278
- package/server/engine/mcp/tools/browse-computer.js +421 -421
- package/server/engine/mcp/tools/browse-find-batch.js +183 -183
- package/server/engine/mcp/tools/browse-screenshot.js +157 -157
- package/server/engine/mcp/tools/browse.js +480 -480
- package/server/engine/mcp/tools/build-docx.js +101 -101
- package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
- package/server/engine/mcp/tools/create-on-board.js +152 -155
- package/server/engine/mcp/tools/crystallize-skill.js +168 -168
- package/server/engine/mcp/tools/edit-sketch.js +167 -0
- package/server/engine/mcp/tools/explain-style.js +237 -237
- package/server/engine/mcp/tools/export-handoff.js +133 -133
- package/server/engine/mcp/tools/expose-tweaks.js +149 -149
- package/server/engine/mcp/tools/generate-image.js +842 -842
- package/server/engine/mcp/tools/get-computed-styles.js +164 -164
- package/server/engine/mcp/tools/get-pending-changes.js +205 -205
- package/server/engine/mcp/tools/h3box-ssh.js +84 -84
- package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
- package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
- package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
- package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
- package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
- package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
- package/server/engine/mcp/tools/highlight.js +68 -68
- package/server/engine/mcp/tools/list-pages.js +222 -222
- package/server/engine/mcp/tools/look-at-board.js +124 -0
- package/server/engine/mcp/tools/lookup-tags.js +59 -59
- package/server/engine/mcp/tools/navigate-to-page.js +60 -60
- package/server/engine/mcp/tools/organize-board.js +64 -64
- package/server/engine/mcp/tools/paint-still.js +434 -434
- package/server/engine/mcp/tools/pin-to-board.js +130 -130
- package/server/engine/mcp/tools/preview-deck.js +130 -130
- package/server/engine/mcp/tools/profile-scroll.js +305 -305
- package/server/engine/mcp/tools/publish-site.js +102 -102
- package/server/engine/mcp/tools/query-elements.js +195 -195
- package/server/engine/mcp/tools/read-board.js +190 -103
- package/server/engine/mcp/tools/read-document.js +93 -93
- package/server/engine/mcp/tools/read-page.js +264 -264
- package/server/engine/mcp/tools/read-tavern-json.js +143 -143
- package/server/engine/mcp/tools/read-user-view.js +59 -0
- package/server/engine/mcp/tools/record-decision.js +140 -140
- package/server/engine/mcp/tools/relate-on-board.js +112 -110
- package/server/engine/mcp/tools/report-issue.js +115 -115
- package/server/engine/mcp/tools/roll-film.js +266 -266
- package/server/engine/mcp/tools/screenshot-docx.js +138 -138
- package/server/engine/mcp/tools/screenshot-url.js +195 -195
- package/server/engine/mcp/tools/screenshot.js +583 -583
- package/server/engine/mcp/tools/sketch-on-board.js +337 -0
- package/server/engine/mcp/tools/tier-gate.js +96 -96
- package/server/engine/mcp/tools/trace-motion.js +215 -215
- package/server/engine/mcp/tools/web-search.js +548 -548
- package/server/engine/mcp/tools/write-on-board.js +130 -0
- package/server/engine/motion/inventory.js +349 -349
- package/server/engine/perception/session.js +235 -235
- package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +199 -192
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +169 -162
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
- package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +235 -228
- package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +78 -78
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +330 -323
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
- package/server/engine/runs/active-runs.js +698 -698
- package/server/engine/runs/board-tasklist.js +130 -0
- package/server/engine/runs/live-turn.js +252 -252
- package/server/engine/runs/store.js +364 -364
- package/server/engine/runs/turn-relay.js +217 -217
- package/server/engine/runtime/workspace.js +164 -164
- package/server/index.js +3 -0
- package/server/lib/artifact-file-path.js +80 -80
- package/server/lib/artifact-target.js +284 -284
- package/server/lib/asset-refs.js +239 -239
- package/server/lib/async-queue.js +104 -104
- package/server/lib/auto-relations.js +130 -130
- package/server/lib/binding-types.js +72 -60
- package/server/lib/board-graph-export.js +191 -0
- package/server/lib/board-groups.js +167 -0
- package/server/lib/board-hero.js +76 -0
- package/server/lib/board-kind-sizes.js +85 -62
- package/server/lib/board-relations.js +133 -133
- package/server/lib/browse-proxy.js +180 -180
- package/server/lib/canvas-id.js +36 -36
- package/server/lib/chalk.js +121 -0
- package/server/lib/cover.js +227 -227
- package/server/lib/danbooru-tags.js +291 -291
- package/server/lib/doc-extract.js +156 -156
- package/server/lib/docx/build-from-source.js +260 -260
- package/server/lib/docx/build.js +490 -490
- package/server/lib/docx/dump-styles.js +287 -287
- package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
- package/server/lib/docx/merge-runs.js +129 -129
- package/server/lib/docx/numbering.js +218 -218
- package/server/lib/docx/order.js +152 -152
- package/server/lib/docx/rawzip.js +172 -172
- package/server/lib/docx/render.js +92 -92
- package/server/lib/docx/text-lint.js +225 -225
- package/server/lib/docx/tokens.js +335 -335
- package/server/lib/docx/units.js +29 -29
- package/server/lib/docx/xml.js +291 -291
- package/server/lib/docx-pages.js +187 -187
- package/server/lib/export-collect.js +289 -289
- package/server/lib/export-package.js +210 -210
- package/server/lib/html-srcset.js +145 -145
- package/server/lib/image-variant.js +460 -460
- package/server/lib/ingress/forward-openai-chat.js +305 -305
- package/server/lib/ingress/openai-chat.js +491 -491
- package/server/lib/ingress/session-notice.js +66 -66
- package/server/lib/ingress/session-routes.js +68 -68
- package/server/lib/ingress/slot-probe.js +130 -130
- package/server/lib/ingress/upstream-billing.js +52 -52
- package/server/lib/ingress/upstream-fail-streak.js +72 -72
- package/server/lib/ingress/upstream-truncation.js +48 -48
- package/server/lib/issues-store.js +182 -182
- package/server/lib/kinds/deck.js +72 -72
- package/server/lib/kinds/docx.js +283 -283
- package/server/lib/kinds/file-kinds.js +113 -113
- package/server/lib/kinds/index.js +202 -202
- package/server/lib/kinds/site.js +161 -161
- package/server/lib/model-ingress.js +543 -543
- package/server/lib/moderation.js +255 -255
- package/server/lib/notice-store.js +103 -103
- package/server/lib/plugin-install.js +104 -104
- package/server/lib/plugin-validator.js +721 -721
- package/server/lib/publish-store.js +115 -115
- package/server/lib/quick-summary.js +52 -52
- package/server/lib/quota.js +270 -270
- package/server/lib/rate-window.js +29 -29
- package/server/lib/reference-assets.js +92 -92
- package/server/lib/region-shot.js +135 -135
- package/server/lib/safe-path.js +73 -73
- package/server/lib/sdk-session.js +33 -33
- package/server/lib/showcase-store.js +92 -92
- package/server/lib/site-publish.js +465 -465
- package/server/lib/sketch-layout.js +277 -0
- package/server/lib/ssrf-guard.js +432 -432
- package/server/lib/task-scan.js +158 -158
- package/server/lib/tavern-json.js +154 -154
- package/server/lib/video-variant.js +237 -237
- package/server/lib/workspace-path.js +33 -33
- package/server/ops/fix-sdk-musl.mjs +40 -40
- package/server/ops/install-macos-fonts.sh +114 -114
- package/server/ops/macos-fonts.conf +336 -336
- package/server/ops/sandbox-shim/bwrap +57 -57
- package/server/projects/assets-summary.js +119 -119
- package/server/projects/auto-name.js +53 -53
- package/server/projects/board-limits.js +12 -0
- package/server/projects/board-sanitize.js +252 -0
- package/server/projects/board-store.js +507 -640
- package/server/projects/move-entry.js +103 -103
- package/server/projects/store.js +270 -270
- package/server/projects/ui-config.js +60 -54
- package/server/projects/viewpoint-store.js +78 -0
- package/server/projects/workspace-templates.js +53 -53
- package/server/projects/workspace.js +1025 -1025
- package/server/runtime/local-config.js +177 -177
- package/server/runtime/profile.js +72 -72
- package/server/services/rembg-service.py +334 -334
- package/server/shared/README.md +25 -25
- package/server/shared/deck.js +54 -54
- package/server/shared/time.js +49 -49
- package/server/style-pipeline/README.md +53 -53
- package/server/ws/broker.js +39 -36
- package/server/ws/browse-channel.js +177 -177
- package/server/ws/index.js +386 -386
- package/web/README.md +89 -89
- package/web/dist/assets/BrowserWindow-P_vIdh-G.js +11 -0
- package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-BpLv5082.js} +3 -3
- package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-DSAU1-tD.js} +4 -4
- package/web/dist/assets/MermaidBlock-DR1GikCv.js +313 -0
- package/web/dist/assets/SiteWindow-92uBuRRR.js +18 -0
- package/web/dist/assets/abnfDiagram-VCTEODGH-uA8Esix4.js +1 -0
- package/web/dist/assets/arc-D_khH_xN.js +1 -0
- package/web/dist/assets/architectureDiagram-5GKGNRK7-DydJHqOt.js +36 -0
- package/web/dist/assets/blockDiagram-NRAW4CY4-B7wBbKiW.js +129 -0
- package/web/dist/assets/c4Diagram-UCG6FXSJ-6mrH0x7Y.js +38 -0
- package/web/dist/assets/channel-DGkccMO1.js +1 -0
- package/web/dist/assets/chunk-2Q5K7J3B-Cuikho5Q.js +1 -0
- package/web/dist/assets/chunk-5VM5RSS4-3-KZ5AUz.js +15 -0
- package/web/dist/assets/chunk-F27PBJKO-Bkwq2gGU.js +1 -0
- package/web/dist/assets/chunk-G27WJ6UU-BLyQzdGE.js +231 -0
- package/web/dist/assets/chunk-JWPE2WC7-B7HxxQds.js +1 -0
- package/web/dist/assets/chunk-LCL6LL3I-CtXrnUwF.js +206 -0
- package/web/dist/assets/chunk-POPQ4Y6H-dxddZlXt.js +1 -0
- package/web/dist/assets/chunk-SVP7TREG-DpkmrgHn.js +88 -0
- package/web/dist/assets/chunk-XXDRQBXY-BwZ7RbpQ.js +1 -0
- package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +1 -0
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +1 -0
- package/web/dist/assets/cose-bilkent-JH36ORCC-DGFOsMUt.js +1 -0
- package/web/dist/assets/cynefin-OW5HDTMX-DUwn3ehS.js +178 -0
- package/web/dist/assets/cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js +62 -0
- package/web/dist/assets/cytoscape.esm-BjAbmLdb.js +331 -0
- package/web/dist/assets/dagre-3AP2YEHR-CyourLfz.js +4 -0
- package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/web/dist/assets/diagram-S7CK7UJ4-BTMbx2np.js +30 -0
- package/web/dist/assets/diagram-UQ7AKVKN-UK94_1-P.js +41 -0
- package/web/dist/assets/diagram-VSXAHHWV-C7zS-v9v.js +3 -0
- package/web/dist/assets/diagram-VX7I27RA-DKJRa6CK.js +24 -0
- package/web/dist/assets/diagram-Z3DM3KII-kUbAfGAp.js +24 -0
- package/web/dist/assets/ebnfDiagram-PWID7BFC-CVW16_vb.js +1 -0
- package/web/dist/assets/erDiagram-SSCWMZ5O-BRQMFQ5y.js +99 -0
- package/web/dist/assets/flowDiagram-A5DVABFB-S34LpLtK.js +168 -0
- package/web/dist/assets/ganttDiagram-EL5Y4UJY-BFiqk1-G.js +292 -0
- package/web/dist/assets/gitGraphDiagram-WWUBYQGX-LZsrdyyv.js +106 -0
- package/web/dist/assets/index-GAamh1p4.js +2152 -0
- package/web/dist/assets/infoDiagram-RXCK75RN-0DvN6KIp.js +2 -0
- package/web/dist/assets/init-Gi6I4Gst.js +1 -0
- package/web/dist/assets/ishikawaDiagram-5VMMS53U-CaY3A-Bx.js +70 -0
- package/web/dist/assets/journeyDiagram-EYS64GPL-BBeiaCrj.js +139 -0
- package/web/dist/assets/kanban-definition-3QL26DDD-ux42DRXQ.js +89 -0
- package/web/dist/assets/layout-CnXiF_NB.js +1 -0
- package/web/dist/assets/linear-BMW0u6G6.js +1 -0
- package/web/dist/assets/mindmap-definition-FBJOCRG2-hUxSpdKn.js +96 -0
- package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/web/dist/assets/pegDiagram-XKGWAZYB-aCaQPH0w.js +1 -0
- package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-AcdOQ9Ij.js} +6 -6
- package/web/dist/assets/pieDiagram-E7YTZNPT-CmOEZ8zg.js +39 -0
- package/web/dist/assets/quadrantDiagram-AXDQQJYC-VW6fAhsV.js +7 -0
- package/web/dist/assets/railroadDiagram-O6MQD6OU-CKJ7Ba5K.js +1 -0
- package/web/dist/assets/requirementDiagram-EFPCY7ZU-pNGkq9Vw.js +84 -0
- package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-KAfChbGV.js} +1 -1
- package/web/dist/assets/sankeyDiagram-P5KCCOFB-BIMmDTfZ.js +40 -0
- package/web/dist/assets/sequenceDiagram-WJ2MYXX4-BBW7wzKY.js +162 -0
- package/web/dist/assets/sizeCapture-X5ZJPWSS-DdkXmLYR.js +1 -0
- package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-CDUlhUP7.js} +1 -1
- package/web/dist/assets/stateDiagram-HBIQ2CUA-W313Uz16.js +1 -0
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +1 -0
- package/web/dist/assets/swimlanes-XN3QIQJK-C29TL26K.js +1 -0
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +8 -0
- package/web/dist/assets/timeline-definition-24CTP7MA-CF0JMObX.js +120 -0
- package/web/dist/assets/vennDiagram-4TSXK5OY-DrDFsaH3.js +34 -0
- package/web/dist/assets/wardleyDiagram-VM6X3IG4-CVrnRIS_.js +78 -0
- package/web/dist/assets/xychartDiagram-S5SC5T6Z-BvkN1zsT.js +7 -0
- package/web/dist/index.html +17 -17
- package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
- package/web/dist/assets/SiteWindow-D6eS-2gM.js +0 -18
- package/web/dist/assets/index-Cbl3ATvp.js +0 -2125
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-log.js —— 演出的对话记录与摘要落盘。
|
|
3
|
-
*
|
|
4
|
-
* 对话记录(默认 `对话.jsonl`,可在 编排.yaml 的 历史.文件 改):
|
|
5
|
-
* 一行一条 { seq, role: 'user'|'assistant', text, at }。
|
|
6
|
-
* ⭐ 只存双方真实发言,永远只追加、永远不删——摘要折叠掉的轮次仍然留在
|
|
7
|
-
* 文件里(编译时靠 摘要.json 的 `至` 跳过),全量记录随时可重放、可导出。
|
|
8
|
-
*
|
|
9
|
-
* 摘要(固定名 `摘要.json`,派生态不给改名旋钮):
|
|
10
|
-
* { 至, 内容, 时, 模型, 用量, 花费 } —— `至` 是被折叠的最后一条记录的 seq,
|
|
11
|
-
* 滚动覆盖写(旧摘要在生成新摘要时被并入,不单独存档)。
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import path from 'node:path';
|
|
15
|
-
import fs from 'node:fs/promises';
|
|
16
|
-
|
|
17
|
-
export const SUMMARY_FILE = '摘要.json';
|
|
18
|
-
|
|
19
|
-
function logPath(dir, config) {
|
|
20
|
-
const rel = config?.历史?.文件 || '对话.jsonl';
|
|
21
|
-
const abs = path.resolve(dir, rel);
|
|
22
|
-
if (abs !== path.resolve(dir) && !abs.startsWith(path.resolve(dir) + path.sep)) {
|
|
23
|
-
throw Object.assign(new Error(`历史.文件「${rel}」跑出了演出文件夹`), { code: 'ORCH_INVALID' });
|
|
24
|
-
}
|
|
25
|
-
return abs;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 读全量记录。文件不存在 = 空场;坏行跳过不炸(记录是追加型文件,尾行可能残缺)。 */
|
|
29
|
-
export async function readLog(dir, config) {
|
|
30
|
-
let raw;
|
|
31
|
-
try { raw = await fs.readFile(logPath(dir, config), 'utf8'); } catch { return []; }
|
|
32
|
-
const out = [];
|
|
33
|
-
for (const line of raw.split('\n')) {
|
|
34
|
-
const s = line.trim();
|
|
35
|
-
if (!s) continue;
|
|
36
|
-
try {
|
|
37
|
-
const r = JSON.parse(s);
|
|
38
|
-
if (Number.isInteger(r.seq) && (r.role === 'user' || r.role === 'assistant') && typeof r.text === 'string') {
|
|
39
|
-
out.push(r);
|
|
40
|
-
}
|
|
41
|
-
} catch { /* 残行跳过 */ }
|
|
42
|
-
}
|
|
43
|
-
return out;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 追加一轮(user + assistant 成对写入,调用成功之后才落盘——失败的调用
|
|
48
|
-
* 不留半条记录,重试不会双写)。
|
|
49
|
-
*/
|
|
50
|
-
export async function appendTurn(dir, config, userText, assistantText) {
|
|
51
|
-
const records = await readLog(dir, config);
|
|
52
|
-
const last = records.length ? records[records.length - 1].seq : 0;
|
|
53
|
-
const at = new Date().toISOString();
|
|
54
|
-
const lines = [
|
|
55
|
-
{ seq: last + 1, role: 'user', text: String(userText), at },
|
|
56
|
-
{ seq: last + 2, role: 'assistant', text: String(assistantText), at },
|
|
57
|
-
];
|
|
58
|
-
await fs.appendFile(
|
|
59
|
-
logPath(dir, config),
|
|
60
|
-
lines.map(r => JSON.stringify(r)).join('\n') + '\n',
|
|
61
|
-
'utf8',
|
|
62
|
-
);
|
|
63
|
-
return lines;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function readSummary(dir) {
|
|
67
|
-
try {
|
|
68
|
-
const s = JSON.parse(await fs.readFile(path.join(dir, SUMMARY_FILE), 'utf8'));
|
|
69
|
-
return (s && Number.isInteger(s.至) && typeof s.内容 === 'string') ? s : null;
|
|
70
|
-
} catch { return null; }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** 原子写(tmp + rename):摘要生成要花一次真调用,别让并发读读到半个文件。 */
|
|
74
|
-
export async function writeSummary(dir, summary) {
|
|
75
|
-
const target = path.join(dir, SUMMARY_FILE);
|
|
76
|
-
const tmp = `${target}.tmp`;
|
|
77
|
-
await fs.writeFile(tmp, JSON.stringify(summary, null, 2), 'utf8');
|
|
78
|
-
await fs.rename(tmp, target);
|
|
79
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* chat-log.js —— 演出的对话记录与摘要落盘。
|
|
3
|
+
*
|
|
4
|
+
* 对话记录(默认 `对话.jsonl`,可在 编排.yaml 的 历史.文件 改):
|
|
5
|
+
* 一行一条 { seq, role: 'user'|'assistant', text, at }。
|
|
6
|
+
* ⭐ 只存双方真实发言,永远只追加、永远不删——摘要折叠掉的轮次仍然留在
|
|
7
|
+
* 文件里(编译时靠 摘要.json 的 `至` 跳过),全量记录随时可重放、可导出。
|
|
8
|
+
*
|
|
9
|
+
* 摘要(固定名 `摘要.json`,派生态不给改名旋钮):
|
|
10
|
+
* { 至, 内容, 时, 模型, 用量, 花费 } —— `至` 是被折叠的最后一条记录的 seq,
|
|
11
|
+
* 滚动覆盖写(旧摘要在生成新摘要时被并入,不单独存档)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import fs from 'node:fs/promises';
|
|
16
|
+
|
|
17
|
+
export const SUMMARY_FILE = '摘要.json';
|
|
18
|
+
|
|
19
|
+
function logPath(dir, config) {
|
|
20
|
+
const rel = config?.历史?.文件 || '对话.jsonl';
|
|
21
|
+
const abs = path.resolve(dir, rel);
|
|
22
|
+
if (abs !== path.resolve(dir) && !abs.startsWith(path.resolve(dir) + path.sep)) {
|
|
23
|
+
throw Object.assign(new Error(`历史.文件「${rel}」跑出了演出文件夹`), { code: 'ORCH_INVALID' });
|
|
24
|
+
}
|
|
25
|
+
return abs;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 读全量记录。文件不存在 = 空场;坏行跳过不炸(记录是追加型文件,尾行可能残缺)。 */
|
|
29
|
+
export async function readLog(dir, config) {
|
|
30
|
+
let raw;
|
|
31
|
+
try { raw = await fs.readFile(logPath(dir, config), 'utf8'); } catch { return []; }
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const line of raw.split('\n')) {
|
|
34
|
+
const s = line.trim();
|
|
35
|
+
if (!s) continue;
|
|
36
|
+
try {
|
|
37
|
+
const r = JSON.parse(s);
|
|
38
|
+
if (Number.isInteger(r.seq) && (r.role === 'user' || r.role === 'assistant') && typeof r.text === 'string') {
|
|
39
|
+
out.push(r);
|
|
40
|
+
}
|
|
41
|
+
} catch { /* 残行跳过 */ }
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 追加一轮(user + assistant 成对写入,调用成功之后才落盘——失败的调用
|
|
48
|
+
* 不留半条记录,重试不会双写)。
|
|
49
|
+
*/
|
|
50
|
+
export async function appendTurn(dir, config, userText, assistantText) {
|
|
51
|
+
const records = await readLog(dir, config);
|
|
52
|
+
const last = records.length ? records[records.length - 1].seq : 0;
|
|
53
|
+
const at = new Date().toISOString();
|
|
54
|
+
const lines = [
|
|
55
|
+
{ seq: last + 1, role: 'user', text: String(userText), at },
|
|
56
|
+
{ seq: last + 2, role: 'assistant', text: String(assistantText), at },
|
|
57
|
+
];
|
|
58
|
+
await fs.appendFile(
|
|
59
|
+
logPath(dir, config),
|
|
60
|
+
lines.map(r => JSON.stringify(r)).join('\n') + '\n',
|
|
61
|
+
'utf8',
|
|
62
|
+
);
|
|
63
|
+
return lines;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function readSummary(dir) {
|
|
67
|
+
try {
|
|
68
|
+
const s = JSON.parse(await fs.readFile(path.join(dir, SUMMARY_FILE), 'utf8'));
|
|
69
|
+
return (s && Number.isInteger(s.至) && typeof s.内容 === 'string') ? s : null;
|
|
70
|
+
} catch { return null; }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 原子写(tmp + rename):摘要生成要花一次真调用,别让并发读读到半个文件。 */
|
|
74
|
+
export async function writeSummary(dir, summary) {
|
|
75
|
+
const target = path.join(dir, SUMMARY_FILE);
|
|
76
|
+
const tmp = `${target}.tmp`;
|
|
77
|
+
await fs.writeFile(tmp, JSON.stringify(summary, null, 2), 'utf8');
|
|
78
|
+
await fs.rename(tmp, target);
|
|
79
|
+
}
|
|
@@ -1,193 +1,193 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chatai —— 第二条模型通路(跟 agent 会话完全无关)
|
|
3
|
-
*
|
|
4
|
-
* agent 走 Claude Agent SDK、有工具、会改文件;**chatai 只是一次补全调用**:
|
|
5
|
-
* 传进去 system + messages,回来一段文本和用量。它没有工具、没有会话、
|
|
6
|
-
* 改不了任何东西。
|
|
7
|
-
*
|
|
8
|
-
* 存在的理由不是省钱,是**别让高频生成挤掉设计会话**:订阅通路是全站共享的一个
|
|
9
|
-
* OAuth 额度加并发上限,而生成类动作频次高,一跑起来能把正经设计会话挤没。
|
|
10
|
-
*
|
|
11
|
-
* 通路配置走 env(`CHATAI_BASE_URL` / `CHATAI_API_KEY` / `CHATAI_MODEL`),
|
|
12
|
-
* 密钥不进代码不进 git。
|
|
13
|
-
*
|
|
14
|
-
* 计价:上游中转站的计量口径不透明,所以金额一律按**我们自己的单价表**算,
|
|
15
|
-
* 谁要拿它进配额闸门就用这个数。
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { callOpenAICompat } from './openai-compat.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 模型表 —— 单价、思考档位、中转站实名,一张表说完(2026-08-15 加思考档)。
|
|
22
|
-
*
|
|
23
|
-
* 单价 USD / 1M token。gemini-3.7-flash 是促销价,2027-01-01 起翻倍到 1.5 / 7.5。
|
|
24
|
-
* 思考 token 按输出计价(Gemini 的口径;Claude 的思考量直接混在 completion_tokens 里)。
|
|
25
|
-
*
|
|
26
|
-
* ⭐ **思考等级在中转站是模型名的一部分,不是请求参数**(2026-08-15 实测它家 100 个
|
|
27
|
-
* 模型:gemini 系是 -low/-medium/-high 三个独立模型名,claude 系是 base 与 -thinking
|
|
28
|
-
* 两个)。所以「档」不发 reasoning_effort,而是换实名。用户那边只看见标准名 + 一个
|
|
29
|
-
* 档位旋钮,换渠道时改这张表就行。
|
|
30
|
-
*
|
|
31
|
-
* 思耗 = 粗估的「思考 token ÷ 正文 token」倍数,只给设置页算预演帐目用,不进计费。
|
|
32
|
-
* 实测(演出类提示,2026-08-15):gemini flash 正文 234 / 思考 785 ≈ 3.4 倍,且
|
|
33
|
-
* 低/中/高 三档的思考量差异淹在噪声里(785/587/601),别把它当精确旋钮;
|
|
34
|
-
* claude 开思考后正文 363→408、输入 115→144(中转站往提示里塞了东西)。
|
|
35
|
-
*/
|
|
36
|
-
export const MODEL_SPECS = Object.freeze({
|
|
37
|
-
'gemini-3.7-flash': {
|
|
38
|
-
价: { in: 0.75, out: 3.75 }, 思耗: 3.4,
|
|
39
|
-
注: '$0.75 / $3.75 每百万 · 促销价,思考计入输出',
|
|
40
|
-
默认档: '低',
|
|
41
|
-
档: {
|
|
42
|
-
低: { 名: '反重力-流式抗截断/gemini-3.7-flash-low', 注: '最省,中转站的常规档' },
|
|
43
|
-
中: { 名: '反重力-流式抗截断/gemini-3.7-flash-medium', 注: '中档' },
|
|
44
|
-
高: { 名: '反重力-流式抗截断/gemini-3.7-flash-high', 注: '让它多想一会儿(实测三档差异有限,别指望它是精确旋钮)' },
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
'gemini-3.6-flash': {
|
|
48
|
-
价: { in: 0.75, out: 3.75 }, 思耗: 3.4,
|
|
49
|
-
注: '$0.75 / $3.75 每百万 · 上一版,行文更收敛',
|
|
50
|
-
默认档: '低',
|
|
51
|
-
档: {
|
|
52
|
-
低: { 名: '反重力-流式抗截断/gemini-3.6-flash-low', 注: '最省,中转站的常规档' },
|
|
53
|
-
中: { 名: '反重力-流式抗截断/gemini-3.6-flash-medium', 注: '中档' },
|
|
54
|
-
高: { 名: '反重力-流式抗截断/gemini-3.6-flash-high', 注: '让它多想一会儿' },
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
'gemini-3-flash': {
|
|
58
|
-
价: { in: 0.50, out: 2.50 }, 思耗: 3.0,
|
|
59
|
-
注: '$0.50 / $2.50 每百万 · 更便宜,思考档不可调',
|
|
60
|
-
实名: '反重力-流式抗截断/gemini-3-flash',
|
|
61
|
-
档: null,
|
|
62
|
-
},
|
|
63
|
-
'claude-sonnet-4-6': {
|
|
64
|
-
价: { in: 3.00, out: 15.00 }, 思耗: 1,
|
|
65
|
-
注: '$3 / $15 每百万 · 文笔更稳,一轮贵一个量级',
|
|
66
|
-
默认档: '关',
|
|
67
|
-
档: {
|
|
68
|
-
关: { 名: '反重力-流式抗截断/claude-sonnet-4-6', 思耗: 1, 注: '不开扩展思考' },
|
|
69
|
-
高: { 名: '反重力-流式抗截断/claude-sonnet-4-6-thinking', 思耗: 1.3, 注: '开扩展思考:思考走单独通道不进台词,但按输出计价' },
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
'claude-opus-4-6': {
|
|
73
|
-
价: { in: 15.00, out: 75.00 }, 思耗: 1,
|
|
74
|
-
注: '$15 / $75 每百万 · 只在真的在乎的场合;中转站常报没容量',
|
|
75
|
-
默认档: '关',
|
|
76
|
-
档: {
|
|
77
|
-
关: { 名: '反重力-claude-opus-4-6', 思耗: 1, 注: '不开扩展思考' },
|
|
78
|
-
高: { 名: '反重力-claude-opus-4-6-thinking', 思耗: 1.3, 注: '开扩展思考:思考不进台词,但按输出计价' },
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
/** 档位词表 —— 编排.yaml 只认这四个词,设置页只画模型支持的那几档 */
|
|
84
|
-
export const LEVELS = Object.freeze(['关', '低', '中', '高']);
|
|
85
|
-
|
|
86
|
-
/** 单价表(USD / 1M token),从模型表派生 —— 价格只有一处定义 */
|
|
87
|
-
export const PRICES = Object.freeze({
|
|
88
|
-
...Object.fromEntries(Object.entries(MODEL_SPECS).map(([k, v]) => [k, v.价])),
|
|
89
|
-
_default: { in: 1.00, out: 5.00 },
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
/** 从中转站的花式模型名里认出计价用的底模(`反重力-流式抗截断/gemini-3.7-flash-low`) */
|
|
93
|
-
export function priceKeyOf(model) {
|
|
94
|
-
const s = String(model || '').toLowerCase();
|
|
95
|
-
for (const k of Object.keys(PRICES)) {
|
|
96
|
-
if (k !== '_default' && s.includes(k)) return k;
|
|
97
|
-
}
|
|
98
|
-
return '_default';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** 按单价表估这次调用多少钱。缓存读按输入价一折(隐式缓存的通行口径) */
|
|
102
|
-
export function costOf(model, usage) {
|
|
103
|
-
const p = PRICES[priceKeyOf(model)];
|
|
104
|
-
const inTok = Math.max(0, (usage.inputTokens || 0) - (usage.cacheReadTokens || 0));
|
|
105
|
-
const cached = usage.cacheReadTokens || 0;
|
|
106
|
-
const outTok = (usage.outputTokens || 0) + (usage.reasoningTokens || 0);
|
|
107
|
-
return (inTok * p.in + cached * p.in * 0.1 + outTok * p.out) / 1e6;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function chataiConfig() {
|
|
111
|
-
const base = (process.env.CHATAI_BASE_URL || '').replace(/\/+$/, '');
|
|
112
|
-
const key = process.env.CHATAI_API_KEY || '';
|
|
113
|
-
const model = process.env.CHATAI_MODEL || '';
|
|
114
|
-
return { base, key, model, ok: !!(base && key && model) };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** 简写 → 标准名(历史遗留的手写值,别再往里加) */
|
|
118
|
-
const SHORTHAND = Object.freeze({ 'claude-sonnet': 'claude-sonnet-4-6' });
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 标准模型名 + 思考档 → 中转站实名(2026-08-15 时停之城 503 案 + 思考档)。
|
|
122
|
-
*
|
|
123
|
-
* 中转站的 100 个模型**没有一个裸标准名**,全是「反重力-流式抗截断/xxx」这类
|
|
124
|
-
* 前缀变体,思考等级也编在名字里。而 编排.yaml / 设置页 / skill 教的都是标准名
|
|
125
|
-
* (用户不该学中转站的命名黑话,换渠道也不用改配置)。这层在发请求前换成实名。
|
|
126
|
-
*
|
|
127
|
-
* 档给空或者这个模型不支持这档 → 落到模型的默认档(每个模型的默认档都是最便宜
|
|
128
|
-
* 那档),并把降级理由报回去:设置页只画支持的档,但 编排.yaml 是 agent 和人手写的,
|
|
129
|
-
* 写岔了不该拒开演出,只该说清楚跑的是哪档。
|
|
130
|
-
* 表里没有的模型名原样放行(直接写实名也合法)。env `CHATAI_MODEL_ALIASES`(JSON)
|
|
131
|
-
* 覆盖标准名→实名,写了就绕开档位(中转站哪天改名,改 env 不用动代码)。
|
|
132
|
-
*
|
|
133
|
-
* @returns {{ 名: string, 档: string|null, 降级: string|null, 思耗: number }}
|
|
134
|
-
*/
|
|
135
|
-
export function resolveModelVariant(model, 档) {
|
|
136
|
-
const empty = { 名: model, 档: null, 降级: null, 思耗: 1 };
|
|
137
|
-
if (!model) return empty;
|
|
138
|
-
let extra = {};
|
|
139
|
-
try { extra = JSON.parse(process.env.CHATAI_MODEL_ALIASES || '{}'); } catch { /* 坏 JSON 当没配 */ }
|
|
140
|
-
if (extra[model]) return { ...empty, 名: extra[model] };
|
|
141
|
-
|
|
142
|
-
const canonical = SHORTHAND[model] || model;
|
|
143
|
-
const spec = MODEL_SPECS[canonical];
|
|
144
|
-
if (!spec) return empty;
|
|
145
|
-
if (!spec.档) {
|
|
146
|
-
return {
|
|
147
|
-
名: spec.实名, 档: null, 思耗: spec.思耗,
|
|
148
|
-
降级: 档 ? `${canonical} 的思考档不可调,「${档}」没生效` : null,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
const 生效 = spec.档[档] ? 档 : spec.默认档;
|
|
152
|
-
const v = spec.档[生效];
|
|
153
|
-
return {
|
|
154
|
-
名: v.名, 档: 生效, 思耗: v.思耗 ?? spec.思耗,
|
|
155
|
-
降级: 档 && 生效 !== 档 ? `${canonical} 没有「${档}」档,按默认档「${生效}」跑` : null,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** 只要实名的老口径(默认档) */
|
|
160
|
-
export function resolveModelAlias(model) {
|
|
161
|
-
return resolveModelVariant(model).名;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** 给设置页的模型目录:单价、档位、注释都从这一张表来,前端不再各存一份 */
|
|
165
|
-
export function modelCatalog() {
|
|
166
|
-
return Object.entries(MODEL_SPECS).map(([id, s]) => ({
|
|
167
|
-
id, 入: s.价.in, 出: s.价.out, 注: s.注, 思耗: s.思耗,
|
|
168
|
-
档: s.档 ? Object.entries(s.档).map(([名, v]) => ({ 名, 思耗: v.思耗 ?? s.思耗, 注: v.注 ?? null })) : [],
|
|
169
|
-
默认档: s.默认档 ?? null,
|
|
170
|
-
}));
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* 跑一次 chatai。
|
|
175
|
-
* @param {object} opts system / messages / model / 思考 / maxTokens / signal / onDelta
|
|
176
|
-
*/
|
|
177
|
-
export async function runChatai({
|
|
178
|
-
system = '', messages = [], model, 思考 = null, maxTokens = 2000, signal, onDelta,
|
|
179
|
-
} = {}) {
|
|
180
|
-
const cfg = chataiConfig();
|
|
181
|
-
if (!cfg.ok) {
|
|
182
|
-
throw Object.assign(
|
|
183
|
-
new Error('chatai 通路没配置(CHATAI_BASE_URL / CHATAI_API_KEY / CHATAI_MODEL)'),
|
|
184
|
-
{ code: 'CHATAI_UNCONFIGURED' },
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
const v = resolveModelVariant(model || cfg.model, 思考);
|
|
188
|
-
const out = await callOpenAICompat({
|
|
189
|
-
base: cfg.base, key: cfg.key, model: v.名,
|
|
190
|
-
system, messages, maxTokens, signal, onDelta,
|
|
191
|
-
});
|
|
192
|
-
return { ...out, model: v.名, 档: v.档, 降级: v.降级, costUsd: costOf(v.名, out.usage) };
|
|
193
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* chatai —— 第二条模型通路(跟 agent 会话完全无关)
|
|
3
|
+
*
|
|
4
|
+
* agent 走 Claude Agent SDK、有工具、会改文件;**chatai 只是一次补全调用**:
|
|
5
|
+
* 传进去 system + messages,回来一段文本和用量。它没有工具、没有会话、
|
|
6
|
+
* 改不了任何东西。
|
|
7
|
+
*
|
|
8
|
+
* 存在的理由不是省钱,是**别让高频生成挤掉设计会话**:订阅通路是全站共享的一个
|
|
9
|
+
* OAuth 额度加并发上限,而生成类动作频次高,一跑起来能把正经设计会话挤没。
|
|
10
|
+
*
|
|
11
|
+
* 通路配置走 env(`CHATAI_BASE_URL` / `CHATAI_API_KEY` / `CHATAI_MODEL`),
|
|
12
|
+
* 密钥不进代码不进 git。
|
|
13
|
+
*
|
|
14
|
+
* 计价:上游中转站的计量口径不透明,所以金额一律按**我们自己的单价表**算,
|
|
15
|
+
* 谁要拿它进配额闸门就用这个数。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { callOpenAICompat } from './openai-compat.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 模型表 —— 单价、思考档位、中转站实名,一张表说完(2026-08-15 加思考档)。
|
|
22
|
+
*
|
|
23
|
+
* 单价 USD / 1M token。gemini-3.7-flash 是促销价,2027-01-01 起翻倍到 1.5 / 7.5。
|
|
24
|
+
* 思考 token 按输出计价(Gemini 的口径;Claude 的思考量直接混在 completion_tokens 里)。
|
|
25
|
+
*
|
|
26
|
+
* ⭐ **思考等级在中转站是模型名的一部分,不是请求参数**(2026-08-15 实测它家 100 个
|
|
27
|
+
* 模型:gemini 系是 -low/-medium/-high 三个独立模型名,claude 系是 base 与 -thinking
|
|
28
|
+
* 两个)。所以「档」不发 reasoning_effort,而是换实名。用户那边只看见标准名 + 一个
|
|
29
|
+
* 档位旋钮,换渠道时改这张表就行。
|
|
30
|
+
*
|
|
31
|
+
* 思耗 = 粗估的「思考 token ÷ 正文 token」倍数,只给设置页算预演帐目用,不进计费。
|
|
32
|
+
* 实测(演出类提示,2026-08-15):gemini flash 正文 234 / 思考 785 ≈ 3.4 倍,且
|
|
33
|
+
* 低/中/高 三档的思考量差异淹在噪声里(785/587/601),别把它当精确旋钮;
|
|
34
|
+
* claude 开思考后正文 363→408、输入 115→144(中转站往提示里塞了东西)。
|
|
35
|
+
*/
|
|
36
|
+
export const MODEL_SPECS = Object.freeze({
|
|
37
|
+
'gemini-3.7-flash': {
|
|
38
|
+
价: { in: 0.75, out: 3.75 }, 思耗: 3.4,
|
|
39
|
+
注: '$0.75 / $3.75 每百万 · 促销价,思考计入输出',
|
|
40
|
+
默认档: '低',
|
|
41
|
+
档: {
|
|
42
|
+
低: { 名: '反重力-流式抗截断/gemini-3.7-flash-low', 注: '最省,中转站的常规档' },
|
|
43
|
+
中: { 名: '反重力-流式抗截断/gemini-3.7-flash-medium', 注: '中档' },
|
|
44
|
+
高: { 名: '反重力-流式抗截断/gemini-3.7-flash-high', 注: '让它多想一会儿(实测三档差异有限,别指望它是精确旋钮)' },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
'gemini-3.6-flash': {
|
|
48
|
+
价: { in: 0.75, out: 3.75 }, 思耗: 3.4,
|
|
49
|
+
注: '$0.75 / $3.75 每百万 · 上一版,行文更收敛',
|
|
50
|
+
默认档: '低',
|
|
51
|
+
档: {
|
|
52
|
+
低: { 名: '反重力-流式抗截断/gemini-3.6-flash-low', 注: '最省,中转站的常规档' },
|
|
53
|
+
中: { 名: '反重力-流式抗截断/gemini-3.6-flash-medium', 注: '中档' },
|
|
54
|
+
高: { 名: '反重力-流式抗截断/gemini-3.6-flash-high', 注: '让它多想一会儿' },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
'gemini-3-flash': {
|
|
58
|
+
价: { in: 0.50, out: 2.50 }, 思耗: 3.0,
|
|
59
|
+
注: '$0.50 / $2.50 每百万 · 更便宜,思考档不可调',
|
|
60
|
+
实名: '反重力-流式抗截断/gemini-3-flash',
|
|
61
|
+
档: null,
|
|
62
|
+
},
|
|
63
|
+
'claude-sonnet-4-6': {
|
|
64
|
+
价: { in: 3.00, out: 15.00 }, 思耗: 1,
|
|
65
|
+
注: '$3 / $15 每百万 · 文笔更稳,一轮贵一个量级',
|
|
66
|
+
默认档: '关',
|
|
67
|
+
档: {
|
|
68
|
+
关: { 名: '反重力-流式抗截断/claude-sonnet-4-6', 思耗: 1, 注: '不开扩展思考' },
|
|
69
|
+
高: { 名: '反重力-流式抗截断/claude-sonnet-4-6-thinking', 思耗: 1.3, 注: '开扩展思考:思考走单独通道不进台词,但按输出计价' },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
'claude-opus-4-6': {
|
|
73
|
+
价: { in: 15.00, out: 75.00 }, 思耗: 1,
|
|
74
|
+
注: '$15 / $75 每百万 · 只在真的在乎的场合;中转站常报没容量',
|
|
75
|
+
默认档: '关',
|
|
76
|
+
档: {
|
|
77
|
+
关: { 名: '反重力-claude-opus-4-6', 思耗: 1, 注: '不开扩展思考' },
|
|
78
|
+
高: { 名: '反重力-claude-opus-4-6-thinking', 思耗: 1.3, 注: '开扩展思考:思考不进台词,但按输出计价' },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/** 档位词表 —— 编排.yaml 只认这四个词,设置页只画模型支持的那几档 */
|
|
84
|
+
export const LEVELS = Object.freeze(['关', '低', '中', '高']);
|
|
85
|
+
|
|
86
|
+
/** 单价表(USD / 1M token),从模型表派生 —— 价格只有一处定义 */
|
|
87
|
+
export const PRICES = Object.freeze({
|
|
88
|
+
...Object.fromEntries(Object.entries(MODEL_SPECS).map(([k, v]) => [k, v.价])),
|
|
89
|
+
_default: { in: 1.00, out: 5.00 },
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
/** 从中转站的花式模型名里认出计价用的底模(`反重力-流式抗截断/gemini-3.7-flash-low`) */
|
|
93
|
+
export function priceKeyOf(model) {
|
|
94
|
+
const s = String(model || '').toLowerCase();
|
|
95
|
+
for (const k of Object.keys(PRICES)) {
|
|
96
|
+
if (k !== '_default' && s.includes(k)) return k;
|
|
97
|
+
}
|
|
98
|
+
return '_default';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 按单价表估这次调用多少钱。缓存读按输入价一折(隐式缓存的通行口径) */
|
|
102
|
+
export function costOf(model, usage) {
|
|
103
|
+
const p = PRICES[priceKeyOf(model)];
|
|
104
|
+
const inTok = Math.max(0, (usage.inputTokens || 0) - (usage.cacheReadTokens || 0));
|
|
105
|
+
const cached = usage.cacheReadTokens || 0;
|
|
106
|
+
const outTok = (usage.outputTokens || 0) + (usage.reasoningTokens || 0);
|
|
107
|
+
return (inTok * p.in + cached * p.in * 0.1 + outTok * p.out) / 1e6;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function chataiConfig() {
|
|
111
|
+
const base = (process.env.CHATAI_BASE_URL || '').replace(/\/+$/, '');
|
|
112
|
+
const key = process.env.CHATAI_API_KEY || '';
|
|
113
|
+
const model = process.env.CHATAI_MODEL || '';
|
|
114
|
+
return { base, key, model, ok: !!(base && key && model) };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 简写 → 标准名(历史遗留的手写值,别再往里加) */
|
|
118
|
+
const SHORTHAND = Object.freeze({ 'claude-sonnet': 'claude-sonnet-4-6' });
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 标准模型名 + 思考档 → 中转站实名(2026-08-15 时停之城 503 案 + 思考档)。
|
|
122
|
+
*
|
|
123
|
+
* 中转站的 100 个模型**没有一个裸标准名**,全是「反重力-流式抗截断/xxx」这类
|
|
124
|
+
* 前缀变体,思考等级也编在名字里。而 编排.yaml / 设置页 / skill 教的都是标准名
|
|
125
|
+
* (用户不该学中转站的命名黑话,换渠道也不用改配置)。这层在发请求前换成实名。
|
|
126
|
+
*
|
|
127
|
+
* 档给空或者这个模型不支持这档 → 落到模型的默认档(每个模型的默认档都是最便宜
|
|
128
|
+
* 那档),并把降级理由报回去:设置页只画支持的档,但 编排.yaml 是 agent 和人手写的,
|
|
129
|
+
* 写岔了不该拒开演出,只该说清楚跑的是哪档。
|
|
130
|
+
* 表里没有的模型名原样放行(直接写实名也合法)。env `CHATAI_MODEL_ALIASES`(JSON)
|
|
131
|
+
* 覆盖标准名→实名,写了就绕开档位(中转站哪天改名,改 env 不用动代码)。
|
|
132
|
+
*
|
|
133
|
+
* @returns {{ 名: string, 档: string|null, 降级: string|null, 思耗: number }}
|
|
134
|
+
*/
|
|
135
|
+
export function resolveModelVariant(model, 档) {
|
|
136
|
+
const empty = { 名: model, 档: null, 降级: null, 思耗: 1 };
|
|
137
|
+
if (!model) return empty;
|
|
138
|
+
let extra = {};
|
|
139
|
+
try { extra = JSON.parse(process.env.CHATAI_MODEL_ALIASES || '{}'); } catch { /* 坏 JSON 当没配 */ }
|
|
140
|
+
if (extra[model]) return { ...empty, 名: extra[model] };
|
|
141
|
+
|
|
142
|
+
const canonical = SHORTHAND[model] || model;
|
|
143
|
+
const spec = MODEL_SPECS[canonical];
|
|
144
|
+
if (!spec) return empty;
|
|
145
|
+
if (!spec.档) {
|
|
146
|
+
return {
|
|
147
|
+
名: spec.实名, 档: null, 思耗: spec.思耗,
|
|
148
|
+
降级: 档 ? `${canonical} 的思考档不可调,「${档}」没生效` : null,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const 生效 = spec.档[档] ? 档 : spec.默认档;
|
|
152
|
+
const v = spec.档[生效];
|
|
153
|
+
return {
|
|
154
|
+
名: v.名, 档: 生效, 思耗: v.思耗 ?? spec.思耗,
|
|
155
|
+
降级: 档 && 生效 !== 档 ? `${canonical} 没有「${档}」档,按默认档「${生效}」跑` : null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** 只要实名的老口径(默认档) */
|
|
160
|
+
export function resolveModelAlias(model) {
|
|
161
|
+
return resolveModelVariant(model).名;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** 给设置页的模型目录:单价、档位、注释都从这一张表来,前端不再各存一份 */
|
|
165
|
+
export function modelCatalog() {
|
|
166
|
+
return Object.entries(MODEL_SPECS).map(([id, s]) => ({
|
|
167
|
+
id, 入: s.价.in, 出: s.价.out, 注: s.注, 思耗: s.思耗,
|
|
168
|
+
档: s.档 ? Object.entries(s.档).map(([名, v]) => ({ 名, 思耗: v.思耗 ?? s.思耗, 注: v.注 ?? null })) : [],
|
|
169
|
+
默认档: s.默认档 ?? null,
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 跑一次 chatai。
|
|
175
|
+
* @param {object} opts system / messages / model / 思考 / maxTokens / signal / onDelta
|
|
176
|
+
*/
|
|
177
|
+
export async function runChatai({
|
|
178
|
+
system = '', messages = [], model, 思考 = null, maxTokens = 2000, signal, onDelta,
|
|
179
|
+
} = {}) {
|
|
180
|
+
const cfg = chataiConfig();
|
|
181
|
+
if (!cfg.ok) {
|
|
182
|
+
throw Object.assign(
|
|
183
|
+
new Error('chatai 通路没配置(CHATAI_BASE_URL / CHATAI_API_KEY / CHATAI_MODEL)'),
|
|
184
|
+
{ code: 'CHATAI_UNCONFIGURED' },
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
const v = resolveModelVariant(model || cfg.model, 思考);
|
|
188
|
+
const out = await callOpenAICompat({
|
|
189
|
+
base: cfg.base, key: cfg.key, model: v.名,
|
|
190
|
+
system, messages, maxTokens, signal, onDelta,
|
|
191
|
+
});
|
|
192
|
+
return { ...out, model: v.名, 档: v.档, 降级: v.降级, costUsd: costOf(v.名, out.usage) };
|
|
193
|
+
}
|