cortico 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/package.json +43 -0
- package/src/boot.ts +46 -0
- package/src/bot.ts +1342 -0
- package/src/config-file.ts +41 -0
- package/src/core/README.md +92 -0
- package/src/core/billing.ts +26 -0
- package/src/core/blobs.ts +106 -0
- package/src/core/bus.ts +311 -0
- package/src/core/config-schema.ts +201 -0
- package/src/core/config.ts +262 -0
- package/src/core/core.ts +699 -0
- package/src/core/cost.ts +244 -0
- package/src/core/event-store.ts +304 -0
- package/src/core/fork.ts +113 -0
- package/src/core/generation.ts +118 -0
- package/src/core/instance-lock.ts +215 -0
- package/src/core/ipc-logger.ts +105 -0
- package/src/core/language.ts +62 -0
- package/src/core/log-context.ts +38 -0
- package/src/core/loop.ts +1858 -0
- package/src/core/markers.ts +48 -0
- package/src/core/prefix.ts +121 -0
- package/src/core/run.ts +95 -0
- package/src/core/secrets.ts +18 -0
- package/src/core/session.ts +56 -0
- package/src/core/sessions.ts +261 -0
- package/src/core/state.ts +104 -0
- package/src/core/template.ts +72 -0
- package/src/core/timers.ts +129 -0
- package/src/core/tool-log.ts +135 -0
- package/src/core/transcript.ts +92 -0
- package/src/core/truncate.ts +121 -0
- package/src/core/types.ts +1140 -0
- package/src/core/usage-log.ts +79 -0
- package/src/core/util.ts +386 -0
- package/src/deploy-listing.ts +160 -0
- package/src/deploy.ts +162 -0
- package/src/extensions/README.md +72 -0
- package/src/extensions/dry-mount.ts +390 -0
- package/src/extensions/manifest.ts +153 -0
- package/src/extensions/runtime.ts +42 -0
- package/src/extensions.ts +519 -0
- package/src/launcher.ts +285 -0
- package/src/paths.ts +155 -0
- package/src/protocol/open-responses/LICENSE +201 -0
- package/src/protocol/open-responses/README.md +14 -0
- package/src/protocol/open-responses/context-helpers.ts +27 -0
- package/src/protocol/open-responses/context-log.ts +59 -0
- package/src/protocol/open-responses/context.ts +89 -0
- package/src/protocol/open-responses/generated.ts +115 -0
- package/src/protocol/open-responses/index.ts +33 -0
- package/src/protocol/open-responses/openapi.json +4230 -0
- package/src/protocol/open-responses/stream.ts +146 -0
- package/src/protocol/open-responses/tokens.ts +31 -0
- package/src/providers/README.md +86 -0
- package/src/providers/base.ts +111 -0
- package/src/providers/configuration.ts +97 -0
- package/src/providers/console/config.ts +21 -0
- package/src/providers/console/settings.ts +443 -0
- package/src/providers/console/strings.ts +74 -0
- package/src/providers/console/types.ts +11 -0
- package/src/providers/llamacpp/archive.ts +148 -0
- package/src/providers/llamacpp/catalog.ts +247 -0
- package/src/providers/llamacpp/config.ts +36 -0
- package/src/providers/llamacpp/console/client.ts +6 -0
- package/src/providers/llamacpp/console/models-panel.ts +120 -0
- package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
- package/src/providers/llamacpp/console/server.ts +153 -0
- package/src/providers/llamacpp/index.ts +95 -0
- package/src/providers/llamacpp/native.ts +51 -0
- package/src/providers/llamacpp/options.ts +117 -0
- package/src/providers/llamacpp/runtime-store.ts +139 -0
- package/src/providers/llamacpp/runtime.ts +214 -0
- package/src/providers/llamacpp/server.ts +286 -0
- package/src/providers/llamacpp/strings.ts +230 -0
- package/src/providers/openai-responses-compat/config.ts +20 -0
- package/src/providers/openai-responses-compat/index.ts +89 -0
- package/src/providers/openai-responses-compat/native.ts +127 -0
- package/src/providers/openai-responses-compat/strings.ts +19 -0
- package/src/providers/pricebook.ts +75 -0
- package/src/providers/registry.ts +137 -0
- package/src/providers/strings.ts +71 -0
- package/src/providers/transport/chat.ts +156 -0
- package/src/providers/transport/errors.ts +48 -0
- package/src/providers/transport/history.ts +68 -0
- package/src/providers/transport/native-input.ts +83 -0
- package/src/providers/transport/native-types.ts +13 -0
- package/src/providers/transport/response-assembly.ts +191 -0
- package/src/providers/transport/response-http.ts +196 -0
- package/src/providers/transport/response-meters.ts +27 -0
- package/src/providers/transport/responses-input.ts +56 -0
- package/src/web/README.md +84 -0
- package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
- package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
- package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
- package/src/web/client/console-pages/builtins.ts +10 -0
- package/src/web/client/console-pages/context.ts +135 -0
- package/src/web/client/console-pages/host.ts +633 -0
- package/src/web/client/console-pages/loader.ts +132 -0
- package/src/web/client/console-pages/strings.ts +90 -0
- package/src/web/client/console-pages/tools/strings.ts +49 -0
- package/src/web/client/console-pages/tools/view.ts +215 -0
- package/src/web/client/core/api.ts +200 -0
- package/src/web/client/core/language.ts +56 -0
- package/src/web/client/core/lifecycle.ts +132 -0
- package/src/web/client/core/router.ts +199 -0
- package/src/web/client/core/stream.ts +157 -0
- package/src/web/client/core/websocket.ts +82 -0
- package/src/web/client/features/appearance/index.ts +333 -0
- package/src/web/client/features/appearance/strings.ts +87 -0
- package/src/web/client/features/config/index.ts +11 -0
- package/src/web/client/features/config/strings.ts +41 -0
- package/src/web/client/features/config/view.ts +428 -0
- package/src/web/client/features/core/events.ts +197 -0
- package/src/web/client/features/core/index.ts +254 -0
- package/src/web/client/features/core/run.ts +56 -0
- package/src/web/client/features/core/runlog.ts +214 -0
- package/src/web/client/features/core/sessions.ts +53 -0
- package/src/web/client/features/core/strings.ts +157 -0
- package/src/web/client/features/extensions/index.ts +422 -0
- package/src/web/client/features/extensions/strings.ts +164 -0
- package/src/web/client/features/feature.ts +93 -0
- package/src/web/client/features/live/context.ts +280 -0
- package/src/web/client/features/live/fork.ts +156 -0
- package/src/web/client/features/live/index.ts +392 -0
- package/src/web/client/features/live/onboarding.ts +113 -0
- package/src/web/client/features/live/protocol.ts +122 -0
- package/src/web/client/features/live/sessions.ts +71 -0
- package/src/web/client/features/live/status.ts +68 -0
- package/src/web/client/features/live/strings.ts +252 -0
- package/src/web/client/features/live/timeline.ts +590 -0
- package/src/web/client/features/prompts/editor.ts +292 -0
- package/src/web/client/features/prompts/index.ts +346 -0
- package/src/web/client/features/prompts/strings.ts +136 -0
- package/src/web/client/features/prompts/view.ts +256 -0
- package/src/web/client/features/providers/index.ts +100 -0
- package/src/web/client/features/providers/strings.ts +23 -0
- package/src/web/client/features/settings/general.ts +28 -0
- package/src/web/client/features/settings/index.ts +82 -0
- package/src/web/client/features/settings/strings.ts +33 -0
- package/src/web/client/features/storage/index.ts +1 -0
- package/src/web/client/features/storage/strings.ts +45 -0
- package/src/web/client/features/storage/view.ts +154 -0
- package/src/web/client/features/usage/chart.ts +486 -0
- package/src/web/client/features/usage/index.ts +407 -0
- package/src/web/client/features/usage/labels.ts +46 -0
- package/src/web/client/features/usage/range.ts +36 -0
- package/src/web/client/features/usage/state.ts +37 -0
- package/src/web/client/features/usage/strings.ts +191 -0
- package/src/web/client/features/usage/tooltip.ts +86 -0
- package/src/web/client/features/usage/types.ts +96 -0
- package/src/web/client/features/worlds/index.ts +424 -0
- package/src/web/client/features/worlds/strings.ts +121 -0
- package/src/web/client/main.ts +273 -0
- package/src/web/client/shell/avatar.ts +233 -0
- package/src/web/client/shell/index.ts +549 -0
- package/src/web/client/shell/strings.ts +89 -0
- package/src/web/client/theme/handoff.ts +43 -0
- package/src/web/client/theme/palette.ts +109 -0
- package/src/web/client/theme/registry.ts +130 -0
- package/src/web/client/theme/storage.ts +72 -0
- package/src/web/client/theme/strings.ts +123 -0
- package/src/web/client/theme/studio.ts +366 -0
- package/src/web/client/ui/actions.ts +109 -0
- package/src/web/client/ui/data.ts +185 -0
- package/src/web/client/ui/dom.ts +23 -0
- package/src/web/client/ui/fields.ts +191 -0
- package/src/web/client/ui/format.ts +75 -0
- package/src/web/client/ui/icons.ts +183 -0
- package/src/web/client/ui/images.ts +91 -0
- package/src/web/client/ui/index.ts +89 -0
- package/src/web/client/ui/lamp.ts +125 -0
- package/src/web/client/ui/log.ts +98 -0
- package/src/web/client/ui/overlay.ts +184 -0
- package/src/web/client/ui/page.ts +8 -0
- package/src/web/client/ui/prompt-input.tsx +311 -0
- package/src/web/client/ui/sheet.ts +131 -0
- package/src/web/client/ui/strings.ts +71 -0
- package/src/web/console-pages.ts +367 -0
- package/src/web/files.ts +107 -0
- package/src/web/path-picker.ts +296 -0
- package/src/web/public/index.html +17 -0
- package/src/web/public/styles.css +1715 -0
- package/src/web/server.ts +1883 -0
- package/src/web/shared/client-panel.ts +603 -0
- package/src/web/shared/console-protocol.ts +560 -0
- package/src/web/shared/css.d.ts +1 -0
- package/src/web/shared/path-picker.ts +23 -0
- package/src/web/shared/theme.ts +256 -0
- package/src/web/theme-store.ts +37 -0
- package/src/world.ts +449 -0
- package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
- package/src/worlds/bilibili/README.md +176 -0
- package/src/worlds/bilibili/audience-admission.ts +995 -0
- package/src/worlds/bilibili/client.ts +528 -0
- package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
- package/src/worlds/bilibili/config.ts +185 -0
- package/src/worlds/bilibili/console/client.ts +202 -0
- package/src/worlds/bilibili/definition.ts +35 -0
- package/src/worlds/bilibili/gift-frame.ts +131 -0
- package/src/worlds/bilibili/normalize.ts +452 -0
- package/src/worlds/bilibili/overlay/announcement.ts +76 -0
- package/src/worlds/bilibili/overlay/assets.ts +82 -0
- package/src/worlds/bilibili/overlay/model.ts +494 -0
- package/src/worlds/bilibili/overlay/project.ts +202 -0
- package/src/worlds/bilibili/overlay/server.ts +286 -0
- package/src/worlds/bilibili/overlay/types.ts +217 -0
- package/src/worlds/bilibili/overlay/web/app.js +690 -0
- package/src/worlds/bilibili/overlay/web/editor.css +297 -0
- package/src/worlds/bilibili/overlay/web/editor.html +102 -0
- package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
- package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
- package/src/worlds/bilibili/overlay/web/styles.css +258 -0
- package/src/worlds/bilibili/protobuf.ts +123 -0
- package/src/worlds/bilibili/wire.ts +94 -0
- package/src/worlds/bilibili/world.ts +1389 -0
- package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
- package/src/worlds/console-fixture/console/client.ts +72 -0
- package/src/worlds/console-fixture/world.ts +73 -0
- package/src/worlds/index.ts +15 -0
- package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
- package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
- package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
- package/src/worlds/minecraft/README.md +917 -0
- package/src/worlds/minecraft/blueprint-plan.ts +915 -0
- package/src/worlds/minecraft/blueprint-registry.ts +566 -0
- package/src/worlds/minecraft/blueprint-repair.ts +236 -0
- package/src/worlds/minecraft/blueprint-resource.ts +272 -0
- package/src/worlds/minecraft/blueprint.ts +492 -0
- package/src/worlds/minecraft/body-lease.ts +340 -0
- package/src/worlds/minecraft/bridge.ts +749 -0
- package/src/worlds/minecraft/check.ts +651 -0
- package/src/worlds/minecraft/chests.ts +292 -0
- package/src/worlds/minecraft/client-launch.ts +272 -0
- package/src/worlds/minecraft/client-options.ts +85 -0
- package/src/worlds/minecraft/client-skins.ts +164 -0
- package/src/worlds/minecraft/client-window.ps1 +91 -0
- package/src/worlds/minecraft/client.ts +423 -0
- package/src/worlds/minecraft/combat-context.ts +109 -0
- package/src/worlds/minecraft/combat.ts +1552 -0
- package/src/worlds/minecraft/config.ts +499 -0
- package/src/worlds/minecraft/console/access.ts +218 -0
- package/src/worlds/minecraft/console/client.ts +244 -0
- package/src/worlds/minecraft/console/log.ts +110 -0
- package/src/worlds/minecraft/console/mount.ts +420 -0
- package/src/worlds/minecraft/console/skin.ts +257 -0
- package/src/worlds/minecraft/console/style.css +154 -0
- package/src/worlds/minecraft/console/world.ts +449 -0
- package/src/worlds/minecraft/deaths.ts +135 -0
- package/src/worlds/minecraft/definition.ts +18 -0
- package/src/worlds/minecraft/engine-child.ts +274 -0
- package/src/worlds/minecraft/engine-ipc.ts +98 -0
- package/src/worlds/minecraft/entity-facts.ts +132 -0
- package/src/worlds/minecraft/escape.ts +407 -0
- package/src/worlds/minecraft/executor.ts +14494 -0
- package/src/worlds/minecraft/explored.ts +156 -0
- package/src/worlds/minecraft/geometry.ts +205 -0
- package/src/worlds/minecraft/goal-plan.ts +647 -0
- package/src/worlds/minecraft/item-break.ts +126 -0
- package/src/worlds/minecraft/item-facts.ts +146 -0
- package/src/worlds/minecraft/item-pick.ts +67 -0
- package/src/worlds/minecraft/level-dat.ts +176 -0
- package/src/worlds/minecraft/log.ts +132 -0
- package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
- package/src/worlds/minecraft/names.ts +377 -0
- package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
- package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
- package/src/worlds/minecraft/piglin.ts +49 -0
- package/src/worlds/minecraft/policy.ts +547 -0
- package/src/worlds/minecraft/precheck.ts +655 -0
- package/src/worlds/minecraft/proxy.ts +445 -0
- package/src/worlds/minecraft/ranged.ts +635 -0
- package/src/worlds/minecraft/readouts.ts +102 -0
- package/src/worlds/minecraft/round.ts +26 -0
- package/src/worlds/minecraft/search-observation.ts +92 -0
- package/src/worlds/minecraft/server-config.ts +467 -0
- package/src/worlds/minecraft/server.ts +658 -0
- package/src/worlds/minecraft/show.ts +84 -0
- package/src/worlds/minecraft/skills.ts +1763 -0
- package/src/worlds/minecraft/terrain.ts +1735 -0
- package/src/worlds/minecraft/window.ts +96 -0
- package/src/worlds/minecraft/works.ts +179 -0
- package/src/worlds/minecraft/world.ts +6438 -0
- package/src/worlds/qq/ENV_PROMPT.md +5 -0
- package/src/worlds/qq/config.ts +108 -0
- package/src/worlds/qq/console/client.ts +126 -0
- package/src/worlds/qq/console/events.ts +155 -0
- package/src/worlds/qq/console/gate.ts +232 -0
- package/src/worlds/qq/console/roster.ts +203 -0
- package/src/worlds/qq/console/style.css +65 -0
- package/src/worlds/qq/conversation.ts +28 -0
- package/src/worlds/qq/definition.ts +62 -0
- package/src/worlds/qq/driver.ts +375 -0
- package/src/worlds/qq/history-tools.ts +209 -0
- package/src/worlds/qq/normalize.ts +323 -0
- package/src/worlds/qq/qface-map.ts +282 -0
- package/src/worlds/qq/vision-prompt.ts +27 -0
- package/src/worlds/qq/vision.ts +642 -0
- package/src/worlds/qq/vlm.ts +142 -0
- package/src/worlds/qq/world.ts +1503 -0
- package/src/worlds/terminal/ENV_PROMPT.md +11 -0
- package/src/worlds/terminal/config.ts +9 -0
- package/src/worlds/terminal/definition.ts +14 -0
- package/src/worlds/terminal/world.ts +700 -0
- package/src/worlds/websearch/ENV_PROMPT.md +1 -0
- package/src/worlds/websearch/brave-client.ts +261 -0
- package/src/worlds/websearch/config.ts +75 -0
- package/src/worlds/websearch/definition.ts +20 -0
- package/src/worlds/websearch/world.ts +171 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { pick, type Language } from '../../core/language.ts';
|
|
2
|
+
|
|
3
|
+
/** Server side: ConfigGroup titles, validation errors, runtime and server state details. */
|
|
4
|
+
const zh = {
|
|
5
|
+
tierOff: '关闭',
|
|
6
|
+
tierOn: '开启',
|
|
7
|
+
releaseRequired: '版本 tag 不能为空',
|
|
8
|
+
backendUnsupported: (backend: string) => `本机平台没有后端 ${backend} 的官方构建`,
|
|
9
|
+
runtimeDirString: '自备运行时目录必须是字符串',
|
|
10
|
+
positiveInteger: (field: string) => `${field} 必须为正整数`,
|
|
11
|
+
nonNegativeInteger: (field: string) => `${field} 必须为非负整数`,
|
|
12
|
+
extraArgsString: '附加启动参数必须是字符串',
|
|
13
|
+
autoStartBoolean: '自动启动必须是布尔值',
|
|
14
|
+
notManaged: '这条端点没有开启托管',
|
|
15
|
+
runtimeNotInstalled: '运行时还没装好',
|
|
16
|
+
runtimeUnsupported: '本机平台没有这个后端的官方构建,填自备运行时目录',
|
|
17
|
+
runtimePanel: '运行时',
|
|
18
|
+
runtimePanelDescription: 'llama.cpp 官方二进制的下载、安装与 llama-server 进程的启停。',
|
|
19
|
+
modelsPanel: '模型',
|
|
20
|
+
modelsPanelDescription: 'llama-server 认得的模型:缓存里的、本机目录里的,以及正在下载的。',
|
|
21
|
+
unknownPanel: '未知面板',
|
|
22
|
+
unknownMethod: '未知操作',
|
|
23
|
+
instanceNameRequired: '需要端点名',
|
|
24
|
+
modelIdRequired: '需要模型 id',
|
|
25
|
+
bodyRequired: '需要请求体',
|
|
26
|
+
serverUnreachable: "llama-server 不可达;请检查端点地址与服务状态。",
|
|
27
|
+
externalServer: '端点已有服务在跑(外部启动),不接管',
|
|
28
|
+
starting: 'llama-server 启动中',
|
|
29
|
+
spawnFailed: (message: string) => `进程启动失败: ${message}`,
|
|
30
|
+
spawnBlocked: (exe: string) =>
|
|
31
|
+
`Windows 启动 ${exe} 失败(UNKNOWN)。` +
|
|
32
|
+
'启动失败时可在 Windows 安全中心核对拦截记录;若确认被拦截,请使用符合信任要求的运行时。',
|
|
33
|
+
exited: (code: number | null, tail: string) => `进程退出 code=${code};stderr 尾部: ${tail}`,
|
|
34
|
+
badBaseUrl: (baseUrl: string) => `baseUrl 无法解析出 host/port: ${baseUrl}`,
|
|
35
|
+
missingBinary: (exe: string) => `运行时目录里没有 ${exe}`,
|
|
36
|
+
healthTimeout: "/health 检查超时;请检查端点地址与服务状态。",
|
|
37
|
+
downloadFailed: (file: string, message: string) => `下载 ${file} 失败: ${message}`,
|
|
38
|
+
extractFailed: (file: string, message: string) => `解压 ${file} 失败: ${message}`,
|
|
39
|
+
installBusy: '正在安装另一份运行时',
|
|
40
|
+
};
|
|
41
|
+
const en: typeof zh = {
|
|
42
|
+
tierOff: 'Off',
|
|
43
|
+
tierOn: 'On',
|
|
44
|
+
releaseRequired: 'The release tag cannot be empty',
|
|
45
|
+
backendUnsupported: (backend: string) => `No official build of backend ${backend} for this platform`,
|
|
46
|
+
runtimeDirString: 'The own runtime directory must be a string',
|
|
47
|
+
positiveInteger: (field: string) => `${field} must be a positive integer`,
|
|
48
|
+
nonNegativeInteger: (field: string) => `${field} must be a non-negative integer`,
|
|
49
|
+
extraArgsString: 'Extra launch arguments must be a string',
|
|
50
|
+
autoStartBoolean: 'Auto start must be a boolean',
|
|
51
|
+
notManaged: 'This endpoint is not managed',
|
|
52
|
+
runtimeNotInstalled: 'The runtime is not installed yet',
|
|
53
|
+
runtimeUnsupported: 'No official build for this backend on this platform; set an own runtime directory',
|
|
54
|
+
runtimePanel: 'Runtime',
|
|
55
|
+
runtimePanelDescription: 'Download and install the official llama.cpp binaries; start and stop the llama-server process.',
|
|
56
|
+
modelsPanel: 'Models',
|
|
57
|
+
modelsPanelDescription: 'Models llama-server knows: cached, in the local directory, and downloading.',
|
|
58
|
+
unknownPanel: 'Unknown panel',
|
|
59
|
+
unknownMethod: 'Unknown action',
|
|
60
|
+
instanceNameRequired: 'An endpoint name is required',
|
|
61
|
+
modelIdRequired: 'A model id is required',
|
|
62
|
+
bodyRequired: 'A request body is required',
|
|
63
|
+
serverUnreachable: "llama-server is unreachable; check the endpoint URL and server status.",
|
|
64
|
+
externalServer: 'A server is already running at the endpoint (started externally); not taking over',
|
|
65
|
+
starting: 'llama-server starting',
|
|
66
|
+
spawnFailed: (message: string) => `Process failed to start: ${message}`,
|
|
67
|
+
spawnBlocked: (exe: string) =>
|
|
68
|
+
`Windows failed to start ${exe} (UNKNOWN). ` +
|
|
69
|
+
'If startup fails, check Windows Security for a block record. If a block is confirmed, use a runtime that meets the trust requirements.',
|
|
70
|
+
exited: (code: number | null, tail: string) => `Process exited code=${code}; stderr tail: ${tail}`,
|
|
71
|
+
badBaseUrl: (baseUrl: string) => `Cannot derive host/port from baseUrl: ${baseUrl}`,
|
|
72
|
+
missingBinary: (exe: string) => `No ${exe} in the runtime directory`,
|
|
73
|
+
healthTimeout: "The /health check timed out; check the endpoint URL and server status.",
|
|
74
|
+
downloadFailed: (file: string, message: string) => `Download of ${file} failed: ${message}`,
|
|
75
|
+
extractFailed: (file: string, message: string) => `Extraction of ${file} failed: ${message}`,
|
|
76
|
+
installBusy: 'Another runtime is being installed',
|
|
77
|
+
};
|
|
78
|
+
export type Text = typeof zh;
|
|
79
|
+
export const text = (language: Language) => pick(language, { zh, en });
|
|
80
|
+
|
|
81
|
+
/** Browser side: the two panels pick by `ctx.language`. */
|
|
82
|
+
const panelZh = {
|
|
83
|
+
runtimeTitle: '运行时',
|
|
84
|
+
modelsTitle: '模型',
|
|
85
|
+
managedOff: "此端点由外部管理;开启托管后由 Cortico 管理 llama-server 进程。",
|
|
86
|
+
enable: '开启托管',
|
|
87
|
+
disable: '关闭托管',
|
|
88
|
+
release: '版本',
|
|
89
|
+
backend: '后端',
|
|
90
|
+
runtimeDir: '运行时目录',
|
|
91
|
+
ownRuntime: '自备',
|
|
92
|
+
installStatus: '安装',
|
|
93
|
+
installed: '已安装',
|
|
94
|
+
absent: '未安装',
|
|
95
|
+
downloading: '下载中',
|
|
96
|
+
extracting: '解压中',
|
|
97
|
+
install: '下载并安装',
|
|
98
|
+
reinstall: '重新安装',
|
|
99
|
+
sacWarning:
|
|
100
|
+
"检测到 Windows 智能应用控制处于强制模式。" +
|
|
101
|
+
'启动失败时可在 Windows 安全中心核对拦截记录;若确认被拦截,请使用符合信任要求的运行时。',
|
|
102
|
+
server: '服务进程',
|
|
103
|
+
serverSection: '服务进程与端点',
|
|
104
|
+
serverSectionDesc: '',
|
|
105
|
+
runtimeSection: '二进制运行环境',
|
|
106
|
+
runtimeSectionDesc: '',
|
|
107
|
+
releaseHint: '上游 release tag,如 b10930;换了要重新下载。',
|
|
108
|
+
backendHint: '按本机平台给出的官方构建;CUDA 版自动搭配上游的 cudart 包。',
|
|
109
|
+
runtimeDirHint: '填了就不下载:目录里要有 llama-server 及其动态库(自编译、Linux CUDA、签过名的构建)。',
|
|
110
|
+
browse: '选择目录',
|
|
111
|
+
launchSection: '启动参数',
|
|
112
|
+
launchSectionDesc: '在下一次启动 llama-server 时生效。',
|
|
113
|
+
contextSize: '上下文长度(-c)',
|
|
114
|
+
nGpuLayers: 'GPU 层数(-ngl)',
|
|
115
|
+
parallel: '并发槽位(--parallel)',
|
|
116
|
+
extraArgs: '附加启动参数',
|
|
117
|
+
extraArgsHint: '按空格拆开追加到 llama-server 命令行。',
|
|
118
|
+
autoStart: 'bot 启动时一并起 server',
|
|
119
|
+
copyEndpoint: '复制端点',
|
|
120
|
+
installRequired: '请先下载并安装运行时二进制,方可启动服务',
|
|
121
|
+
status: '状态',
|
|
122
|
+
endpoint: '端点',
|
|
123
|
+
reachable: '可达',
|
|
124
|
+
unreachable: '不可达',
|
|
125
|
+
pid: '进程 PID',
|
|
126
|
+
pendingNote: '启动参数已修改;停止并重新启动后生效。',
|
|
127
|
+
start: '启动',
|
|
128
|
+
stop: '停止',
|
|
129
|
+
refresh: '刷新',
|
|
130
|
+
phase: { stopped: '未运行', starting: '启动中', running: '运行中', error: '出错' } as Record<string, string>,
|
|
131
|
+
modelId: '模型 ID',
|
|
132
|
+
modelStatus: '状态',
|
|
133
|
+
modality: '输入',
|
|
134
|
+
path: '文件',
|
|
135
|
+
actions: '操作',
|
|
136
|
+
load: '加载',
|
|
137
|
+
unload: '卸载',
|
|
138
|
+
cancel: '取消',
|
|
139
|
+
reload: '重扫',
|
|
140
|
+
pull: '拉取',
|
|
141
|
+
pullPlaceholder: 'HuggingFace 仓库,如 ggml-org/Qwen2.5-Coder-7B-Instruct-GGUF',
|
|
142
|
+
localDir: '本机 GGUF 目录',
|
|
143
|
+
cacheDir: '缓存目录',
|
|
144
|
+
noModels: '暂无 GGUF 模型。可下载模型,或将 .gguf 文件放入本机目录后重新扫描。',
|
|
145
|
+
modelStatusLabel: {
|
|
146
|
+
loaded: '已加载',
|
|
147
|
+
loading: '加载中',
|
|
148
|
+
unloaded: '未加载',
|
|
149
|
+
downloading: '下载中',
|
|
150
|
+
sleeping: '休眠',
|
|
151
|
+
failed: '加载失败',
|
|
152
|
+
unknown: '在线',
|
|
153
|
+
} as Record<string, string>,
|
|
154
|
+
serverDown: "llama-server 不可达;请检查端点地址与服务状态。",
|
|
155
|
+
};
|
|
156
|
+
const panelEn: typeof panelZh = {
|
|
157
|
+
runtimeTitle: 'Runtime',
|
|
158
|
+
modelsTitle: 'Models',
|
|
159
|
+
managedOff: "This endpoint is externally managed. Enable hosting to manage its llama-server process through Cortico.",
|
|
160
|
+
enable: 'Enable hosting',
|
|
161
|
+
disable: 'Disable hosting',
|
|
162
|
+
release: 'Release',
|
|
163
|
+
backend: 'Backend',
|
|
164
|
+
runtimeDir: 'Runtime directory',
|
|
165
|
+
ownRuntime: 'own',
|
|
166
|
+
installStatus: 'Install',
|
|
167
|
+
installed: 'installed',
|
|
168
|
+
absent: 'not installed',
|
|
169
|
+
downloading: 'downloading',
|
|
170
|
+
extracting: 'extracting',
|
|
171
|
+
install: 'Download and install',
|
|
172
|
+
reinstall: 'Reinstall',
|
|
173
|
+
sacWarning:
|
|
174
|
+
"Windows Smart App Control is in enforced mode. " +
|
|
175
|
+
'If startup fails, check Windows Security for a block record. If a block is confirmed, use a runtime that meets the trust requirements.',
|
|
176
|
+
server: 'Server',
|
|
177
|
+
serverSection: 'Server Process & Endpoint',
|
|
178
|
+
serverSectionDesc: '',
|
|
179
|
+
runtimeSection: 'Binary Runtime Environment',
|
|
180
|
+
runtimeSectionDesc: '',
|
|
181
|
+
releaseHint: 'Upstream release tag, such as b10930; changing it downloads another build.',
|
|
182
|
+
backendHint: 'Official builds for this platform; CUDA builds are paired with the upstream cudart package.',
|
|
183
|
+
runtimeDirHint: 'When set nothing is downloaded: the directory holds llama-server and its shared libraries (self-built, Linux CUDA, signed builds).',
|
|
184
|
+
browse: 'Choose directory',
|
|
185
|
+
launchSection: 'Launch parameters',
|
|
186
|
+
launchSectionDesc: 'Applied on the next llama-server start.',
|
|
187
|
+
contextSize: 'Context size (-c)',
|
|
188
|
+
nGpuLayers: 'GPU layers (-ngl)',
|
|
189
|
+
parallel: 'Parallel slots (--parallel)',
|
|
190
|
+
extraArgs: 'Extra launch arguments',
|
|
191
|
+
extraArgsHint: 'Split on whitespace and appended to the llama-server command line.',
|
|
192
|
+
autoStart: 'Start the server with the bot',
|
|
193
|
+
copyEndpoint: 'Copy endpoint',
|
|
194
|
+
installRequired: 'Install the runtime binary before starting the server',
|
|
195
|
+
status: 'Status',
|
|
196
|
+
endpoint: 'Endpoint',
|
|
197
|
+
reachable: 'reachable',
|
|
198
|
+
unreachable: 'unreachable',
|
|
199
|
+
pid: 'Process PID',
|
|
200
|
+
pendingNote: 'Launch parameters changed; stop and start again to apply them.',
|
|
201
|
+
start: 'Start',
|
|
202
|
+
stop: 'Stop',
|
|
203
|
+
refresh: 'Refresh',
|
|
204
|
+
phase: { stopped: 'stopped', starting: 'starting', running: 'running', error: 'error' } as Record<string, string>,
|
|
205
|
+
modelId: 'Model ID',
|
|
206
|
+
modelStatus: 'Status',
|
|
207
|
+
modality: 'Input',
|
|
208
|
+
path: 'File',
|
|
209
|
+
actions: 'Actions',
|
|
210
|
+
load: 'Load',
|
|
211
|
+
unload: 'Unload',
|
|
212
|
+
cancel: 'Cancel',
|
|
213
|
+
reload: 'Rescan',
|
|
214
|
+
pull: 'Pull',
|
|
215
|
+
pullPlaceholder: 'HuggingFace repo, e.g. ggml-org/Qwen2.5-Coder-7B-Instruct-GGUF',
|
|
216
|
+
localDir: 'Local GGUF directory',
|
|
217
|
+
cacheDir: 'Cache directory',
|
|
218
|
+
noModels: 'No GGUF models. Download a model, or place GGUF files in the local directory and rescan.',
|
|
219
|
+
modelStatusLabel: {
|
|
220
|
+
loaded: 'loaded',
|
|
221
|
+
loading: 'loading',
|
|
222
|
+
unloaded: 'unloaded',
|
|
223
|
+
downloading: 'downloading',
|
|
224
|
+
sleeping: 'sleeping',
|
|
225
|
+
failed: 'load failed',
|
|
226
|
+
unknown: 'online',
|
|
227
|
+
} as Record<string, string>,
|
|
228
|
+
serverDown: "llama-server is unreachable; check the endpoint URL and server status.",
|
|
229
|
+
};
|
|
230
|
+
export const panel = { zh: panelZh, en: panelEn };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ConfigGroup } from '../../core/config-schema.ts';
|
|
2
|
+
import type { LLMProviderEntry } from '../../core/types.ts';
|
|
3
|
+
import type { Language } from '../../core/language.ts';
|
|
4
|
+
import { text } from './strings.ts';
|
|
5
|
+
|
|
6
|
+
export function protocolConfig(name: string, entry: LLMProviderEntry, language: Language): ConfigGroup[] {
|
|
7
|
+
const S = text(language);
|
|
8
|
+
return [{
|
|
9
|
+
id: `llm.${entry.kind}.${name}.protocol`,
|
|
10
|
+
owner: `provider:${entry.kind}`,
|
|
11
|
+
schema: {
|
|
12
|
+
type: 'object', title: name,
|
|
13
|
+
properties: {
|
|
14
|
+
[`providers.${name}.options.endpointPath`]: {
|
|
15
|
+
type: 'string', title: S.endpointPath, description: S.endpointPathDescription, 'x-hot': true,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
}];
|
|
20
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ProviderModule } from '../base.ts';
|
|
2
|
+
import type { LLMProviderEntry } from '../../core/types.ts';
|
|
3
|
+
import { isContextOverflow } from '../transport/errors.ts';
|
|
4
|
+
import { ModelCatalog, ResponsesProvider } from './native.ts';
|
|
5
|
+
import { text } from './strings.ts';
|
|
6
|
+
import { protocolConfig } from './config.ts';
|
|
7
|
+
|
|
8
|
+
/** Suggested base URLs for the console field; operators may enter other URLs. */
|
|
9
|
+
const BASE_URLS: readonly string[] = [
|
|
10
|
+
'https://api.openai.com/v1',
|
|
11
|
+
'https://api.deepseek.com',
|
|
12
|
+
'https://openrouter.ai/api/v1',
|
|
13
|
+
'https://api.x.ai/v1',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/** Reasoning-effort choices used by the console. */
|
|
17
|
+
const EFFORTS: readonly string[] = ['none', 'low', 'medium', 'high', 'xhigh'];
|
|
18
|
+
|
|
19
|
+
export interface CompatOptions {
|
|
20
|
+
endpointPath?: string;
|
|
21
|
+
extraHeaders?: Record<string, string>;
|
|
22
|
+
extraBody?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export function compatOptions(entry: LLMProviderEntry): CompatOptions {
|
|
25
|
+
return (entry.options ?? {}) as CompatOptions;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Empty strings and empty objects are the console's "unset"; they do not reach the wire. */
|
|
29
|
+
function normalizeCompat(entry: LLMProviderEntry): LLMProviderEntry {
|
|
30
|
+
const options: Record<string, unknown> = { ...entry.options };
|
|
31
|
+
if (options.endpointPath === '') delete options.endpointPath;
|
|
32
|
+
for (const key of ['extraHeaders', 'extraBody'] as const) {
|
|
33
|
+
const value = options[key];
|
|
34
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) delete options[key];
|
|
35
|
+
}
|
|
36
|
+
return { ...entry, options };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
40
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default {
|
|
44
|
+
id: 'openai-responses-compat',
|
|
45
|
+
title: 'OpenAI Responses Compatible',
|
|
46
|
+
defaultBaseUrl: BASE_URLS[0],
|
|
47
|
+
baseUrlSuggestions: BASE_URLS,
|
|
48
|
+
normalize: normalizeCompat,
|
|
49
|
+
config: protocolConfig,
|
|
50
|
+
console: () => ({ config: [] }),
|
|
51
|
+
reasoningTiers: [],
|
|
52
|
+
effortSuggestions: EFFORTS,
|
|
53
|
+
serviceTiers: [],
|
|
54
|
+
validateEntry: (entry, language) => {
|
|
55
|
+
const S = text(language);
|
|
56
|
+
const options = compatOptions(entry);
|
|
57
|
+
if (options.endpointPath !== undefined && (typeof options.endpointPath !== 'string' || !options.endpointPath.startsWith('/')))
|
|
58
|
+
throw new Error(S.endpointPathSlash);
|
|
59
|
+
if (options.extraHeaders !== undefined && (!isPlainObject(options.extraHeaders)
|
|
60
|
+
|| Object.values(options.extraHeaders).some((value) => typeof value !== 'string')))
|
|
61
|
+
throw new Error(S.extraHeadersObject);
|
|
62
|
+
if (options.extraBody !== undefined && !isPlainObject(options.extraBody)) throw new Error(S.extraBodyObject);
|
|
63
|
+
},
|
|
64
|
+
contextOverflow: isContextOverflow,
|
|
65
|
+
create(name, entry, host) {
|
|
66
|
+
const options = compatOptions(entry);
|
|
67
|
+
const apiKey = entry.secret ? host.secret(entry.secret) : undefined;
|
|
68
|
+
const headers = (): Record<string, string> => ({
|
|
69
|
+
...options.extraHeaders,
|
|
70
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
71
|
+
});
|
|
72
|
+
const catalog = new ModelCatalog(() => ({ baseUrl: entry.baseUrl, headers: headers() }));
|
|
73
|
+
return {
|
|
74
|
+
listModels: () => catalog.list(),
|
|
75
|
+
contextWindow: (model) => catalog.contextWindow(model),
|
|
76
|
+
compatibilityKey: () => [options.endpointPath ?? '/responses', name],
|
|
77
|
+
client: new ResponsesProvider({
|
|
78
|
+
baseUrl: entry.baseUrl,
|
|
79
|
+
apiKey,
|
|
80
|
+
endpointPath: options.endpointPath,
|
|
81
|
+
extraHeaders: options.extraHeaders,
|
|
82
|
+
extraBody: options.extraBody,
|
|
83
|
+
log: host.log,
|
|
84
|
+
media: { enabled: () => entry.multimodal === true, read: host.readBlob },
|
|
85
|
+
keepThinking: host.keepThinking,
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
} satisfies ProviderModule;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/** Native Responses over any OpenAI-compatible endpoint: bearer key, stateless replay, no vendor branching. */
|
|
2
|
+
import type { Logger } from '../../core/types.ts';
|
|
3
|
+
import { nullLogger } from '../../core/util.ts';
|
|
4
|
+
import { OpenAIHttpClient } from '../transport/chat.ts';
|
|
5
|
+
import { ResponseProtocolError } from '../../protocol/open-responses/stream.ts';
|
|
6
|
+
import { createResponse, type Request } from '../../protocol/open-responses/index.ts';
|
|
7
|
+
import { standardUsage, type GenerateOptions } from '../../core/generation.ts';
|
|
8
|
+
import { NativeResponseAssembly, type ResponseAssembly, type parseChatResponse } from '../transport/response-assembly.ts';
|
|
9
|
+
import { responseMeters } from '../transport/response-meters.ts';
|
|
10
|
+
import { responsesInput, type ResponsesInputOptions } from '../transport/responses-input.ts';
|
|
11
|
+
|
|
12
|
+
type Item = Record<string, unknown>;
|
|
13
|
+
|
|
14
|
+
export interface ResponsesProviderOptions {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
/** Path appended to `baseUrl`; default `/responses`. */
|
|
18
|
+
endpointPath?: string;
|
|
19
|
+
extraHeaders?: Record<string, string>;
|
|
20
|
+
/** Merged into every request body last, so vendor-specific fields (`service_tier`, ...) win. */
|
|
21
|
+
extraBody?: Record<string, unknown>;
|
|
22
|
+
media?: ResponsesInputOptions['media'];
|
|
23
|
+
keepThinking?: () => boolean;
|
|
24
|
+
log?: Logger;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Send reasoning only when an effort is supplied; otherwise leave it to the endpoint.
|
|
29
|
+
* Requests set store=false and request encrypted reasoning content for local stateless replay.
|
|
30
|
+
* Endpoint extraBody fields are merged last.
|
|
31
|
+
*/
|
|
32
|
+
export function buildResponsesBody(
|
|
33
|
+
request: Request,
|
|
34
|
+
options: GenerateOptions,
|
|
35
|
+
input: ResponsesInputOptions,
|
|
36
|
+
extraBody: Record<string, unknown> = {},
|
|
37
|
+
): Record<string, unknown> {
|
|
38
|
+
const replay = responsesInput(request, options, input);
|
|
39
|
+
const body: Item = {
|
|
40
|
+
...request,
|
|
41
|
+
input: replay.input,
|
|
42
|
+
include: [...new Set([...(request.include ?? []), 'reasoning.encrypted_content'])],
|
|
43
|
+
store: false,
|
|
44
|
+
stream: Boolean(options.onEvent),
|
|
45
|
+
};
|
|
46
|
+
delete body.previous_response_id;
|
|
47
|
+
if (replay.instructions !== undefined) body.instructions = replay.instructions;
|
|
48
|
+
else delete body.instructions;
|
|
49
|
+
if (!request.reasoning?.effort) delete body.reasoning;
|
|
50
|
+
if (options.sessionId) body.prompt_cache_key = options.sessionId;
|
|
51
|
+
return { ...body, ...extraBody };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class ResponsesProvider extends OpenAIHttpClient {
|
|
55
|
+
private readonly opts: ResponsesProviderOptions;
|
|
56
|
+
|
|
57
|
+
constructor(opts: ResponsesProviderOptions) {
|
|
58
|
+
super(opts.baseUrl, opts.log ?? nullLogger());
|
|
59
|
+
this.opts = opts;
|
|
60
|
+
this.chatPath = opts.endpointPath ?? '/responses';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected override buildResponseBody(request: Request, options: GenerateOptions): Record<string, unknown> {
|
|
64
|
+
return buildResponsesBody(request, options, { media: this.opts.media, keepThinking: this.opts.keepThinking }, this.opts.extraBody);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The Chat body builder is unreachable here: `buildResponseBody` is overridden whole. */
|
|
68
|
+
protected buildBody(): never {
|
|
69
|
+
throw new Error('ResponsesProvider does not build Chat Completions bodies');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected headers(): Record<string, string> {
|
|
73
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json', ...this.opts.extraHeaders };
|
|
74
|
+
if (this.opts.apiKey) headers.Authorization = `Bearer ${this.opts.apiKey}`;
|
|
75
|
+
return headers;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected override responseAssembly(): ResponseAssembly {
|
|
79
|
+
return new NativeResponseAssembly();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected override parseResponse(raw: unknown, request: Request): ReturnType<typeof parseChatResponse> {
|
|
83
|
+
const data = raw as Item;
|
|
84
|
+
if (typeof data.id !== 'string' || typeof data.model !== 'string' || !Array.isArray(data.output)
|
|
85
|
+
|| !['completed', 'incomplete', 'failed'].includes(String(data.status))) throw new ResponseProtocolError('Invalid native Responses resource');
|
|
86
|
+
const meters = responseMeters(data.usage as Record<string, unknown> | null);
|
|
87
|
+
return {
|
|
88
|
+
response: { ...createResponse(data.id, request), ...data, usage: standardUsage(meters) },
|
|
89
|
+
meters,
|
|
90
|
+
serviceTier: typeof data.service_tier === 'string' ? data.service_tier : null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface CatalogModel {
|
|
96
|
+
id: string;
|
|
97
|
+
contextWindow?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** `GET /models`: ids always; OpenRouter also states `context_length` per model. */
|
|
101
|
+
export class ModelCatalog {
|
|
102
|
+
private known = new Map<string, number | undefined>();
|
|
103
|
+
constructor(
|
|
104
|
+
private readonly endpoint: () => { baseUrl: string; headers: Record<string, string> },
|
|
105
|
+
private readonly fetchImpl: typeof fetch = fetch,
|
|
106
|
+
) {}
|
|
107
|
+
|
|
108
|
+
async list(): Promise<CatalogModel[]> {
|
|
109
|
+
const { baseUrl, headers } = this.endpoint();
|
|
110
|
+
const res = await this.fetchImpl(`${baseUrl.replace(/\/+$/, '')}/models`, { headers, signal: AbortSignal.timeout(15_000) });
|
|
111
|
+
if (!res.ok) throw new Error(`GET /models ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
112
|
+
const json = (await res.json()) as { data?: Array<{ id?: unknown; context_length?: unknown }> };
|
|
113
|
+
if (!Array.isArray(json.data)) throw new Error('GET /models returned no data array');
|
|
114
|
+
const models: CatalogModel[] = [];
|
|
115
|
+
for (const row of json.data) {
|
|
116
|
+
if (typeof row.id !== 'string') continue;
|
|
117
|
+
const window = typeof row.context_length === 'number' && Number.isInteger(row.context_length) && row.context_length > 0 ? row.context_length : undefined;
|
|
118
|
+
this.known.set(row.id, window);
|
|
119
|
+
models.push(window ? { id: row.id, contextWindow: window } : { id: row.id });
|
|
120
|
+
}
|
|
121
|
+
return models.sort((a, b) => a.id.localeCompare(b.id));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
contextWindow(model: string): number | undefined {
|
|
125
|
+
return this.known.get(model);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { pick, type Language } from '../../core/language.ts';
|
|
2
|
+
|
|
3
|
+
/** Server side: ConfigGroup titles and validation errors. */
|
|
4
|
+
const zh = {
|
|
5
|
+
endpointPath: 'Responses 端点路径',
|
|
6
|
+
endpointPathDescription: '相对供应地址;默认 /responses。',
|
|
7
|
+
endpointPathSlash: '端点路径必须以 / 开头',
|
|
8
|
+
extraHeadersObject: '附加请求头必须是字符串到字符串的对象',
|
|
9
|
+
extraBodyObject: '附加请求体字段必须是对象',
|
|
10
|
+
};
|
|
11
|
+
const en: typeof zh = {
|
|
12
|
+
endpointPath: 'Responses endpoint path',
|
|
13
|
+
endpointPathDescription: 'Relative to the provider URL; default /responses.',
|
|
14
|
+
endpointPathSlash: 'The endpoint path must start with /',
|
|
15
|
+
extraHeadersObject: 'Extra headers must be an object of strings',
|
|
16
|
+
extraBodyObject: 'Extra body fields must be an object',
|
|
17
|
+
};
|
|
18
|
+
export type Text = typeof zh;
|
|
19
|
+
export const text = (language: Language) => pick(language, { zh, en });
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type { Request } from '../protocol/open-responses/index.ts';
|
|
3
|
+
import type { LLMProviderEntry } from '../core/types.ts';
|
|
4
|
+
import type { Language } from '../core/language.ts';
|
|
5
|
+
import type { PriceSnapshot, PriceRule } from '../core/generation.ts';
|
|
6
|
+
import { text } from './strings.ts';
|
|
7
|
+
|
|
8
|
+
export interface PriceDefinition {
|
|
9
|
+
models: string[];
|
|
10
|
+
currency: string;
|
|
11
|
+
basis: 'marginal' | 'equivalent';
|
|
12
|
+
rules: PriceRule[];
|
|
13
|
+
inputBands?: Array<{ from: number; rules: PriceRule[] }>;
|
|
14
|
+
serviceTiers?: Record<string, { rules: PriceRule[]; inputBands?: Array<{ from: number; rules: PriceRule[] }> }>;
|
|
15
|
+
source: string;
|
|
16
|
+
}
|
|
17
|
+
export interface QuoteTime { startedAt: string; requestedServiceTier: string | null; }
|
|
18
|
+
|
|
19
|
+
export function snapshotPrice(definition: PriceDefinition, at: QuoteTime): PriceSnapshot {
|
|
20
|
+
const { models: _models, ...definitionBody } = definition;
|
|
21
|
+
return { ...structuredClone(definitionBody), id: createHash('sha256').update(JSON.stringify(definition)).digest('hex').slice(0, 20), capturedAt: at.startedAt };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** An explicit instance override replaces the module's quote for that cost basis. */
|
|
25
|
+
export function quotePrices(entry: LLMProviderEntry, request: Request, at: QuoteTime, defaults: readonly PriceDefinition[]): PriceSnapshot[] {
|
|
26
|
+
const byBasis = new Map<string, PriceDefinition>();
|
|
27
|
+
for (const definition of [...defaults, ...(entry.pricing ?? [])]) {
|
|
28
|
+
if (definition.models.includes('*') || definition.models.includes(request.model ?? '')) byBasis.set(definition.basis, definition);
|
|
29
|
+
}
|
|
30
|
+
return [...byBasis.values()].map(definition => snapshotPrice(definition, at));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** External configuration boundary for module-owned rate definitions. `language` picks the message wording. */
|
|
34
|
+
export function validatePrices(value: unknown, language: Language = 'zh'): PriceDefinition[] {
|
|
35
|
+
const S = text(language);
|
|
36
|
+
if (!Array.isArray(value)) throw new Error(S.pricingArray);
|
|
37
|
+
const meters = new Set(['input', 'output', 'total', 'cachedInput', 'uncachedInput', 'reasoning']);
|
|
38
|
+
const rules = (raw: unknown): PriceRule[] => {
|
|
39
|
+
if (!Array.isArray(raw)) throw new Error(S.rulesArray);
|
|
40
|
+
return raw.map(rule => {
|
|
41
|
+
if (!rule || typeof rule !== 'object' || typeof rule.meter !== 'string' || (!meters.has(rule.meter) && !rule.meter.startsWith('detail:'))
|
|
42
|
+
|| typeof rule.perMillion !== 'number' || !Number.isFinite(rule.perMillion) || rule.perMillion < 0) throw new Error(S.ruleShape);
|
|
43
|
+
if (rule.unit !== undefined && (typeof rule.unit !== 'string' || !rule.unit.trim())) throw new Error(S.unitRequired);
|
|
44
|
+
if (meters.has(rule.meter) && rule.unit !== undefined && rule.unit !== 'token') throw new Error(S.tokenUnit);
|
|
45
|
+
return { meter: rule.meter, perMillion: rule.perMillion, ...(rule.unit ? { unit: rule.unit } : {}) };
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const bands = (raw: unknown) => {
|
|
49
|
+
if (raw === undefined) return undefined;
|
|
50
|
+
if (!Array.isArray(raw)) throw new Error(S.bandsArray);
|
|
51
|
+
let previous = 0;
|
|
52
|
+
return raw.map(band => {
|
|
53
|
+
if (!band || typeof band.from !== 'number' || !Number.isFinite(band.from) || band.from <= previous) throw new Error(S.bandsIncreasing);
|
|
54
|
+
previous = band.from;
|
|
55
|
+
return { from: band.from, rules: rules(band.rules) };
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
return value.map(raw => {
|
|
59
|
+
if (!raw || typeof raw !== 'object' || !Array.isArray(raw.models) || !raw.models.length || raw.models.some((model: unknown) => typeof model !== 'string' || !model.trim())) throw new Error(S.modelsRequired);
|
|
60
|
+
if (typeof raw.currency !== 'string' || !raw.currency.trim()) throw new Error(S.currencyRequired);
|
|
61
|
+
if (raw.basis !== 'marginal' && raw.basis !== 'equivalent') throw new Error(S.basisValue);
|
|
62
|
+
if (typeof raw.source !== 'string' || !raw.source.trim()) throw new Error(S.sourceRequired);
|
|
63
|
+
const tiers: PriceDefinition['serviceTiers'] = {};
|
|
64
|
+
if (raw.serviceTiers !== undefined) {
|
|
65
|
+
if (!raw.serviceTiers || typeof raw.serviceTiers !== 'object' || Array.isArray(raw.serviceTiers)) throw new Error(S.tiersObject);
|
|
66
|
+
for (const [tier, data] of Object.entries(raw.serviceTiers) as [string, any][]) {
|
|
67
|
+
if (!tier.trim()) throw new Error(S.tierNameRequired);
|
|
68
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) throw new Error(S.tierRules);
|
|
69
|
+
tiers[tier] = { rules: rules(data.rules), ...(data.inputBands ? { inputBands: bands(data.inputBands) } : {}) };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { models: [...raw.models], currency: raw.currency.trim(), basis: raw.basis, source: raw.source.trim(), rules: rules(raw.rules),
|
|
73
|
+
...(raw.inputBands ? { inputBands: bands(raw.inputBands) } : {}), ...(raw.serviceTiers ? { serviceTiers: tiers } : {}) };
|
|
74
|
+
});
|
|
75
|
+
}
|