dsh-lcx-codex 0.4.3-pre.4 → 0.4.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 +68 -126
- package/THIRD_PARTY_NOTICES.md +64 -0
- package/lib/auxiliary-usage.js +63 -0
- package/lib/client.js +1363 -295
- package/lib/dsh-compat.js +80 -18
- package/lib/dsh-responses.js +75 -8
- package/lib/grok-native-search.js +151 -79
- package/lib/index.js +159 -101
- package/lib/invocation-policy-scope.js +261 -0
- package/lib/json-store.js +11 -4
- package/lib/pi-responses-runtime.js +1571 -0
- package/lib/responses-request.js +1 -1
- package/lib/responses-stream.js +152 -84
- package/lib/route.js +17 -3
- package/lib/search-accounting.js +86 -0
- package/lib/search-usage.js +86 -0
- package/lib/transport.js +38 -10
- package/lib/types/client/index.d.ts +12 -0
- package/lib/types/client/search-media.d.ts +16 -0
- package/lib/web-run-output.js +22 -0
- package/lib/web-search-alpha.js +230 -28
- package/lib/web-search-capability.js +26 -1
- package/lib/web-search-hosted.js +119 -9
- package/lib/web-search-ref-store.js +88 -7
- package/package.json +45 -24
- package/lib/types/compact-v2.d.ts +0 -104
- package/lib/types/dsh-compat.d.ts +0 -78
- package/lib/types/dsh-responses.d.ts +0 -82
- package/lib/types/grok-native-search.d.ts +0 -39
- package/lib/types/json-store.d.ts +0 -10
- package/lib/types/native-checkpoint.d.ts +0 -213
- package/lib/types/responses-request.d.ts +0 -59
- package/lib/types/responses-stream.d.ts +0 -64
- package/lib/types/route.d.ts +0 -153
- package/lib/types/service-mutex.d.ts +0 -14
- package/lib/types/token-budget.d.ts +0 -50
- package/lib/types/transport.d.ts +0 -21
- package/lib/types/web-run-output.d.ts +0 -29
- package/lib/types/web-search-alpha.d.ts +0 -286
- package/lib/types/web-search-capability.d.ts +0 -26
- package/lib/types/web-search-hosted.d.ts +0 -246
- package/lib/types/web-search-ref-store.d.ts +0 -22
package/README.md
CHANGED
|
@@ -4,183 +4,125 @@
|
|
|
4
4
|
|
|
5
5
|
# LCX Codex
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**让 DeepSeek Harness 里的 GPT 与 Grok 更适合长任务、联网搜索和连续工作。**
|
|
8
8
|
|
|
9
|
-
[](https://www.npmjs.com/package/dsh-lcx-codex)
|
|
10
|
+
[](#安装)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
|
|
13
13
|
**简体中文** · [English](README_EN.md) · [更新日志](CHANGELOG.md) · [问题反馈](https://github.com/kk3ya03-star/dsh-lcx-codex/issues)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
|
-
LCX Codex 是 [DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness)(DSH
|
|
17
|
+
LCX Codex 是 [DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness)(DSH)的社区插件。
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
它不会替代 DSH,也不需要你重新配置模型、接口或 API Key。模型、凭据、会话和工具仍由 DSH 管理;LCX 只在需要时为 GPT 和 Grok 增加额外能力。
|
|
20
20
|
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
本页对应 **`0.4.3-pre.4` 预发布版**,适配 **DSH `0.1.3-alpha.2`**。稳定版仍为 `0.4.2`,使用旧版 DSH `0.1.1-rc.2` 的用户请看[稳定版说明](https://github.com/kk3ya03-star/dsh-lcx-codex/blob/v0.4.2/README.md)。
|
|
24
|
-
|
|
25
|
-
安装前,请确认 DSH Web 能正常启动,并已配置需要使用的 GPT Responses 或 Grok Responses API 路由。Node.js 要求 `^22.19.0 || >=24.0.0`;Windows 启动出现 `fs-ext` 报错时,先看下方[故障排查](#故障排查)。
|
|
26
|
-
|
|
27
|
-
```sh
|
|
28
|
-
dsh plugin --profile web add dsh-lcx-codex@0.4.3-pre.4
|
|
29
|
-
dsh web
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
以后升级预发布版,可执行 `dsh plugin --profile web add dsh-lcx-codex@prelatest`。不带版本或标签安装会得到稳定版,**不是本页介绍的新版本**。
|
|
33
|
-
|
|
34
|
-
**从 `0.4.2` 升级:** 旧插件配置和 v3/v4 压缩检查点不再兼容,请重新配置插件并新建会话。新版 v5 GPT 检查点仍支持重启续聊。
|
|
21
|
+
## 你可以用它做什么
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
打开 DSH Web 的插件设置,展开 **Responses / Codex 能力**,按需打开开关并保存:
|
|
39
|
-
|
|
40
|
-
| 开关 | 什么时候开 |
|
|
23
|
+
| 能力 | 适合场景 |
|
|
41
24
|
| --- | --- |
|
|
42
|
-
|
|
|
43
|
-
| GPT
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
49
|
-
**六个开关默认均关闭。** GPT 四项由“启用 LCX”控制;Grok 两项独立,不要求打开 GPT 的 LCX 主开关。模型、推理等级、图片输入、endpoint 与 credential 都继续在 DSH 的模型/provider 配置中管理。
|
|
50
|
-
|
|
51
|
-
模型切换不需要重启插件:
|
|
25
|
+
| GPT 联网搜索 | 让 GPT 直接通过 DSH 的 `web_search` 查询网页。 |
|
|
26
|
+
| GPT 高级搜索 | 搜图片、限定网站、加入位置等额外搜索条件。 |
|
|
27
|
+
| Grok 原生 Web / X Search | 直接使用 xAI Responses 的 Web Search 和 X Search。 |
|
|
28
|
+
| 长对话压缩 | GPT 长任务接近上下文上限时自动尝试远程压缩,并继续当前会话。 |
|
|
29
|
+
| 搜索媒体预览 | 在回答下方直接预览可用的图片和视频链接。 |
|
|
30
|
+
| Alpha 网页操作 | 实验性连续网页阅读能力,适合需要进一步打开、查找页面内容的任务。 |
|
|
52
31
|
|
|
53
|
-
|
|
54
|
-
- Grok → 只启用已打开的原生 Web / X Search;开启任意 Grok 原生搜索时,该请求不会同时暴露 DSH `web_search`,但 `web_fetch` 和其他 DSH/MCP 工具仍可用;
|
|
55
|
-
- Claude、Gemini、DeepSeek 等其他模型 → 保持 DSH 原生会话、搜索、工具和压缩行为。
|
|
32
|
+
其他模型继续使用 DSH 原生路径,不会因为安装 LCX 而被接管。
|
|
56
33
|
|
|
57
|
-
##
|
|
58
|
-
|
|
59
|
-
### GPT 联网查询
|
|
60
|
-
|
|
61
|
-
GPT 普通查询沿用 DSH 的单一 `web_search` 工具入口;开启 **GPT Hosted Search** 后,LCX 按当前 GPT Responses route 执行 Hosted Search。需要域名、位置、search context 或图片等额外参数时,可启用 `websearch_gpt_advanced`。
|
|
34
|
+
## 安装
|
|
62
35
|
|
|
63
|
-
|
|
36
|
+
当前稳定版:**`dsh-lcx-codex@0.4.3`**
|
|
64
37
|
|
|
65
|
-
|
|
38
|
+
兼容环境:
|
|
66
39
|
|
|
67
|
-
|
|
40
|
+
- DSH `0.1.5-rc.1` 起的 `0.1.5` 系列(当前完整验证基线:`0.1.5-rc.1`)
|
|
41
|
+
- Node.js `^22.19.0 || >=24.0.0`
|
|
68
42
|
|
|
69
|
-
|
|
43
|
+
安装:
|
|
70
44
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
45
|
+
```sh
|
|
46
|
+
dsh plugin --profile web add dsh-lcx-codex@0.4.3
|
|
47
|
+
dsh web
|
|
74
48
|
```
|
|
75
49
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
当前支持 **API Key / API Gateway** 路由;OAuth / SuperGrok / X Premium 订阅登录不在本版范围内。搜索来源 URL 会尽量保留,但具体 citation 展示形式仍受上游兼容网关返回格式影响。
|
|
79
|
-
|
|
80
|
-
### GPT 搜索图片并显示
|
|
81
|
-
|
|
82
|
-
开启高级 Hosted 工具后,可以这样说:
|
|
83
|
-
|
|
84
|
-
> 搜索金门大桥的照片,选一张直接显示在回复里,并附上来源网页。
|
|
85
|
-
|
|
86
|
-
**不需要自己填写 JSON 参数。** 模型负责选择图片搜索参数;如果只返回文字,可以补充“请使用高级 Hosted 的图片搜索,并把图片显示出来”。
|
|
87
|
-
|
|
88
|
-
搜索工具提供图片链接与来源,DSH 用现有的 Markdown 渲染器显示图片。这是**搜索已有图片,不是生成图片**,也不代表模型已经读取了图片像素。图片能否显示还取决于原站链接是否可访问。
|
|
89
|
-
|
|
90
|
-
### Alpha 网页操作
|
|
91
|
-
|
|
92
|
-
`websearch_alpha` 支持 `search / open / find / click / screenshot` 等动作,用于 GPT 连续查阅网页。它操作的是**上游搜索服务中的网页内容**,不会操作你的本机浏览器或 DSH 界面。
|
|
50
|
+
后续稳定版更新可以使用:
|
|
93
51
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
开启 LCX 后,插件将 DSH 的压缩请求交给上游 GPT 接口执行 **Native V2 远程原生压缩**,再将结果接回当前会话。会话保存、工具执行和压缩事务仍由 DSH 负责。
|
|
99
|
-
|
|
100
|
-
| 上下文占用 | 处理方式 |
|
|
101
|
-
| --- | --- |
|
|
102
|
-
| 低于 `90%` | 正常对话,不提前裁剪工具结果。 |
|
|
103
|
-
| `90%` 至 `95%` | 优先尝试远程原生压缩。 |
|
|
104
|
-
| 达到 `95%` | 允许 DSH 紧急裁剪工具结果,再按流程压缩。 |
|
|
52
|
+
```sh
|
|
53
|
+
dsh plugin --profile web add dsh-lcx-codex@latest
|
|
54
|
+
```
|
|
105
55
|
|
|
106
|
-
|
|
56
|
+
不指定版本或使用 `@latest` 都会安装当前稳定版。需要预发布版本时可使用 `@prelatest`;`0.4.3-pre.13` 作为本次正式版之前的已验证预发布历史继续保留。
|
|
107
57
|
|
|
108
|
-
|
|
58
|
+
本版已在官方 DSH `0.1.5-rc.1` 完整验证。后续同一 `0.1.5` 系列版本可以直接安装并进行兼容评估,不再需要仅因为 RC 版本号变化而发布新的 LCX 版本。
|
|
109
59
|
|
|
110
|
-
##
|
|
60
|
+
## 启用功能
|
|
111
61
|
|
|
112
|
-
|
|
62
|
+
打开 DSH Web 的插件设置,找到 **Responses / Codex 能力**。
|
|
113
63
|
|
|
114
|
-
|
|
115
|
-
- **Grok**:严格跟随 DSH/Pi 原生语义;每个前台/子代理使用自己的 DSH Session ID 作为 prompt cache / session affinity identity,sibling 子代理不会共享 opaque replay state。
|
|
64
|
+
### 使用 GPT
|
|
116
65
|
|
|
117
|
-
|
|
66
|
+
通常打开:
|
|
118
67
|
|
|
119
|
-
|
|
68
|
+
1. **启用 LCX**
|
|
69
|
+
2. **GPT Hosted Search**(需要联网时)
|
|
120
70
|
|
|
121
|
-
|
|
71
|
+
按需再打开:
|
|
122
72
|
|
|
123
|
-
|
|
73
|
+
- **高级 Hosted 工具**:需要图片搜索、限定域名、位置等高级搜索条件时使用;它依赖 GPT Hosted Search。
|
|
74
|
+
- **Alpha**:实验性网页连续操作。日常联网搜索不需要开启。
|
|
124
75
|
|
|
125
|
-
|
|
76
|
+
### 使用 Grok
|
|
126
77
|
|
|
78
|
+
Grok 原生搜索与 GPT 的 LCX 主开关相互独立:
|
|
127
79
|
|
|
128
|
-
|
|
80
|
+
- **Grok 原生 Web Search**:搜索网页。
|
|
81
|
+
- **Grok 原生 X Search**:搜索 X 内容。
|
|
129
82
|
|
|
130
|
-
|
|
131
|
-
2. 检查该包相邻的 `package.json`,确认版本为 `0.1.3-alpha.2`,并把 `lib/index.js` 复制为 `lib/index.js.before-win32-fs-ext.bak`;备份已存在时保留它。
|
|
132
|
-
3. 用编辑器将下面唯一一行:
|
|
83
|
+
开启 Grok 原生搜索后,LCX 会使用 xAI 的服务端搜索能力,同时保留页面读取和其他 DSH / MCP 工具。
|
|
133
84
|
|
|
134
|
-
|
|
135
|
-
import { flock } from "fs-ext";
|
|
136
|
-
```
|
|
85
|
+
### 搜索媒体预览
|
|
137
86
|
|
|
138
|
-
|
|
87
|
+
**搜索媒体预览**可以独立开启。它只改变回答的显示方式,不会修改模型提示词、搜索请求、聊天历史或缓存。
|
|
139
88
|
|
|
140
|
-
|
|
141
|
-
const flock = process.platform === "win32"
|
|
142
|
-
? undefined
|
|
143
|
-
: (await import("fs-ext")).flock;
|
|
144
|
-
```
|
|
89
|
+
可识别的图片会显示缩略图并支持放大;直接视频链接可以点击播放。媒体加载失败时,原来的文字和链接仍会保留。
|
|
145
90
|
|
|
146
|
-
|
|
147
|
-
4. 保存后重新运行 `dsh web`,新建会话并发送一条消息,确认启动和会话写入正常。此修正不保证解决其他启动错误。
|
|
148
|
-
5. 恢复时先停止 DSH,再用备份覆盖 `index.js`。重新安装或升级 DSH 可能覆盖此本地修改;不要把这个旧版本修正直接套到新版本。
|
|
91
|
+
## 常见用法
|
|
149
92
|
|
|
150
|
-
|
|
93
|
+
直接像平时一样和模型说话,不需要手写工具参数。例如:
|
|
151
94
|
|
|
152
|
-
|
|
95
|
+
> 搜索 Python 官方文档,告诉我 `asyncio.TaskGroup` 应该怎么用。
|
|
153
96
|
|
|
154
|
-
|
|
97
|
+
> 搜索几张金门大桥的照片,显示一张,并给我来源网页。
|
|
155
98
|
|
|
156
|
-
|
|
99
|
+
> 用 Grok 搜一下 X 上最近关于这个项目的讨论。
|
|
157
100
|
|
|
158
|
-
|
|
101
|
+
长任务也不需要自己盯着上下文。启用 LCX 后,兼容的 GPT 路由会在对话变得很长时自动尝试远程压缩并继续工作;你也可以使用 `/compact` 手动压缩。
|
|
159
102
|
|
|
160
|
-
##
|
|
103
|
+
## 兼容性与已知限制
|
|
161
104
|
|
|
162
|
-
|
|
105
|
+
- `0.4.3` 的当前完整验证基线是 DSH `0.1.5-rc.1`。同一 `0.1.5` 系列后续版本处于可安装、可评估范围,但仍会单独记录兼容验证结果;`0.1.6` 及更高版本不会自动视为兼容。
|
|
106
|
+
- 从较旧的 LCX / DSH 版本升级时,建议新建会话;旧版保存的压缩状态不保证兼容。
|
|
107
|
+
- Grok 原生搜索支持 API Key / API 网关路由;xAI OAuth / SuperGrok 登录不在当前范围。
|
|
108
|
+
- Alpha 仍是实验功能。搜索可以正常使用,但连续 `open / find` 等操作在部分路由上仍可能失败;截图也不保证能作为可显示图片返回。
|
|
109
|
+
- 修改 DSH profile 中的插件启用状态后,已经打开的浏览器页面可能需要刷新。
|
|
110
|
+
- 不同 API 网关支持的能力可能不同。普通聊天可用,不代表搜索、长对话压缩或 Alpha 等功能也一定可用。
|
|
163
111
|
|
|
164
|
-
|
|
112
|
+
## 遇到问题
|
|
165
113
|
|
|
166
|
-
|
|
114
|
+
**安装后看不到变化**:确认安装和启动使用的是同一个 `web` profile,并检查对应功能开关是否已经保存。
|
|
167
115
|
|
|
168
|
-
|
|
116
|
+
**对话正常,但搜索或压缩失败**:先确认当前 GPT / Grok Responses 路由确实提供对应服务端能力。网关名称本身不代表一定支持所有功能。
|
|
169
117
|
|
|
170
|
-
|
|
118
|
+
提交 [Issue](https://github.com/kk3ya03-star/dsh-lcx-codex/issues) 时,请附上插件版本、DSH 版本、Node.js 版本和复现步骤。不要上传 API Key、完整请求或未脱敏的会话日志。
|
|
171
119
|
|
|
172
|
-
|
|
173
|
-
npm ci --ignore-scripts
|
|
174
|
-
npm ci --prefix scripts/runtime-alpha2 --ignore-scripts
|
|
175
|
-
node scripts/link-dsh-runtime.mjs scripts/runtime-alpha2
|
|
176
|
-
node scripts/check-generated.mjs
|
|
177
|
-
npm run typecheck
|
|
178
|
-
npm test
|
|
179
|
-
npm run test:schema
|
|
180
|
-
```
|
|
120
|
+
## 更多信息
|
|
181
121
|
|
|
182
|
-
|
|
122
|
+
- [更新日志](CHANGELOG.md)
|
|
123
|
+
- [架构说明](ARCHITECTURE.md)
|
|
124
|
+
- [第三方许可](THIRD_PARTY_NOTICES.md)
|
|
183
125
|
|
|
184
126
|
## 许可证
|
|
185
127
|
|
|
186
|
-
[MIT](LICENSE)。独立社区插件,与 OpenAI、DeepSeek、Sub2API、NewAPI 无隶属或官方背书关系。
|
|
128
|
+
[MIT](LICENSE)。独立社区插件,与 OpenAI、DeepSeek、Sub2API、NewAPI 无隶属或官方背书关系。
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
`lib/pi-responses-runtime.js` is generated from public exports of the packages below. The build recipe in `scripts/build-pi-runtime.mjs` verifies that no other third-party package contributes output bytes.
|
|
4
|
+
|
|
5
|
+
## @earendil-works/pi-ai 0.85.1
|
|
6
|
+
|
|
7
|
+
Upstream source: https://github.com/earendil-works/pi/tree/v0.85.1/packages/ai
|
|
8
|
+
|
|
9
|
+
Included public Responses helpers and model catalogs:
|
|
10
|
+
|
|
11
|
+
- `api/openai-responses-shared`
|
|
12
|
+
- `api/constrained-sampling`
|
|
13
|
+
- `api/openai-prompt-cache`
|
|
14
|
+
- `utils/event-stream`
|
|
15
|
+
- model helpers from the package root
|
|
16
|
+
- Responses-bearing catalogs for `cloudflare-ai-gateway`, `github-copilot`, `openai`, `opencode`, `opencode-go`, and `xai`
|
|
17
|
+
|
|
18
|
+
MIT License
|
|
19
|
+
|
|
20
|
+
Copyright (c) 2025 Mario Zechner
|
|
21
|
+
|
|
22
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
23
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
24
|
+
in the Software without restriction, including without limitation the rights
|
|
25
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
26
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
27
|
+
furnished to do so, subject to the following conditions:
|
|
28
|
+
|
|
29
|
+
The above copyright notice and this permission notice shall be included in all
|
|
30
|
+
copies or substantial portions of the Software.
|
|
31
|
+
|
|
32
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
33
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
34
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
35
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
36
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
37
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
38
|
+
SOFTWARE.
|
|
39
|
+
|
|
40
|
+
## partial-json 0.1.7
|
|
41
|
+
|
|
42
|
+
Upstream source: https://github.com/promplate/partial-json-parser-js
|
|
43
|
+
|
|
44
|
+
MIT License
|
|
45
|
+
|
|
46
|
+
Copyright (c) 2023 Promplate Dev Team
|
|
47
|
+
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
49
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
50
|
+
in the Software without restriction, including without limitation the rights
|
|
51
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
52
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
53
|
+
furnished to do so, subject to the following conditions:
|
|
54
|
+
|
|
55
|
+
The above copyright notice and this permission notice shall be included in all
|
|
56
|
+
copies or substantial portions of the Software.
|
|
57
|
+
|
|
58
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
59
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
60
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
61
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
62
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
63
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
64
|
+
SOFTWARE.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
const calls = new AsyncLocalStorage();
|
|
3
|
+
const object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
const count = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
5
|
+
/** Collect only authoritative Responses billing, independently of context size. */
|
|
6
|
+
export function recordHostedUsage(response, requestId, provider, model) {
|
|
7
|
+
const store = calls.getStore();
|
|
8
|
+
if (!store || !object(response) || !object(response.usage))
|
|
9
|
+
return;
|
|
10
|
+
const u = response.usage;
|
|
11
|
+
const input = u.input_tokens, output = u.output_tokens, total = u.total_tokens;
|
|
12
|
+
const cached = object(u.input_tokens_details) ? u.input_tokens_details.cached_tokens : undefined;
|
|
13
|
+
if (!count(input) || !count(output) || !count(total) || !count(cached)
|
|
14
|
+
|| cached > input || input + output !== total)
|
|
15
|
+
return;
|
|
16
|
+
store.push({ requestId, provider, model, usage: {
|
|
17
|
+
inputTokens: input - cached, outputTokens: output, totalTokens: total,
|
|
18
|
+
cacheReadTokens: cached, cacheWriteTokens: 0,
|
|
19
|
+
} });
|
|
20
|
+
}
|
|
21
|
+
/** Extend only LCX-owned search output; rendering and request schemas stay intact. */
|
|
22
|
+
export function withAuxiliaryUsage(tool) {
|
|
23
|
+
const schema = tool.output.schema;
|
|
24
|
+
if (schema.type !== "object")
|
|
25
|
+
throw new Error("Search accounting requires an object output");
|
|
26
|
+
return {
|
|
27
|
+
...tool,
|
|
28
|
+
output: {
|
|
29
|
+
...tool.output,
|
|
30
|
+
schema: { ...schema, properties: { ...schema.properties, auxiliaryUsage: {
|
|
31
|
+
type: "array", items: { type: "object", additionalProperties: true },
|
|
32
|
+
} } },
|
|
33
|
+
render(args, value) {
|
|
34
|
+
if (!object(value))
|
|
35
|
+
return tool.output.render(args, value);
|
|
36
|
+
const { auxiliaryUsage: _billing, ...original } = value;
|
|
37
|
+
return tool.output.render(args, original);
|
|
38
|
+
},
|
|
39
|
+
presentationMeta(args, value) {
|
|
40
|
+
if (!object(value))
|
|
41
|
+
return tool.output.presentationMeta?.(args, value) ?? {};
|
|
42
|
+
const { auxiliaryUsage, ...original } = value;
|
|
43
|
+
const meta = tool.output.presentationMeta?.(args, original) ?? {};
|
|
44
|
+
if (!Array.isArray(auxiliaryUsage) || auxiliaryUsage.length === 0)
|
|
45
|
+
return meta;
|
|
46
|
+
if (!object(meta))
|
|
47
|
+
throw new Error("Search accounting requires object presentation metadata");
|
|
48
|
+
return { ...meta, auxiliaryUsage };
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
async execute(args, exec) {
|
|
52
|
+
const records = [];
|
|
53
|
+
return calls.run(records, async () => {
|
|
54
|
+
const value = await tool.execute(args, exec);
|
|
55
|
+
if (records.length === 0)
|
|
56
|
+
return value;
|
|
57
|
+
if (!object(value))
|
|
58
|
+
throw new Error("Search accounting requires an object result");
|
|
59
|
+
return { ...value, auxiliaryUsage: records };
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|